├── README.md └── src ├── Makefile └── multipipe.c /README.md: -------------------------------------------------------------------------------- 1 |

2 | Multipipe_tutor 3 |

4 | 5 |

6 | Summary 7 | · 8 | Pipe 9 | · 10 | Multipipe 11 | · 12 | Sources 13 | · 14 | Contact 15 |

16 | 17 | # Summary 18 | 19 | This small project aims to guarantee me a quick and clear reunderstanding of my 20 | implementation of the multi-pipe when the notion will be as rusty as a distant 21 | memory the day I need it. 22 | 23 | For this purpose we will **dissect** the `prg1 | prg2 | prg3` **command execution** 24 | using a [**C multi-pipe implementation**](https://github.com/clemedon/Multipipe_tutor/tree/main/src). 25 | 26 | *For reasons of readability the code does not include the protections.* 27 | 28 | [**→ GitHub Page ←**](https://clemedon.github.io/Multipipe_tutor/)
29 | [**→ GitHub Repo ←**](https://github.com/clemedon/Multipipe_tutor/) 30 | 31 | # Pipe 32 | 33 | - A pipe is a **unidirectional** data channel that can be used for **interprocess 34 | communication**. 35 | 36 | - It is made of **two descriptors**: `pipefd[0]` that refers to the **read end** of 37 | the pipe and `pipefd[1]` that refers to the **write end** of the pipe. 38 | 39 | - **Data written to the write end of the pipe is buffered** by the kernel until it 40 | is read from the read end of the pipe. 41 | 42 | ``` 43 | Fig.0 Interprocess communication with a single pipe. 44 | 45 | ₍₂₎ 46 | IN→ 1=====xxxx0 47 | ⁽³⁾↓ 48 | ⁽¹⁾ 1xxxx=====0 →OUT 49 | 50 | 51 | (0) The main process (futur parent) creates a pipe and forks itself which 52 | duplicates its pipe's set of file descriptors pipefd[1] and pipefd[0] into the 53 | newly created (child) process. 54 | 55 | (1) The parent process closes its pipefd[1] to prevent its process from writing 56 | in the pipe. 57 | 58 | (2) Simultaneously, the child process closes its pipefd[0] to prevent its 59 | process from reading in the pipe. 60 | 61 | (3) In the end we have a parent that can read and a child that can write, both 62 | sharing the same pipe. If the child write in the pipe, the data stream will 63 | find its way out in the read end of the parent process ⇒ interprocess 64 | communication. 65 | ``` 66 | 67 | # Multipipe 68 | 69 | - Alternating several pipes and processes we can create an interprocess 70 | communication chain, **passing the output of one program as the input of 71 | another program** and so on. 72 | 73 | ``` 74 | Fig.1 Overall idea of following multi-pipe example. 75 | 76 | Stdin → PRG1 PRG2 PRG3 → Stdout 106 | * [**Illustrations**](#illustrations) 107 | 108 | ### Instructions 109 | 110 | ``` 111 | main() 112 | 113 | - initialize prevpipe to any valid file descriptor. 114 | - start the while loop that iterate over each of the 3 commands (PRG). 115 | 116 | 117 | PRG1 in ft_pipe() 118 | 119 | - create a pipe     P1[2] Size 2 array that contains P1[0] and P1[1] 120 | - fork itself Which clones P1 121 | 122 | Child 123 | 124 | - close              P1[0]      Unused 125 | - redirect Stdout to P1[1]      Fill the pipe with PRG1 output 126 | - close              P1[1]      Not needed anymore 127 | - redirect Stdin  to prevpipe   Here Stdin (cf. prevpipe init) 128 | - close              prevpipe   Not needed anymore 129 | - exec 130 | 131 | Parent 132 | 133 | - close              P1[1]      Unused 134 | - prevpipe         = P1[0]      Save prevpipe for PRG2 Stdin 135 | 136 | 137 | PRG2 in ft_pipe() 138 | 139 | - create a pipe     P2[2] Size 2 array that contains P2[0] and P2[1] 140 | - fork itself Which clones P2 141 | 142 | Child 143 | 144 | - close              P2[0]      Unused 145 | - redirect Stdout to P2[1]      Fill the pipe with PRG2 output 146 | - close              P2[1]      Not needed anymore 147 | - redirect Stdin  to prevpipe   Here P1[0] (the previous P[0]) 148 | - close              prevpipe   Not needed anymore 149 | - exec 150 | 151 | Parent 152 | 153 | - close              P2[1]      Unused 154 | - prevpipe         = P2[0]      Save prevpipe for PRG3 Stdin 155 | 156 | 157 | PRG3 in ft_last() 158 | 159 | - fork itself 160 | 161 | Child 162 | 163 | - redirect Stdin  to prevpipe   Here P2[0] (the previous P[0]) 164 | - close              prevpipe   Not needed anymore 165 | - exec 166 | 167 | Parent 168 | 169 | - close              prevpipe   Unused 170 | - wait for children 171 | ``` 172 | [**Return to Index ↑**](#example) 173 | 174 | ### Illustrations 175 | 176 | The path taken by the **stream of data** during the whole execution is paved by 177 | the **`(A)`** to **`(J)`** symbols. 178 | 179 | ``` 180 | PRG1 in ft_pipe() 181 | 182 | P1 in the child process. 183 | 184 | P1[1] P1[0] 185 | ――――――――――――――――――――― 186 | (A) Stdin → PRG1 → OPEN → (B) CLOSED 187 | ――――――――――――――――――――― 188 | 189 | P1 in the parent process. 190 | 191 | P1[1] P1[0] 192 | ――――――――――――――――――――― 193 | CLOSED (C) → OPEN → prevpipe (D) 194 | ――――――――――――――――――――― 195 | 196 | PRG2 in ft_pipe() 197 | 198 | P2 in the child process. 199 | 200 | P2[1] P2[0] 201 | ――――――――――――――――――――― 202 | (E) prevpipe → PRG2 → OPEN → (F) CLOSED 203 | ――――――――――――――――――――― 204 | 205 | P2 in the parent process. 206 | 207 | P2[1] P2[0] 208 | ――――――――――――――――――――― 209 | CLOSED (G) → OPEN → prevpipe (H) 210 | ――――――――――――――――――――― 211 | PRG3 in ft_last() 212 | 213 | Last program execution. 214 | 215 | (I) prevpipe → PRG3 → Stdout (J) 216 | ``` 217 | 218 | [**Return to Index ↑**](#example) 219 | 220 | # Sources 221 | 222 | - **`$ man 2 pipe`** 223 | - [**GPT Chat**](https://chat.openai.com/chat) 224 | 225 | # Contact 226 | 227 | ``` 228 | cvidon 42 229 | clemedon icloud 230 | ``` 231 | 232 | Copyright 2022 Clément Vidon. All Rights Reserved. 233 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | NAME := multipipe 2 | SRCS := multipipe.c 3 | OBJS := multipipe.o 4 | CC := clang 5 | CFLAGS := -Wall -Wextra -Werror 6 | RM := rm -f 7 | MAKEFLAGS := --no-print-directory 8 | 9 | all: $(NAME) 10 | 11 | $(NAME): $(OBJS) 12 | $(CC) $(OBJS) -o $(NAME) 13 | 14 | %.o: %.c 15 | $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< 16 | 17 | clean: 18 | $(RM) $(OBJS) $(NAME) 19 | 20 | re: 21 | $(MAKE) clean 22 | $(MAKE) all 23 | 24 | .SILENT: 25 | .PHONY: clean re run 26 | -------------------------------------------------------------------------------- /src/multipipe.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* ::: :::::::: */ 4 | /* multipipe.c :+: :+: :+: */ 5 | /* +:+ +:+ +:+ */ 6 | /* By: cvidon +#+ +:+ +#+ */ 7 | /* +#+#+#+#+#+ +#+ */ 8 | /* Created: 2022/12/14 08:00:00 by cvidon #+# #+# */ 9 | /* Updated: 2022/12/14 18:00:00 by cvidon 888 ########.fr */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | /* 18 | ** @brief Last program. 19 | ** 20 | ** @Instructions 21 | ** 22 | ** Parent 23 | ** 24 | ** - Redirect Stdin to prevpipe 25 | ** - Close prevpipe 26 | ** - Execute 27 | ** 28 | ** Child 29 | ** 30 | ** - Close unused prevpipe 31 | ** - Wait for children 32 | */ 33 | 34 | void ft_last(char **cmd, int len, char **env, int prevpipe) 35 | { 36 | pid_t cpid; 37 | 38 | cpid = fork (); 39 | if (cpid == 0) 40 | { 41 | dup2 (prevpipe, STDIN_FILENO); 42 | close (prevpipe); 43 | cmd[len] = NULL; 44 | execve (cmd[0], cmd, env); 45 | } 46 | else 47 | { 48 | close (prevpipe); 49 | while (wait (NULL) != -1) 50 | ; 51 | } 52 | } 53 | 54 | /* 55 | ** @brief Not last program. 56 | ** 57 | ** @instructions 58 | ** 59 | ** - create a pipe 60 | ** - fork itself 61 | ** 62 | ** Child (Writer) 63 | ** 64 | ** - Close unused pipefd[0] 65 | ** - Redirect Stdin to prevpipe 66 | ** - Redirect Stdout to pipefd[1] 67 | ** - Execute 68 | ** 69 | ** Parent (Reader) 70 | ** 71 | ** - Close unused pipefd[1] 72 | ** - Update prevpipe 73 | */ 74 | 75 | void ft_pipe(char **cmd, int len, char **env, int *prevpipe) 76 | { 77 | int pipefd[2]; 78 | pid_t cpid; 79 | 80 | pipe (pipefd); 81 | cpid = fork (); 82 | if (cpid == 0) 83 | { 84 | close (pipefd[0]); 85 | dup2 (pipefd[1], STDOUT_FILENO); 86 | close (pipefd[1]); 87 | dup2 (*prevpipe, STDIN_FILENO); 88 | close (*prevpipe); 89 | cmd[len] = NULL; 90 | execve (cmd[0], cmd, env); 91 | } 92 | else 93 | { 94 | close (pipefd[1]); 95 | close (*prevpipe); 96 | *prevpipe = pipefd[0]; 97 | } 98 | } 99 | 100 | /* 101 | ** @brief Return command length. 102 | */ 103 | 104 | int ft_len(char **cmd) 105 | { 106 | int len; 107 | 108 | len = 0; 109 | while (cmd[len] && *cmd[len] != '|') 110 | len++; 111 | return (len); 112 | } 113 | 114 | /* 115 | ** @brief Iterate over piped commands. 116 | ** 117 | ** @usage 118 | ** 119 | ** Do not forget to surround "|" with double quotes 120 | ** so that it is not interpreted by the shell. 121 | ** 122 | ** Example: 123 | ** 124 | ** ./multipipe /bin/echo five "|" /bin/wc -c "|" /bin/cat -e 125 | ** 126 | ** File descriptors debugging tools: 127 | ** 128 | ** - Open at exit: 129 | ** valgrind --trace-children=yes --track-fds=yes 130 | ** 131 | ** - Print fds status: 132 | ** 133 | ** #include 134 | ** #include 135 | ** 136 | ** struct stat information; 137 | ** 138 | ** dprintf (2, "> %s: %i\n", __func__, fstat (*prevpipe, &information)); 139 | ** dprintf (2, "> %s: %i\n", __func__, getpid()); 140 | */ 141 | 142 | int main(int ac, char **cmd, char **env) 143 | { 144 | int prevpipe; 145 | int len; 146 | 147 | (void)ac; 148 | len = 0; 149 | prevpipe = dup (0); 150 | while (cmd[len] && cmd[len + 1]) 151 | { 152 | cmd += len + 1; 153 | len = ft_len (cmd); 154 | if (cmd[len] != NULL && *cmd[len] == '|') 155 | ft_pipe (cmd, len, env, &prevpipe); 156 | else if (cmd[len] == NULL) 157 | ft_last (cmd, len, env, prevpipe); 158 | } 159 | return (0); 160 | } 161 | --------------------------------------------------------------------------------