0%
第 5 章 插叙:进程 API
5.1 fork()
- 调用一次,返回两次
- 父进程和子进程的运行顺序是不确定的
- 由于CPU 调度程序非常复杂,所以我们不能假设哪个进程会先运行
5.2 wait()
5.3 exec()
- 让子进程运行不一样的程序
- 调用一次,从不返回
- 没有创建新进程,而是直接将当前运行的程序替换为不同的运行程序
- 从可执行程序中加载代码和静态数据,并用它覆写自己的代码段(以及静态数据),重新初始化堆、栈及其他内存空间
1 2 3 4 5
| char *myargs[3]; myargs[0] = strdup("wc"); myargs[1] = strdup("p3.c"); myargs[2] = NULL; execvp(myargs[0], myargs);
|
5.4 为什么这样设计 API
- UNIX shell
- 给了shell 在 fork 之后 exec 之前运行代码的机会
- 这样可以改变运行时环境,实现有趣的功能
- 重定向文件输出
5.5 其他 API
- kill()
- 信号(signal)、
- 查看 man 手册获取更多知识
作业
(1)
(2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <unistd.h> #include <fcntl.h> #include <stdio.h>
int main(int argc, char** argv) { int pid; close(STDOUT_FILENO); open("2.txt", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU); pid = fork(); if(pid < 0) { fprintf(stderr, "Fork Error!\n"); } else if(pid == 0) { printf("cccccc"); } else { printf("pppppp"); } return 0; }
|
- 子进程和父进程都能共访问 open()
返回的文件描述符,因为父子进程虽然i有自己的文件描述符列表,但是共享打开文件
- 父子进程同时写文件的时候不能保证谁先写入,我的 wsl 中结果为
(3)
(4)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <unistd.h> #include <fcntl.h> #include <stdio.h> #include <sys/wait.h> #include <string.h>
int main(int argc, char** argv, char** env) { int pid; pid = fork(); if(pid < 0) { fprintf(stderr, "Fork Error!\n"); } else if(pid == 0) { char *myargs[2]; myargs[0] = strdup("/bin/ls"); myargs[1] = NULL; execv(myargs[0], myargs); } else { wait(NULL); } return 0; }
|
- 结果确实可以执行 ls 命令,输出当前目录下的文件按
- 我的输出如下
1
| 2.c 2.out 2.txt 4.c 4.out
|
- 同样的基本调用,不同的封装方式为了满足不同的应用场景需要
- 区别如下
- l:用参数列表的方式传递新程序的参数,最后一个参数需要显示的指定为
NULL
- v:用数组的方式传递新程序的参数,数组最后一个值需要显示的指定为
NULL
- e:设置新的环境变量,作为一个数组在最后一个参数传入
- p:运行的可执行文件只需要传递文件名即可,否则需要传递全路径
(5)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <unistd.h> #include <fcntl.h> #include <stdio.h> #include <sys/wait.h> #include <string.h>
int main(int argc, char** argv, char** env) { int pid; pid = fork(); if(pid < 0) { fprintf(stderr, "Fork Error!\n"); } else if(pid == 0) { printf("I'm child!\n"); } else { printf("wait'return pid: %d\n", wait(NULL)); printf("child's pid: %d\n", pid); printf("I'm father!\n"); } return 0; }
|
1 2 3 4
| I'm child! wait'return pid: 323 child's pid: 323 I'm father!
|
wait(NULL)
返回等待的子进程的 pid
- 子进程使用
wait(NULL)
会返回 -1
- 使用
man wait
可以查看具体的含义
wait(NULL)
操作当一个子进程结束的就会返回其 pid,等效于
waitpid(-1, &wstatus, 0)
(6)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <unistd.h> #include <fcntl.h> #include <stdio.h> #include <sys/wait.h> #include <string.h>
int main(int argc, char** argv, char** env) { int pid; pid = fork(); if(pid < 0) { fprintf(stderr, "Fork Error!\n"); } else if(pid == 0) { printf("I'm child!\n"); } else { printf("wait'return pid: %d\n", waitpid(-1, NULL, 0)); printf("child's pid: %d\n", pid); printf("I'm father!\n"); } return 0; }
|
wait(NULL)
等价于
waitpid(-1, NULL, 0)
(7)
(8)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <unistd.h> #include <fcntl.h> #include <stdio.h> #include <sys/wait.h> #include <string.h> #include <stdlib.h>
int main(int argc, char** argv, char** env) { int pid; int pipe_fd[2]; if(pipe(pipe_fd) == -1) { fprintf(stderr, "Pipe Error!\n"); } pid = fork(); if(pid < 0) { fprintf(stderr, "Fork Error!\n"); } else if(pid == 0) { dup2(pipe_fd[1], STDOUT_FILENO); close(pipe_fd[0]); printf("I'm child!\n"); close(pipe_fd[1]); } else { char* msg = NULL; size_t len = 0; dup2(pipe_fd[0], STDIN_FILENO); close(pipe_fd[1]); getline(&msg, &len, stdin); printf("I'm father!\nmessage from child: %s\n", msg); close(pipe_fd[0]); free(msg); } return 0; }
|
1 2 3
| I'm father! message from child: I'm child!
|