├── README.md
└── src
├── Makefile
└── multipipe.c
/README.md:
--------------------------------------------------------------------------------
1 |
2 | Multipipe_tutor
3 |
4 |
5 |
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 |
--------------------------------------------------------------------------------