一、进程组
1. 进程组
(1)进程组,也称之为作业,BSD与1980年前后向UNIX中增加的一个新特性,代表一个或多个进程的集合。每个进程都属于一个进程组,在waitpid函数和kill函数的参数中都曾经使用到,操作系统设计的进程组的概念,是为了简化对多个进程的管理。
当父进程创建子进程的时候,默认子进程与父进程属于同一个进程组,进程组ID等于进程组第一个进程ID(组长进程)。所以,组长进程标识:其进程组ID等于其进程ID.
组长进程可以创建一个进程组,创建该进程组的进程,然后终止,只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。
(2)kill发送给进程组
使用 kill -n -pgid 可以将信号 n 发送到进程组 pgid 中的所有进程。例如命令 kill -9 -4115 表示杀死进程组 4115 中的所有进程。
2. getpgid、getpgrp函数原型:
pid_t getpgrp(void);
pid_t getpgid(pid_t pid);
分析:
- 函数1:获取当前进程的进程组ID
- 函数2:如果pid = 0,那么该函数作用和getpgrp一样。
3. setpgid函数函数原型:改变进程默认所属的进程组,通常可用来加入一个现有的进程组或新进程组。
int setpgid(pid_t pid, pid_t pgid);
分析:将参数1对应的进程,加入参数2对应的进程组中。
注意:
- 如改变子进程为新进程组,用fork后,exec前。
- 权级问题:非root进程只能改变自己创建的子进程,或有权限操作的进程。
4. 测试代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main()
{pid_t pid;if ((pid = fork()) < 0) {perror("fork");exit(1);}else if (pid == 0) //子进程{printf("child PID = %d\n", getpid());printf("child Group ID = %d\n", getpgid(0)); //返回组idsleep(7);printf("-------Group ID of child id change to %d\n", getpgid(0));exit(0);}else if (pid > 0) //父进程{sleep(1);setpgid(pid, pid); //让子进程自立门户,成为进程组组长,以它的pid为进程组 id sleep(13);printf("\n");printf("parent PID = %d\n", getpid());printf("parent's parent PID = %d\n", getppid());printf(" parent Group ID = %d\n", getpgid(0));sleep(5);setpgid(getpid(), getppid()); //改变父进程组id为父进程的父进程printf("\n-------Group ID of parent is change to %d\n", getpgid(0));while (1);}return 0;
}
输出结果:
二、进程组的应用
1. 实验一:
题目:利用进程扇完成一个小实验。该进程扇有 1 个父进程和 3 个子进程,我们希望达到图 1 中的效果,即将进程 0 (父进程)和进程 1 设置成一组,假设为组 1,将进程 2 和 进程 3 设置成另一个组,假设为组 2. 另外,我们希望进程 0 和进程 2 分别是这两个组的组长。
1. 测试代码:
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>int main()
{int pid, i;int group1, group2;// 设置父进程(进程 0)为组长 setpgid(getpid(), getpid());group1 = getpgid(getpid());for (i = 1; i <= 3; ++i) {pid = fork();if (pid == 0) child{if (i == 1) {// 如果 group1 根本不存在,就会出问题。// 比如进程 0 已经运行结束。setpgid(getpid(), group1);}else if (i == 2) {setpgid(getpid(), getpid());group2 = getpgid(getpid());}else if (i == 3) {// 试想如果进程 2 还没运行,进程 3 先运行了,// 这时候 group2 还未进行设置,这里就会有问题。// 或者进程 2 已经结束,那进程 3 的设置也会失败setpgid(getpid(), group2);}break;}else if (pid < 0) {perror("fork");return -1;}}printf("进程 %d, pid: %d -> ppid: %d, pgid: [%d], (%s)\n", i % 4, getpid(), getppid(), getpgid(getpid()), strerror(errno));while (1) sleep(1);return 0;
}
输出结果:
测试代码:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>int main(void)
{setpgid(getpid(), getpid());pid_t group1 = getpgid(getpid());pid_t group2;int i = 0;for(; i < 3; ++i){pid_t pid = fork();if(pid < 0){perror("fork error");exit(1);}else if(pid > 0){// parent processif(i == 0)setpgid(pid, group1);if(i == 1){setpgid(pid, pid);group2 = getpgid(pid);}if(i == 2)setpgid(pid, group2);}else{// child processif(i == 0)setpgid(getpid(), group1);if(i == 1){setpgid(getpid(), getpid());group2 = getpgid(getpid());}if(i == 2)setpgid(getpid(), group2);break;}}printf("pid:%d, ppid:%d, pgid:%d\n", getpid(), getppid(), getpgid(getpid()));for(int i = 0; i < 3; ++i)wait(0);return 0;
}
输出结果:
2. 实验二:
题目:利用进程扇完成一个小实验。该进程扇有 1 个父进程和 3 个子进程,我们希望达到图 1 中的效果,即将进程 0 (父进程)和进程 1 设置成一组,假设为组 1,将进程 2 和 进程 3 设置成另一个组,假设为组 2. 另外,我们希望进程 0 和进程 2 分别是这两个组的组长。
测试代码:
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>int main()
{int pid, i;int group1, group2;setpgid(getpid(), getpid());group1 = getpgid(getpid());for (i = 0; i < 3; ++i) {pid = fork();if (pid > 0) //父进程{if (i == 0) {setpgid(pid, pid);group2 = getpgid(pid);} else if (i == 1) {setpgid(pid, group1);} else if (i == 2){setpgid(pid, group2);} break;} else if (pid == 0) //子进程{if (i == 0) {setpgid(getpid(), getpid());group2 = getpgid(getpid());} else if (i == 1) {setpgid(getpid(), group1);} else if (i == 2) {setpgid(getpid(), group2);} } else if (pid < 0) {perror("fork");return -1; } }printf("进程 %d, pid: %d -> ppid: %d, pgid: [%d]\n", i, getpid(), getppid(), getpgid(getpid()));while(1) sleep(1);return 0;
}