fork() and pipe()
fork()
# create a new process
fork()
is the UNIX system call that creates a new process.-
fork()
creates a new process that is a copy of the calling process. -
After
fork()
we refer to the caller as the parent and the newly-created process as the child. This relationship enables certain capabilities.
fork()
Semantics
-
Generally
fork()
tries to make an exact copy of the calling process.-
Recent version of UNIX have relaxed this requirement and there are now many flavors of
fork()
that copy different amounts of state and are suitable for different purposes. -
For the purposes of this class, ignore them.
-
-
Threads are a notable exception!
fork()
Against Threads
-
Single-threaded
fork()
has reliable semantics because the only thread the processes had is the one that calledfork()
.-
So nothing else is happening while we complete the system call.
-
-
Multi-threaded
fork()
creates a host of problems that many systems choose to ignore.-
Linux will only copy state for the thread that called
fork()
.
-
Multi-Threaded fork()
fork()
-
Another thread could be blocked in the middle of doing something (uniprocessor systems), or
-
another thread could be actually doing something (multiprocessor systems).
fork()
-
fork()
copies one thread—the caller. -
fork()
copies the address space. -
fork()
copies the process file table.
After fork()
returnCode = fork();
if (returnCode == 0) {
# I am the child.
} else {
# I am the parent.
}
-
The child thread returns executing at the exact same point that its parent called
fork()
.-
With one exception:
fork()
returns twice, the PID to the parent and 0 to the child.
-
-
All contents of memory in the parent and child are identical.
-
Both child and parent have the same files open at the same position.
-
But, since they are sharing file handles changes to the file offset made by the parent/child will be reflected in the child/parent!
-
Pipes
pipe()
system call.-
pipe()
creates an anonymous pipe object and returns a two file descriptors: one for the read-only end, and the other for the write-only end. -
Anything written to the write-only end of the pipe is immediately available at the read-only end of the pipe.
-
Pipe contents are buffered in memory.
-
Why is this useful?
IPC Using fork()
and pipe()
-
Before calling
fork()
the parent creates a pipe object by calling pipe(). -
Next, it calls
fork()
. -
After
fork()
the parent closes its copy of the read-only end and the child closes its copy of the write-only end. -
Now the parent can pass information to the child.
# pipeEnds[0] gets the read end; pipeEnds[1] gets the write end.
int pipeEnds[2];
pipe(pipeEnds);
int returnCode = fork();
if (returnCode == 0) {
# Don't need a loopback.
close(pipeEnds[1]);
# Read some data from the pipe.
char data[14];
read(pipeEnds[0], data, 14);
} else {
# Don't need a loopback.
close(pipeEnds[0]);
# Write some data to the pipe.
write(pipeEnds[1], "Hello, sweet child!\n", 14);
}
Issues with fork()
-
Especially when the next thing that a process frequently does is start load a new binary which destroys most of the state
fork()
has carefully copied!
-
Optimize existing semantics: through copy-on-write, a clever memory-management optimization we will discuss in several weeks.
-
Change the semantics:
vfork()
, which will fail if the child does anything other than immediately load a new executable.-
Does not copy the address space!
-
-
fork()
is now replaced byclone()
, a more flexible primitive that enables more control:-
over sharing, including sharing memory, and signal handlers,
-
and over child execution, which begins at a function pointer passed to the system call instead of resuming at the point where
fork()
was called.
-
-
Try
man clone
in your CSE421 VM.