Lecture 11
Lecture 11
Pipes, Continued
This document is copyright (C) Stanford Computer Science and Nick Troccoli, licensed under
Creative Commons Attribution 2.5 License. All rights reserved.
Based on slides and notes created by John Ousterhout, Jerry Cain, Chris Gregg, and others.
NOTICE RE UPLOADING TO WEBSITES: This content is protected and may not be shared, 1
uploaded, or distributed. (without expressed written permission)
CS111 Topic 2: Multiprocessing
Key Question: How can our program create and interact with other programs? How
does the operating system manage user programs?
Managing
Inter-process
Multiprocessing processes and
communication
Introduction running other
with pipes
programs
Lecture 8 Lecture 9 Lecture 10 / Today
3
Plan For Today
• Recap: Pipes and dup2 so far
• Practice: implementing subprocess
• I/O Redirection with files
• Closing pipes
• Why are pipes shared when we call fork?
cp -r /afs/ir/class/cs111/lecture-code/lect11 . 4
Plan For Today
• Recap: Pipes and dup2 so far
• Practice: implementing subprocess
• I/O Redirection with files
• Closing pipes
• Why are pipes shared when we call fork?
cp -r /afs/ir/class/cs111/lecture-code/lect11 . 5
How do we implement shell pipelines?
To implement two-process pipelines, we must do the following:
1. Create a “magic portal” that allows data to be sent between two processes
2. Spawn 2 child processes (1 per command)
3. Connect one end of that portal to the first child’s STDOUT, and the other end
to the second child’s STDIN
First child Second child
STDIN STDOUT STDERR STDIN STDOUT STDERR
7
How do we implement shell pipelines?
Three key questions:
1. What the heck is a “magic portal” and how do we create one?
The pipe() system call
2. How do we share this “magic portal” between processes?
Relying on cloning that happens on fork(), plus a new property of execvp
3. How do we connect a process’s STDIN/STDOUT to this ”magic portal”?
The dup2() system call
8
“Magic Portal”: pipe() System Call
int pipe(int fds[]);
The pipe system call gives us back two file descriptors, where everything written
to one can be read from the other.
• Specifically: populates the 2-element array fds with the two file descriptors.
Everything written to fds[1] can be read from fds[0]. Tip: you learn to read
before you learn to write (read = fds[0], write = fds[1]).
• Returns 0 on success, or -1 on error.
Imagine: like opening the same file twice, once for reading and once for writing
9
How do we implement shell pipelines?
Three key questions:
1. What the heck is a “magic portal” and how do we create one?
The pipe() system call
2. How do we share this “magic portal” between processes?
Relying on cloning that happens on fork(), plus a new property of execvp
3. How do we connect a process’s STDIN/STDOUT to this ”magic portal”?
The dup2() system call
10
pipe() and fork()
Key idea: a pipe can facilitate parent-child communication because file
descriptors are duplicated on fork(). Thus, a pipe created prior to fork() will
also be accessible in the child!
But wait – isn’t the child a copy of the parent? So wouldn’t it get a copy of the
pipe, not share the same one?
No, as it turns out – the child also gets access to the same file descriptor
sessions open at the time fork is called (sort of - we’ll see how this is possible
today!)
11
Demo: Parent Child Pipe
static const char * kPipeMessage = "Hello, this message is coming through a pipe.";
int main(int argc, char *argv[]) {
int fds[2];
pipe(fds);
size_t bytesSent = strlen(kPipeMessage) + 1;
pid_t pidOrZero = fork();
if (pidOrZero == 0) { // Child only reads from pipe (assume everything is read)
close(fds[1]);
char buffer[bytesSent];
read(fds[0], buffer, sizeof(buffer)); Both the parent and the child
close(fds[0]); must close the pipe FDs when
printf("Message from parent: %s\n", buffer);
return 0; they are done with them.
}
// In the parent, we only write to the pipe (assume everything is written)
close(fds[0]);
write(fds[1], kPipeMessage, bytesSent);
close(fds[1]);
waitpid(pidOrZero, NULL, 0);
return 0; 12
How do we implement shell pipelines?
Three key questions:
1. What the heck is a “magic portal” and how do we create one?
The pipe() system call
2. How do we share this “magic portal” between processes?
Relying on cloning that happens on fork(), plus a new property of execvp
3. How do we connect a process’s STDIN/STDOUT to this ”magic portal”?
The dup2() system call
13
Redirecting Process I/O
dup2 duplicates an open resource session from one file descriptor number
(srcfd) to another (dstfd). Both will now refer to the same open resource session
(e.g. if you read from one, it will advance the position of the other). If dstfd is
already open, it is closed before being used.
New insight: execvp consumes the process but leaves the file descriptor table
intact!
15
How do we implement shell pipelines?
To implement two-process pipelines, we must do the following:
1. Create a pipe prior to spawning the child processes
2. Spawn 2 child processes (1 per command)
3. Use dup2 to connect the first child’s STDOUT to the write end of the pipe.
Use dup2 again to connect the second child’s STDIN to the read end of the
pipe. First child Second child
STDIN STDOUT STDERR STDIN STDOUT STDERR
Read Write
Terminal Pipe 16
Plan For Today
• Recap: Pipes and dup2 so far
• Practice: implementing subprocess
• I/O Redirection with files
• Closing pipes
• Why are pipes shared when we call fork?
cp -r /afs/ir/class/cs111/lecture-code/lect11 . 17
Practice: Subprocess
Let’s implement the subprocess function, which spawns a child and connects a
pipe such that the parent can write to the child’s STDIN.
This is useful because we can spawn and run any other program, even if we
don’t have the source code for it, and feed it input.
Parent Child
STDIN STDOUT STDERR 3 STDIN STDOUT STDERR
Read Write
Terminal Pipe 18
subprocess
Let’s implement the subprocess function, which spawns a child and connects a
pipe such that the parent can write to the child’s STDIN.
subprocess_t subprocess(char *command);
subprocess spawns a child to run the specified command and returns its PID as
well as a file descriptor we can write to to write to its STDIN.
// We want to feed these lines as input to grep to print only sunny days
const char * recent_weather[] = { "Sunny 72", "Rainy 55", "Cloudy 62",
"Sunny 80", "Sunny 75", "Cloudy 61", "Sunny 68", "Rainy 60", "Sunny 85"
};
size_t nelems = sizeof(recent_weather) / sizeof(recent_weather[0]);
// write each entry on its own line to the STDIN of the child process
for (size_t i = 0; i < nelems; i++) {
dprintf(sp.supplyfd, "%s\n", recent_weather[i]);
}
// Close the write FD to indicate the input is closed, and wait for child
close(sp.supplyfd);
waitpid(sp.pid, NULL, 0);
return 0;
} 20
subprocess
Implementing subprocess:
1. Create a pipe
2. Spawn a child process
3. That child process changes its STDIN to be the pipe read end
4. That child process calls execvp to run the specified command
5. We return the pipe write end to the caller along with the child’s PID. That
caller can write to the file descriptor, which appears to the child as its STDIN
21
subprocess
subprocess_t subprocess(const char *command) {
// this line parses the command into a pipeline like is done for you on assign3
pipeline p(command);
// Make a pipe
int fds[2];
pipe(fds);
23
subprocess
int main(int argc, char *argv[]) { typedef struct subprocess_t {
pid_t pid;
// Spawn a child that is running the grep command int supplyfd;
subprocess_t sp = subprocess("/usr/bin/grep Sunny"); } subprocess_t;
// We want to feed these lines as input to grep to print only sunny days
const char * recent_weather[] = { "Sunny 72", "Rainy 55", "Cloudy 62",
"Sunny 80", "Sunny 75", "Cloudy 61", "Sunny 68", "Rainy 60", "Sunny 85"
};
size_t nelems = sizeof(recent_weather) / sizeof(recent_weather[0]);
// write each entry on its own line to the STDIN of the child process
for (size_t i = 0; i < nelems; i++) {
dprintf(sp.supplyfd, "%s\n", recent_weather[i]);
}
// Close the write FD to indicate the input is closed, and wait for child
close(sp.supplyfd);
waitpid(sp.pid, NULL, 0);
return 0;
} 24
Plan For Today
• Recap: Pipes and dup2 so far
• Practice: implementing subprocess
• I/O Redirection with files
• Closing pipes
• Why are pipes shared when we call fork?
cp -r /afs/ir/class/cs111/lecture-code/lect11 . 25
Redirecting Process I/O to/from a File
There is one final shell feature we can use our understanding of file descriptors
to implement, I/O Redirection with a file:
This reads input from a file instead of reading from the terminal
sort < input.txt
Consider how we can use our knowledge of file descriptors to implement this
functionality on assign3!
26
Redirecting Process I/O to/from a File
Example: sort < input.txt
Child
STDIN STDOUT STDERR
input.txt Terminal
27
assign3
Implement your own shell! (“stsh” – Stanford Shell)
4 key features:
• Run a single command and wait for it to finish
• Run 2 commands connected via a pipe
• Run an arbitrary number of commands connected via pipes
• Have command input come from a file, or save command output to a file
28
Plan For Today
• Recap: Pipes and dup2 so far
• Practice: implementing subprocess
• I/O Redirection with files
• Closing pipes
• Why are pipes shared when we call fork?
cp -r /afs/ir/class/cs111/lecture-code/lect11 . 29
Pipe Stalling
Not closing write ends of pipes can cause functionality issues. If a process calls
read and there’s nothing more to read, but the write end is still open, it will
block until it gets more input!
- E.g. if the child reads from a pipe, but the parent waits for the child to finish
before writing anything, the child will stall
- E.g. if the child reads until there’s nothing left, but the write end was not
closed everywhere, it will stall.
30
Parent Child Pipe
static const char * kPipeMessage = "Hello, this message is coming through a pipe.";
int main(int argc, char *argv[]) {
int fds[2];
pipe(fds);
size_t bytesSent = strlen(kPipeMessage) + 1;
pid_t pidOrZero = fork();
if (pidOrZero == 0) { // In the child, we only read from the pipe
close(fds[1]);
char buffer[bytesSent];
read(fds[0], buffer, sizeof(buffer));
close(fds[0]);
printf("Message from parent: %s\n", buffer);
return 0;
}
// In the parent, we only write to the pipe (assume everything is written)
close(fds[0]);
write(fds[1], kPipeMessage, bytesSent);
close(fds[1]);
waitpid(pidOrZero, NULL, 0);
return 0; 31
Parent Doesn’t Send Message (still
finishes)
static const char * kPipeMessage = "Hello, this message is coming through a pipe.";
int main(int argc, char *argv[]) {
int fds[2];
pipe(fds);
size_t bytesSent = strlen(kPipeMessage) + 1;
pid_t pidOrZero = fork();
if (pidOrZero == 0) { // In the child, we only read from the pipe
close(fds[1]);
char buffer[bytesSent];
read(fds[0], buffer, sizeof(buffer));
close(fds[0]);
printf("Message from parent: %s\n", buffer);
return 0;
}
// In the parent, we only write to the pipe (assume everything is written)
close(fds[0]);
write(fds[1], kPipeMessage, bytesSent); // program will still terminate
close(fds[1]);
waitpid(pidOrZero, NULL, 0);
return 0; 32
Ex: Child reads, parent doesn’t write or
close
static const char * kPipeMessage = "Hello, this message is coming through a pipe.";
int main(int argc, char *argv[]) {
int fds[2];
pipe(fds);
size_t bytesSent = strlen(kPipeMessage) + 1;
pid_t pidOrZero = fork();
if (pidOrZero == 0) { // In the child, we only read from the pipe
close(fds[1]);
char buffer[bytesSent];
read(fds[0], buffer, sizeof(buffer)); child stuck here!
close(fds[0]);
printf("Message from parent: %s\n", buffer);
return 0;
}
// In the parent, we only write to the pipe (assume everything is written)
close(fds[0]);
write(fds[1], kPipeMessage, bytesSent);
close(fds[1]);
waitpid(pidOrZero, NULL, 0);
return 0; 33
Ex: Child reads, parent writes after waitpid
static const char * kPipeMessage = "Hello, this message is coming through a pipe.";
int main(int argc, char *argv[]) {
int fds[2];
pipe(fds);
size_t bytesSent = strlen(kPipeMessage) + 1;
pid_t pidOrZero = fork();
if (pidOrZero == 0) { // In the child, we only read from the pipe
close(fds[1]);
char buffer[bytesSent];
read(fds[0], buffer, sizeof(buffer)); child stuck here!
close(fds[0]);
printf("Message from parent: %s\n", buffer);
return 0;
}
// In the parent, we only write to the pipe (assume everything is written)
close(fds[0]);
waitpid(pidOrZero, NULL, 0);
write(fds[1], kPipeMessage, bytesSent);
close(fds[1]);
return 0; 34
Ex: Child reads continually, parent
doesn’t close
static const char * kPipeMessage = "Hello, this message is coming through a pipe.";
int main(int argc, char *argv[]) {
int fds[2];
pipe(fds);
size_t bytesSent = strlen(kPipeMessage) + 1;
pid_t pidOrZero = fork();
if (pidOrZero == 0) { // In the child, we only read from the pipe
close(fds[1]);
char buffer[bytesSent];
while (true) {
ssize_t ret = read(fds[0], buffer, sizeof(buffer)); child stuck here!
if (ret == 0) break;
printf("Message from parent: %s\n", buffer);
}
close(fds[0]);
return 0;
}
// In the parent, we only write to the pipe (assume everything is written)
close(fds[0]);
write(fds[1], kPipeMessage, bytesSent);
waitpid(pidOrZero, NULL, 0);
close(fds[1]);
35
return 0;
Ex: Child reads continually, forgets to
close write end itself
static const char * kPipeMessage = "Hello, this message is coming through a pipe.";
int main(int argc, char *argv[]) {
int fds[2];
pipe(fds);
size_t bytesSent = strlen(kPipeMessage) + 1;
pid_t pidOrZero = fork();
if (pidOrZero == 0) { // In the child, we only read from the pipe
close(fds[1]);
char buffer[bytesSent];
while (true) {
ssize_t ret = read(fds[0], buffer, sizeof(buffer)); child stuck here!
if (ret == 0) break;
printf("Message from parent: %s\n", buffer);
}
close(fds[0]);
return 0;
}
// In the parent, we only write to the pipe (assume everything is written)
close(fds[0]);
write(fds[1], kPipeMessage, bytesSent);
close(fds[1]);
waitpid(pidOrZero, NULL, 0);
36
return 0;
Plan For Today
• Recap: Pipes and dup2 so far
• Practice: implementing subprocess
• I/O Redirection with files
• Closing pipes
• Why are pipes shared when we call fork?
cp -r /afs/ir/class/cs111/lecture-code/lect11 . 37
File Descriptor Table
The OS maintains a “Process Control Block” for each process containing info
about it. This includes a process’s file descriptor table, an array of info about
open files/resources for this process.
Key idea: a file descriptor is an index into that process’s file descriptor table!
0 1 2 3 4 …
38
File Descriptor Table
Key idea: a file descriptor is an index into that process’s file descriptor table.
• An entry in a file descriptor table is really a pointer to an entry in another global
table, the open file table.
• The open file table is one array of information about open files/resources
across all processes. There’s one open file table entry per session (not per file).
Process A control block Process B control block
0 1 2 3 … … 0 1 2 3 … …
0 1 2 3 … … 0 1 2 3 … …
0 1 2 3 … …
41
Open File Table
Calling pipe creates 2 new open file table entries, and 2 new file descriptor
indexes point to them. The open file table entries are linked behind the scenes.
int fds[2];
pipe(fds); // afterwards, fds[0] = 3, fds[1] = 4
Process A control block
0 1 2 3 4 …
42
Open File Table
Calling fork means the OS creates a new Process Control Block with a copy of
parent’s FD table; so, all file descriptor indexes point to the same place!
int fds[2];
pipe(fds); // afterwards, fds[0] = 3, fds[1] = 4
pid_t pidOrZero = fork();
Parent process control block
0 1 2 3 4 …
43
Open File Table
Calling fork means the OS creates a new Process Control Block with a copy of
parent’s FD table; so, all file descriptor indexes point to the same place!
int fds[2];
pipe(fds); // afterwards, fds[0] = 3, fds[1] = 4
pid_t pidOrZero = fork();
Parent process control block Child process control block
0 1 2 3 4 … 0 1 2 3 4 …
44
Key Idea: on fork, the child process
gets “shallow copies” of all parent file
descriptors. This is how a parent and
child can share the same pipe even
though it’s “copied” on fork.
45
Reference Count
• When we call close, that makes the file descriptor index no longer point to an
open file table entry, and that open file table entry’s ref count is decremented.
• When open file table entry’s ref count == 0, it’s deleted
46
Key Idea: parent-child duplicated file
descriptors must be closed in both the
parent and child because both parent
and child are referencing them.
47
Practice: Reference Count
a) If a process opens a file, and then spawns a child process, what will the
reference count be for the corresponding open file table entry(ies)?
b) What about if a process spawns a child process and then opens a file?
0 1 2 3 … …
52
Practice: Reference Count
If a process opens a file, and then spawns a child process, what will the
reference count be for the corresponding open file table entry(ies)?
0 1 2 3 … …
53
Practice: Reference Count
If a process opens a file, and then spawns a child process, what will the
reference count be for the corresponding open file table entry(ies)?
0 1 2 3 … … 0 1 2 3 … …
54
Practice: Reference Count
If a process spawns a child process, and then opens a file, what will the
reference count be for the corresponding open file table entry(ies)?
0 1 2 … … …
55
Practice: Reference Count
If a process spawns a child process, and then opens a file, what will the
reference count be for the corresponding open file table entry(ies)?
0 1 2 … … … 0 1 2 … … …
56
Practice: Reference Count
If a process spawns a child process, and then opens a file, what will the
reference count be for the corresponding open file table entry(ies)?
0 1 2 3 … … 0 1 2 3 … …
57
Practice: Reference Count
a) If a process opens a file, and then spawns a child process, what will the
reference count be for the corresponding open file table entry(ies)? 2.
b) What about if a process spawns a child process and then opens a file? 1.
(a) explains why we must close this file in both the parent and child.
int fd = open(…);
pid_t pidOrZero = fork();
if (pidOrZero == 0) {
…
close(fd);
} else {
…
close(fd);
}
58
dup2 and Open File Table
Parent process control block
0 1 2 3 4 …
int fds[2];
pipe(fds); // assume fds[0] is 3 and fds[1] is 4
dup2(fds[0], STDIN_FILENO);
close(fds[0]);
59
dup2 and Open File Table
Parent process control block
0 1 2 3 4 …
int fds[2];
pipe(fds); // assume fds[0] is 3 and fds[1] is 4
dup2(fds[0], STDIN_FILENO);
close(fds[0]);
60
dup2 and Open File Table
Parent process control block
0 1 2 3 4 …
int fds[2];
pipe(fds); // assume fds[0] is 3 and fds[1] is 4
dup2(fds[0], STDIN_FILENO);
close(fds[0]);
61
Summary: File Desciptors / Open File
Table
• Per-process file descriptor table + global open file table. Entries in file
descriptor tables point to entries in the open file table.
• One open file table entry for each session (e.g. every open call), with refcount.
• If a pipe is created and then we call fork, the child accesses the same pipe
because its file descriptor table is copied, which does not contain the actual
pipe data; that is stored in the global “open file table” which is not duplicated
on fork.
62
Recap
• Recap: Pipes and dup2 so far Lecture 11 takeaway: We can
• Practice: implementing subprocess share pipes with child
• I/O Redirection with files processes and change FDs 0-2
• Closing pipes to connect processes and
• Why are pipes shared when we call redirect their I/O. File
fork? descriptors are shared on fork
because the file descriptor
table, which is copied, contains
Next time: introduction to pointers to a shared open file
multithreading table, which is not copied.
cp -r /afs/ir/class/cs111/lecture-code/lect11 . 63
Extra Slides
64
Demo: Parent Child Pipe
Parent process control block
0 1 2 3 4 …
static const char * kPipeMessage = "Hello, this message is coming through a pipe.";
int main(int argc, char *argv[]) {
int fds[2];
pipe(fds);
size_t bytesSent = strlen(kPipeMessage) + 1;
pid_t pidOrZero = fork();
if (pidOrZero == 0) { // In the child, we only read from the pipe
...
} 65
Demo: Parent Child Pipe
Parent process control block
0 1 2 3 4 …
static const char * kPipeMessage = "Hello, this message is coming through a pipe.";
int main(int argc, char *argv[]) {
int fds[2];
pipe(fds);
size_t bytesSent = strlen(kPipeMessage) + 1;
pid_t pidOrZero = fork();
if (pidOrZero == 0) { // In the child, we only read from the pipe
...
} 66
Demo: Parent Child Pipe
Parent process control block
0 1 2 3 4 …
static const char * kPipeMessage = "Hello, this message is coming through a pipe.";
int main(int argc, char *argv[]) {
int fds[2];
pipe(fds); // here, fds[0] = 3, fds[1] = 4
size_t bytesSent = strlen(kPipeMessage) + 1;
pid_t pidOrZero = fork();
if (pidOrZero == 0) { // In the child, we only read from the pipe
...
} 67
Demo: Parent Child Pipe
Parent process control block
0 1 2 3 4 …
static const char * kPipeMessage = "Hello, this message is coming through a pipe.";
int main(int argc, char *argv[]) {
int fds[2];
pipe(fds); // here, fds[0] = 3, fds[1] = 4
size_t bytesSent = strlen(kPipeMessage) + 1;
pid_t pidOrZero = fork();
if (pidOrZero == 0) { // In the child, we only read from the pipe
...
} 68
Demo: Parent Child Pipe
Parent process control block Child process control block
0 1 2 3 4 … 0 1 2 3 4 …
static const char * kPipeMessage = "Hello, this message is coming through a pipe.";
int main(int argc, char *argv[]) {
int fds[2];
pipe(fds); // here, fds[0] = 3, fds[1] = 4
size_t bytesSent = strlen(kPipeMessage) + 1;
pid_t pidOrZero = fork();
if (pidOrZero == 0) { // In the child, we only read from the pipe
...
} 69
Demo: Parent Child Pipe
Parent process control block Child process control block
0 1 2 3 4 … 0 1 2 3 4 …
static const char * kPipeMessage = "Hello, this message is coming through a pipe.";
int main(int argc, char *argv[]) {
int fds[2];
pipe(fds); // here, fds[0] = 3, fds[1] = 4
size_t bytesSent = strlen(kPipeMessage) + 1;
pid_t pidOrZero = fork();
if (pidOrZero == 0) { // In the child, we only read from the pipe
...
} 70
Demo: Parent Child Pipe
Parent process control block Child process control block
0 1 2 3 4 … 0 1 2 3 4 …
...
// In the parent, we only write to the pipe (assume everything is written)
close(fds[0]);
write(fds[1], kPipeMessage, bytesSent);
close(fds[1]);
waitpid(pidOrZero, NULL, 0);
return 0;
}
71
Demo: Parent Child Pipe
Parent process control block Child process control block
0 1 2 3 4 … 0 1 2 3 4 …
...
// In the parent, we only write to the pipe (assume everything is written)
close(fds[0]);
write(fds[1], kPipeMessage, bytesSent);
close(fds[1]);
waitpid(pidOrZero, NULL, 0);
return 0;
}
72
Demo: Parent Child Pipe
Parent process control block Child process control block
0 1 2 3 4 … 0 1 2 3 4 …
...
// In the parent, we only write to the pipe (assume everything is written)
close(fds[0]);
write(fds[1], kPipeMessage, bytesSent);
close(fds[1]);
waitpid(pidOrZero, NULL, 0);
return 0;
}
73
Demo: Parent Child Pipe
Parent process control block Child process control block
0 1 2 3 4 … 0 1 2 3 4 …
...
// In the parent, we only write to the pipe (assume everything is written)
close(fds[0]);
write(fds[1], kPipeMessage, bytesSent);
close(fds[1]);
waitpid(pidOrZero, NULL, 0);
return 0;
}
74
Demo: Parent Child Pipe
Parent process control block Child process control block
0 1 2 3 4 … 0 1 2 3 4 …
...
// In the parent, we only write to the pipe (assume everything is written)
close(fds[0]);
write(fds[1], kPipeMessage, bytesSent);
close(fds[1]);
waitpid(pidOrZero, NULL, 0);
return 0;
}
75
Demo: Parent Child Pipe
Parent process control block Child process control block
0 1 2 3 4 … 0 1 2 3 4 …
...
// In the parent, we only write to the pipe (assume everything is written)
close(fds[0]);
write(fds[1], kPipeMessage, bytesSent);
close(fds[1]);
waitpid(pidOrZero, NULL, 0);
return 0;
}
76
Demo: Parent Child Pipe
Parent process control block Child process control block
0 1 2 3 4 … 0 1 2 3 4 …
...
if (pidOrZero == 0) { // In the child, we only read from the pipe
close(fds[1]);
char buffer[bytesSent];
read(fds[0], buffer, sizeof(buffer));
close(fds[0]);
printf("Message from parent: %s\n", buffer);
return 0;
} ... 77
Demo: Parent Child Pipe
Parent process control block Child process control block
0 1 2 3 4 … 0 1 2 3 4 …
...
if (pidOrZero == 0) { // In the child, we only read from the pipe
close(fds[1]);
char buffer[bytesSent];
read(fds[0], buffer, sizeof(buffer));
close(fds[0]);
printf("Message from parent: %s\n", buffer);
return 0;
} ... 78
Demo: Parent Child Pipe
Parent process control block Child process control block
0 1 2 3 4 … 0 1 2 3 4 …
...
if (pidOrZero == 0) { // In the child, we only read from the pipe
close(fds[1]);
char buffer[bytesSent];
read(fds[0], buffer, sizeof(buffer));
close(fds[0]);
printf("Message from parent: %s\n", buffer);
return 0;
} ... 79
Demo: Parent Child Pipe
Parent process control block Child process control block
0 1 2 3 4 … 0 1 2 3 4 …
...
if (pidOrZero == 0) { // In the child, we only read from the pipe
close(fds[1]);
char buffer[bytesSent];
read(fds[0], buffer, sizeof(buffer));
close(fds[0]);
printf("Message from parent: %s\n", buffer);
return 0;
} ... 80
Demo: Parent Child Pipe
Parent process control block Child process control block
0 1 2 3 4 … 0 1 2 3 4 …
...
if (pidOrZero == 0) { // In the child, we only read from the pipe
close(fds[1]);
char buffer[bytesSent];
read(fds[0], buffer, sizeof(buffer));
close(fds[0]);
printf("Message from parent: %s\n", buffer);
return 0;
} ... 81
Demo: Parent Child Pipe
- CPlayground
https://cplayground.com/?p=hare-camel-buffalo&breakpoints=%5B11%5D
82