系统调用 fork()用于创建新进程.
#include#include #include int main(int argc, char *argv[]) { printf("hello world (pid:%d)n", (int) getpid()); int rc = fork(); if (rc < 0) { // fork failed; exit fprintf(stderr, "fork failedn"); exit(1); } else if (rc == 0) { // child (new process) printf("hello, I am child (pid:%d)n", (int) getpid()); } else { // parent goes down this path (main) printf("hello, I am parent of %d (pid:%d)n",rc, (int) getpid()); } return 0; }
- 开始运行时,进程输出一条 hello world 信息,以及自己的进程描述符(process identifier,PID)。该进程的 PID 是 3580078,输出是不确定的(deterministic)(每个人电脑不一样,每次运行的结果也不同),之后进程调用了 fork()系统调用,这是操作系统提供的创建新进程的方法。
- 新创建的进程称为子进程(child),原来的进程称为父进程(parent)。子进程不会从 main()函数开始执行(故而 hello world 信息只输出了一次),而是直接从 fork()系统调用返回,就好像是它自己调用了 fork()。
- 子进程并不是完全拷贝了父进程。它拥有自己的私有内存、寄存器、程序计数器等,但是它从 fork()返回的值是不同的。
- 父进程获得的返回值是新创建子进程的 PID,而子进程获得的返回值是 0。
有时候父进程需要等待子进程执行完毕,此时需要 wait()系统调用
#include#include #include #include int main(int argc, char *argv[]) { printf("hello world (pid:%d)n", (int) getpid()); int rc = fork(); if (rc < 0) { // fork failed; exit fprintf(stderr, "fork failedn"); exit(1); } else if (rc == 0) { // child (new process) printf("hello, I am child (pid:%d)n", (int) getpid()); } else { // parent goes down this path (main) int wc = wait(NULL); printf("hello, I am parent of %d (wc:%d) (pid:%d)n",rc, wc, (int) getpid()); } return 0; }
- 父进程调用 wait(),延迟自己的执行,直到子进程执行完毕。当子进程结束时,wait()才返回父进程。
- 该系统调用会等待子进程运行结束后才返回。因此,即使父进程先运行,它也会等待子进程运行完毕,然后 wait()返回,接着父进程输出自己的信息。
该系统调用可以使子进程执行与父进程不同的程序
vim m.c
#include#include #include #include #include int main(int argc, char *argv[]) { printf("hello world (pid:%d)n", (int) getpid()); int rc = fork(); if (rc < 0) { // fork failed; exit fprintf(stderr, "fork failedn"); exit(1); } else if (rc == 0) { // child (new process) printf("hello, I am child (pid:%d)n", (int) getpid()); char *myargs[3]; myargs[0] = strdup("wc"); // program: "wc" (word count) myargs[1] = strdup("m.c"); // argument: file to count myargs[2] = NULL; // marks end of array execvp(myargs[0], myargs); // runs word count printf("this shouldn't print out"); } else { // parent goes down this path (main) int wc = wait(NULL); printf("hello, I am parent of %d (wc:%d) (pid:%d)n",rc, wc, (int) getpid()); } return 0; }
- 子进程调用 execvp()来运行字符计数程序 wc。对m.c运行 wc,该文件有多少行、多少单词,以及多少字节。
- exec()会从可执行程序中加载代码和静态数据,并用它覆写自己的代码段(以及静态数据),堆、栈及其他内存空间也会被重新初始化。然后操作系统就执行该程序,将参数通过 argv 传递给该进程。因此,它并没有创建新进程,而是直接将当前运行的程序替换为不同的运行程序(wc)。子进程执行 exec()之后,几乎就像m.c 从未运行过一样。对 exec()的成功调用永远不会返回。



