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,组长就为父进程。
















