23.1 进程链和进程扇
23.1.1 概念
进程链:一个父进程构建出一个子进程,子进程再构建出子子进程,子子进程构建出子子子进程。。。。 这种就为进程链
进程扇:一个父进程构建出多个子进程,子进程都是由同一个父进程构建出来
23.1.2 进程链的构建
process_link.c
1 /* 创建5个进程(包括父进程) */ 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 6 int main(int argc, char *argv[]) 7 { 8 int counter = 0; 9 10 if(argc < 2) { 11 counter = 2; 12 } else { 13 counter = atoi(argv[1]); 14 } 15 16 int i = 1; 17 pid_t pid; 18 19 //循环变量从1开始,要减去父进程,即创建4个子进程 20 //需要保证父进程要跳出循环,子进程去创建子子进程 21 for(; i < counter; i++) { 22 pid = fork(); 23 if(pid < 0) { 24 perror("fork error"); 25 exit(1); 26 } else if(pid > 0) { 27 break; //父进程退出循环,子进程继续做循环 28 } 29 } 30 31 printf("pid : %d, ppid: %d ", getpid(), getppid()); 32 while(1) { 33 sleep(1); 34 } 35 36 return 0; 37 }
运行:
执行 ps -ef | grep process_link 查看进程:
注意 8468 进程编号并不是 process_link 进程,它为 shell 的进程,运行的 process_link 的进程的父进程就是shell终端
23.1.3 进程扇的构建
1 /* 创建5个进程(包括父进程) */ 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 6 int main(int argc, char *argv[]) 7 { 8 int counter = 0; 9 10 if(argc < 2) { 11 counter = 2; 12 } else { 13 counter = atoi(argv[1]); 14 } 15 16 int i = 1; 17 pid_t pid; 18 19 //循环变量从1开始,要减去父进程,即创建4个子进程 20 //需要保证父进程要跳出循环,子进程去创建子子进程 21 for(; i < counter; i++) { 22 pid = fork(); 23 if(pid < 0) { 24 perror("fork error"); 25 exit(1); 26 } else if(pid == 0) { 27 break; //子进程退出循环,父进程继续做循环 28 } 29 } 30 31 printf("pid : %d, ppid: %d ", getpid(), getppid()); 32 while(1) { 33 sleep(1); 34 } 35 36 return 0; 37 }
编译运行如下,
子进程对应的父进程都为 113892
23.2 守护进程和孤儿进程
进程在操作系统中根据功能分为各种各样的进程。
23.2.1 守护进程
- 守护进程(daemon)是生存期长的一种进程。它们常常在系统引导装入时启动,在系统关闭时终止。
- 所有守护进程都以超级用户(用户 ID 为0)的优先权运行
- 守护进程没有控制终端
- 守护进程的父进程都是 init 进程
23.2.2 孤儿进程
- 父进程结束,子进程就成为孤儿进程,会由 1 号进程(init 进程)领养
process_orphen.c
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 5 int main(void) 6 { 7 pid_t pid; 8 9 pid = fork(); 10 if(pid < 0) { 11 perror("fork error"); 12 exit(1); 13 } else if(pid > 0) { 14 printf("%d deaded ", getpid()); 15 exit(0); 16 } else { 17 sleep(4); 18 printf("pid : %d, ppid : %d ", getpid(), getppid()); 19 } 20 21 return 0; 22 }
编译运行:
这里由些奇怪的地方是孤儿进程被进程 2323领养,查看下这个进程:
这个进程的作用是:用于linux开机自动启动某些后台服务,同时还承担监控这些服务运行状态的功能。
这个进程代替了 1 号进程的一些特性,如果作死想试下关闭掉这个进程,可以进入下面的链接尝试:
https://www.cnblogs.com/chilumanxi/p/5136102.html
23.2.3 僵尸进程
- 子进程结束,但是没有完全释放内存(在内核中的 task_struct 没有释放),该进程就成为僵尸进程。
- 当僵尸进程的父进程结束后,就会被 init 进程领养,最终被回收
- 避免僵尸进程
- 让僵尸进程的父进程来回收,父进程每隔一段时间来查询子进程是否结束并回收,调用 wait() 或者 waitpid() ,通知内核释放僵尸进程
- 采用信号 SIGCHLD 通知处理,并在信号处理程序中调用 wait 函数
- 让僵尸进程成为孤儿进程,由 init 进程回收
(1)构建僵尸进程
process_zombie.c
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 5 int main(void) 6 { 7 pid_t pid; 8 9 pid = fork(); 10 if(pid < 0) { 11 perror("fork error"); 12 exit(1); 13 } else if(pid == 0) { 14 printf("pid : %d, ppid: %d ", getpid(), getppid()); 15 exit(0); //子进程结束成为僵尸进程 16 } 17 18 while(1) {//父进程继续循环 19 sleep(1); 20 } 21 22 exit(0); 23 }
编译运行:
另开一终端,查看进程
114707 为父进程,为S+,即可中断运行状态
子进程为 114708,状态为 Z+,Z即代表是僵尸进程,或者看进程名的 defunct ,有这个名字也为僵尸进程