ps axf # display process tree
ps axfj # with more info
ps axfjww # even if lines wrap around terminal
View process information and hierarchy:
man ps
)Consider the following simple shell program from APUE 1.6, Figure 1.7:
#include "apue.h"
#include <sys/wait.h>
int main(void)
{
char buf[MAXLINE]; /* from apue.h */
pid_t pid;
int status;
printf("%% "); /* print prompt (printf requires %% to print %) */
while (fgets(buf, MAXLINE, stdin) != NULL) {
if (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = 0; /* replace newline with null */
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) { /* child */
execlp(buf, buf, (char *)0);
err_ret("couldn't execute: %s", buf);
exit(127);
}
/* parent */
if ((pid = waitpid(pid, &status, 0)) < 0)
err_sys("waitpid error");
printf("%% ");
}
exit(0);
}
fork()
: Create a child process, which is a clone of the parent process. Both
return from fork()
, but with different return values.execlp()
: Causes the program that is currently being run by the calling
process to be replaced with a new program, with newly initialized stack, heap,
and data.waitpid()
: Query or wait for state changes in a child of the calling
process, and also reclaim system resources associated with terminated child
processes.What happens if the parent process terminates before its children?
init
process which periodically calls a
variant of waitpid()
.What happens if a child process has terminated, but the parent never calls
waitpid()
?
Recall that memory mappings can be private or shared. When a process calls
fork()
to create a child process, the parent’s private mappings are duplicated
for the child process, whereas the child inherits the parent’s shared mappings.
In practice, the OS kernel does not eagerly create independent copies of private mappings. Instead, such mappings are marked read-only and shared between the processes until one of them makes a modification. At that point, the OS kernel creates the independent copy of the mapping. This optimization is known as Copy-On-Write (COW). Some mappings will never be modified and remain shared, e.g., program code.
COW is an especially performant optimization when you consider the fact that
fork()
is often followed by exec()
which will replace the program image
anyways.
The exec()
system call also takes advantage of memory mappings. Loading the
new program code into memory can be accomplished simply by mapping the
executable file into the text region of the process’s address space.
Here is the process hierarchy for an SSH login session:
root 1 Jan18 /lib/systemd/systemd --system --deserialize 43
root 2310948 Jan31 \_ sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
root 1525244 10:59 \_ sshd: hans [priv]
hans 1525262 10:59 \_ sshd: hans@pts/26
hans 1525264 10:59 \_ -bash
hans 1556817 11:31 \_ ps auxfww
Last Updated: 2024-02-04