二十七、Linux 进程与信号---进程组和组长进程
27.1 进程组
27.1.1 进程组介绍
- 进程组为一个或多个进程的集合
- 进程组可以接受同一终端的各种信号,同一个信号发送进程组等于发送给组中的所有进程
- 每个进程组有唯一的进程组 ID
- 进程组的消亡要等到组中所有的进程结束
- 进程组的消亡:kill 发送信号给进程组
- kill -9 -进程组号
27.1.2 进程组 ID 获取--- getpgrp 和 getpgid
(1)getpgrp --- 获取调用进程的进程组ID
1 #include <unistd.h> 2 pid_t getpgrp(void);
- 函数说明
- getpgrp()用来取得目前进程所属的组识别码。此函数相当于调用 getpgid(0);
- 返回值
- 返回目前进程所属的组识别码。
(2)getpgid --- 获取 pid 所在进程组的 ID
1 #include <unistd.h> 2 pid_t getpgid( pid_t pid);
- 函数说明
- getpgid()用来取得参数 pid 指定进程所属的组识别码。如果参数 pid 为0,则会取得目前进程的组识别码。
- 返回值
- 执行成功则返回组识别码,如果有错误则返回 -1 ,错误原因存于 errno 中。
- 错误代码
- ESRCH 找不到符合参数 pid 指定的进程。
- getpgid(getpid()) 获取指定进程的进程组
- getpgid(0) 获取当前进程的进程组
27.2 组长进程
27.2.1 概念
- 每个进程组可以有个组长进程,组长进程的ID就是进程组的ID
- 组长进程可以创建进程组以及该组中的进程
- 进程组的创建从第一个进程(组长进程)加入开始
- 进程组的组号取第一个加入组的进程(组长进程)编号
27.2.2 设置进程组 ID--- setpgid(创建进程组或将指定进程加入到指定的进程组中)
1 #include <unistd.h> 2 int setpgid(pid_t pid,pid_t pgid);
- 函数说明
- setpgid() 将参数 pid 指定进程所属的组 ID 设为参数 pgid 指定的组 ID。如果参数 pid 为 0 ,则会用来设置目前进程的组识别码,如果参数 pgid 为0,则会以目前进程的进程 ID 来取代。
- 函数功能:将进程加入到指定的进程组中, pid 为进程号, pgid 为组号
- 返回值
- 执行成功则返回组 ID,如果有错误则返回-1,错误原因存于 errno 中。
- 错误代码
- EINVAL 参数 pgid 小于0。
- EPERM 进程权限不足,无法完成调用。
- ESRCH 找不到符合参数 pid 指定的进程。
27.3 例子
27.3.1 构建进程扇组
构建一个进程扇,要求每两个进程为一个进程组,如下所示
1 #include <unistd.h> 2 #include <fcntl.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <sys/wait.h> 6 7 int main(void) 8 { 9 10 //创建进程组,组长进程为父进程 11 setpgid(getpid(), getpid()); 12 13 /* 组1 */ 14 // 或 pid_t group1 = getpgid(0); 15 pid_t group1 = getpgid(getpid()); 16 pid_t group2; 17 18 int i = 0; 19 pid_t pid; 20 21 for(; i < 3; i++) { 22 pid = fork(); 23 if(pid < 0) { 24 perror("fork error`"); 25 exit(1); 26 } else if(pid > 0) {//父进程 27 //父进程中执行和子进程相同的操作 28 if(i == 0) { 29 //要注意fork 在父进程中返回的是子进程的 pid 30 setpgid(pid, group1); 31 } 32 33 //第二个子进程作为组长进程,要创建进程组 34 if(i == 1) { 35 setpgid(pid, pid); 36 group2 = getpgid(pid); 37 } 38 39 if(i == 2) { 40 //第三个子进程加入到 group2 41 setpgid(pid, group2); 42 } 43 } else {//子进程 44 //将第一个子进程加入到 group1 45 if(i == 0) { 46 setpgid(getpid(), group1); 47 } 48 49 //第二个子进程作为组长进程,要创建进程组 50 if(i == 1){ 51 setpgid(getpid(), getpid()); 52 group2 = getpgid(getpid()); 53 } 54 55 if(i == 2) { 56 setpgid(getpid(), group2); 57 } 58 59 //因为是进程扇,每一个子进程要退出循环 60 //父进程继续循环 fork 61 break; 62 } 63 } 64 65 printf("pid: %d, ppid: %d, pgid: %d\n", getpid(), getppid(), getpgid(0)); 66 67 //防止僵尸进程产生 68 for(i = 0; i < 3; i++) { 69 wait(0); 70 } 71 72 exit(0); 73 }
编译运行:
27.3.2 构建进程链组
1 #include <unistd.h> 2 #include <fcntl.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <sys/wait.h> 6 7 int main() 8 { 9 //创建进程组1,父进程为组长进程 10 setpgid(getpid(), getpid()); 11 pid_t group1 = getpgid(getpid()); 12 13 pid_t pid; 14 int i = 0; 15 for(;i < 2; i++) { 16 pid = fork(); 17 if(pid < 0) { 18 perror("fork error"); 19 exit(1); 20 } else if(pid > 0) { 21 if(i == 0) { 22 //创建进程组2,第一个子进程作为组长进程 23 setpgid(pid, pid); 24 } 25 26 if(i == 1) { 27 //将第二个子进程加入到 group1中 28 setpgid(pid, group1); 29 } 30 31 // 在进程链中,父进程操作完退出循环 32 break; 33 } else if(pid == 0) { 34 35 if(i == 0) { 36 //创建进程组2,第一个子进程作为组长进程 37 setpgid(getpid(), getpid()); 38 } 39 40 if(i == 1) { 41 //将第二个子进程加入到 group1中 42 setpgid(getpid(), group1); 43 } 44 } 45 } 46 47 printf("pid: %d, ppid: %d, pgid: %d\n", getpid(), getppid(), getpgid(0)); 48 49 //防止僵尸进程产生 50 for(i = 0; i < 2; i++) { 51 wait(0); 52 } 53 54 return 0; 55 }
编译运行:
27.3.3 进程组删除
1 #include <unistd.h> 2 #include <fcntl.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <sys/wait.h> 6 7 int main(void) 8 { 9 //创建进程组,父进程作为组长进程 10 setpgid(getpid(), getpid()); 11 12 pid_t pid = fork(); 13 if(pid < 0){ 14 perror("fork error"); 15 exit(1); 16 } else if(pid > 0) { 17 //将子进程加入到父进程所在的组 18 setpgid(pid, getpgid(getpid())); 19 } else { 20 //将子进程加入到父进程所在的组 21 setpgid(getpid(), getpgid(getppid())); 22 } 23 24 printf("pid: %d, ppid: %d, pgid: %d\n", getpid(), getppid(), getpgid(0)); 25 26 pause();//进程暂停,等待信号 27 28 return 0; 29 }
编译运行:
(1)杀掉进程组
(2)杀掉父进程
杀掉父进程后,子进程依然存在,只不过变成了孤儿进程,并被2323进程所领养
(3)杀掉子进程
子进程变为了僵尸进程,子进程被杀死,但是父进程并没有回收子进程。
杀掉父进程:
僵尸进程被回收
27.3.4 子进程不加入父进程组中
1 #include <unistd.h> 2 #include <fcntl.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <sys/wait.h> 6 7 int main(void) 8 { 9 //创建进程组,父进程作为组长进程 10 setpgid(getpid(), getpid()); 11 12 pid_t pid = fork(); 13 if(pid < 0){ 14 perror("fork error"); 15 exit(1); 16 } else if(pid > 0) { 17 //将子进程加入到父进程所在的组 18 setpgid(pid, getpgid(getpid())); 19 } else { 20 //将子进程加入到父进程所在的组 21 //setpgid(getpid(), getpgid(getppid())); 22 } 23 24 printf("pid: %d, ppid: %d, pgid: %d\n", getpid(), getppid(), getpgid(0)); 25 26 pause();//进程暂停,等待信号 27 28 return 0; 29 }
编译运行:
没有将子进程加入到组中,依然将子进程加入到父进程组了。
注意:从 shell 上启动一个父进程,然后从父进程上创建若干个子进程,默认情况下都加入到父进程所在的组中,组号就是父进程的 PID,组长就为父进程。