The global unique process identifer is used to refer a process in Unix system. Use getpid
and getppid
to get the process identifer of the calling process and its parent process. Expect the process identifer, a process also has some other identifiers such as user id uid
, effective user id euid
, group id gid
, effective group id egid
. Those identifiers can be retrive by the following functions.
#include <unistd.h>
void);
pid_t getpid(void);
pid_t getppid(void);
uid_t getuid(void);
uid_t geteuid(void);
gid_t getgid(void); gid_t getegid(
Some process identifers are specialized to specific processes. For example, in most of Unix implementations, pid 0 is the scheduler process swapper
, which is actually not a real process. pid 1 is the init
process, which is invoked by the kernel at the end of bootstrap procedure. Notice that init
process is actually not a kernel process but a user process.
In Mac OS X 10.4, the
init
process wa replaced with thelaunchd
process, which performs the same set of tasks as init, but has expanded functionalty.
fork
We create(duplicate) a new child process by fork
:
#include <unistd.h>
void); pid_t fork(
fork
returns 0 in child, process ID of the child in parent, -1 on error.
We’ll explain the behaviour of fork
by a demo program, and also the common structure of a function that invokes fork
:
#include <stdio.h>
#incldue <stdlib.h>
#include <unistd.h>
int main(int argc, char* argv[]){
"hello world (pid:%d)\n", (int) getpid());
printf(int rc = fork();
if (rc < 0) {
"fork failed\n");
fprintf(stderr, 1);
exit(else if (rc == 0){
} "hello, I am child (pid:%d)\n",
printf(int) getpid());
(else {
} "hello, I am parent of %d (pid:%d)\n",
printf(int) getpid())
rc, (
}return 0;
}
And the outputs are
hello world (pid: 10693)
hello, I am child (pid:10694)
hello, I am parent of 10694 (pid: 10693)
by invoking fork
, the process was duplicated to create a child, and the child starts running from the line int rc = fork()
(not from the beginning of function, and this is somewhat impossible actually…). At this line, one fork
call generates two values: for the parent, fork
returns the pid of child if success, and for the child, fork
returns 0. Hence, the conditional statement about rc
helps us distingulish whether its the parent or child process.
Notice that for the two different processes, the sequence of execution is not determinant, which is depends on the scheduling by CPU. As a result, the output for the given demo is not determined, the output from the child and the parent might exchange.
File fork_file_sharing_example.md gives a more complex example relavent to the file descriptor sharing between parent process and its children, providing by APUE.
Due to the counterintuitive design of fork
, the behaviour of the child process should be carefully taken care of. a_fork_bug.md gives an tricky bug (maybe not that tricky after we found it…) that caused by the misunderstanding about how fork
works, raised during the development of shell.
wait
For a parent process, wait
will block itself until one of its child terminates. For example, in the demo given for fork
, we can add wait()
before the printf
from parent, to make the parent do not print until the child has done.
If we need to wait for n-children, just make n times of wait
call, each termination of child process will only unblcok one of the wait
.
exec
exec
replace the program running in current process with a new one. All the relavent data are replaced, without the PID, since no new process is created.
There are seven different exec
functions with different arguments
#include <unistd.h>
int execl(const char* pathname, const char* arg0, ... );
int execle(const char* pathname, const char* arg0, ..., char* const envp[]);
int execlp(const char* filename, const char* arg0, ...);
int execv(const char* pathname, char* const argv[]);
int execve(const char* pathname, char* const argv[], char* const envp[]);
int execvp(const char* filename, char* const argv[]);
The appeding letters hint the differences among these exec
:
l
and v
represents whether to use individual arguments or an pointer to the argument array.e
allows setting a new environment variables list for the new process instead of copying the current one directly, which can be considered as one approach of passing parameters.p
allows search for filename
in the PATH
, but this is not necessary, the absolute/relative directory can still be used in the filename
argument.The typical usage of exec
is shown as the following demo
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc, char* argv[]){
int rc = fork();
if (rc < 0) {
"fork failed\n");
fprintf(stderr, 1);
exit(else if (rc == 0) {
}
close(STDOUT_FIELNO);"./p4.output", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
open(char* myargs[3];
0] = strdup("wc");
myargs[1] = strdup("p4.c");
myargs[2] = NULL;
myargs[0], myargs);
execvp(myargs[else {
} int wc = wait(NULL);
} }