在上一节,我们仅仅对进程组做了一个简单的介绍,以及两个相关的函数 getpgid
和 setpgid
,不知道你有没有完成最后那个练习。如果你遇到困难,可以在文章后面留言,或者加入讨论群 610441700。
1. 目标
本篇希望利用进程扇完成一个小实验。该进程扇有 1 个父进程和 3 个子进程,我们希望达到图 1 中的效果,即将进程 0 (父进程)和进程 1 设置成一组,假设为组 1,将进程 2 和 进程 3 设置成另一个组,假设为组 2. 另外,我们希望进程 0 和进程 2 分别是这两个组的组长。
图 1 将进程扇划分成两组
2. 思路
- 首先,利用前面的知识,创建包含 4 个进程的进程扇。
- 创建进程组 1,即将进程 0 设置为组长;再将进程 1 加入组 1.
- 创建进程组 2,即将进程 2 设置为组长;再将进程 3 加入组 2.
创建进程组和将某个进程划分到一个进程组在上节已经讲过,这里不在赘述。
3. 程序清单
实际上,3.1 中的代码存在着一点小 bug,不过本篇并不打算给出完美的答案,所以这将作为一个练习留给读者。
3.1 代码
// ps_swing_v1.c
#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;
}
3.2 编译与运行
- 编译并运行
$ gcc ps_swing_v1.c -o ps_swing_v1
$ ./ps_swing_v1
- 运行结果
上面运行了两次程序,但是结果都不一样,最后并没有达到我们预期的结果,都以失败而告终。
不过,我们确实可以看到有两个组,但是每次运行,都有一个进程设置失败了,想想是为什么?
4. 总结
- 掌握创建和设置进程组的方法
- 哪个进程被优先调度是不确定的
练习:修复第 3 节程序中的 bug。