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

猜你喜欢

转载自www.cnblogs.com/kele-dad/p/9164635.html