如果一个会话有控制终端,那么该会话就有一个前台进程组。此时在终端中的输入以及终端产生的信号默认都发送给前台进程组中的所有进程。
前台进程组是可以通过程序进行设置的。在同一个会话中,调用 tcsetpgrp 函数可以设置某个进程组为前台进程组。
1. 目标
任务1: 在 bash 中启动你的进程,然后将前台进程组设置为 bash 所在的进程组。
任务2:关闭启动你进程的终端,查看你的进程能收到什么信号。
2. 思路
在任务 1 中,首先获取 bash 进程的进程组 id,而 bash 进程又是进程组组长,所以它的进程组 id 就相当于 bash 进程 id.
任务 2 中,你的进程可以收到 SIGHUP 信号,所以捕获些信号即可(有关信号的知识请参考专题五)。
3. 程序清单
- 代码
// ct.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void handler(int sig) {
if (sig == SIGHUP) {
int fd = open("tmp", O_WRONLY | O_CREAT, 0664);
write(fd, "hello SIGHUP\n", 32);
exit(0);
}
}
void print() {
pid_t pid, sid, pgid, tpgid;
pid = getpid();
sid = getsid(pid);
pgid = getpgid(pid);
tpgid = tcgetpgrp(0);
if (tpgid < 0) {
perror("tcgetpgrp");
}
printf("pid = %d, pgid = %d, sid = %d, tpgid = %d\n", pid, pgid, sid, tpgid);
}
int main(int argc, char* argv[]) {
signal(SIGHUP, handler);
// 打印当前进程 id,组 id,会话 id,当前会话中的前台进程组 id.
print();
// 将前台进程组设置为 bash 进程组的 id.
tcsetpgrp(0, getppid());
print();
while(1) sleep(1);
return 0;
}
- 编译和运行
$ gcc ct.c -o ct
$ ./ct
- 运行结果
图1 运行结果
启动你的程序后,无论键入 CTRL C、CTRL \ 还是 CTRL Z 都没反应了。程序第一行打印的是未设置前台进程组前的结果,第二行打印是设置后的结果。可以看到设置后的前台进程组 id = 2816.
直接关闭终端,当前文件夹下会生成 tmp 文件,内容就是在信号处理函数里定稿的数据,如图2。
图2 关闭终端,进程收到 SIGHUP 信号
4. 总结
- 掌握如何设置前台进程组
- 当直接关闭控制终端后,进程会收到 SIGHUP 信号
练习:使用 kill 将启动 ct 程序的 bash 进程杀死,看看有什么效果。(提示:ct 进程并没有结束,因为没有收到 SIGHUP 信号)