知识点
信号嵌套: 执行A信号的处理程序时, 又收到了A信号
那么linux是如何处理这种现象的呢? 我们先看一份测试代码:
测试代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
void sigttouhandler(int signum)
{
// 首先通过handler接收, 看收到的信号是哪个, 然后在里面设置父进程为前台进程组
int ppid = getpid();
printf("pid: %d SIGtou received %d.n", ppid, signum);
tcsetpgrp(0,ppid);
sleep(1);
return;
}
int main()
{
int cpid; /* 保存子进程的id号 */
int ppid; /* 保存父进程的id号 */
char buf[256];
char *prog1_argv[2];
prog1_argv[0] = "/usr/bin/vi"; /* 命令ls的参数表 */
prog1_argv[1] = NULL;
ppid = getpid(); //得到进程号
cpid = fork();
if (cpid < 0)
exit(-1);
if (!cpid)
{
fprintf(stdout, "ID(child)=%dn", getpid());
/* 使子进程所在的进程组成为前台进程组,然后执行vi */
setpgid(0, 0);
tcsetpgrp(0, getpid());
int returnvalue = 0;
returnvalue = execvp(prog1_argv[0], prog1_argv);
perror("exec vi");
exit(-1);
}
// signal(SIGTTOU, SIG_IGN);
fprintf(stdout, "ID(parent)=%dn", ppid);
setpgid(cpid, cpid); /* 设置进程组 */
tcsetpgrp(0, cpid); /* 设置控制终端为子进程拥有 */
waitpid(cpid, NULL, 0); /* 父进程等待子进程执行完毕,所在进程组成为前台进程组 */
signal(SIGTTOU, sigttouhandler);
tcsetpgrp(0, ppid);
//父进程等待终端输入,然后回显
while (1)
{
memset(buf, 0, 256);
fgets(buf, 256, stdin);
puts("ECHO: ");
puts(buf);
}
return 0;
}
代码逻辑
该程序首先将子进程所在进程组设为前台进程组, 然后打开vim. 子进程退出后父进程将SIGTTOU信号的handleler设为sigttouhandler, 并调用tcsetpgrp导致父进程收到SIGTTOU信号, 这样父进程就进入sigttouhandler, sigttouhandler内部再调用tcsetpgrp, 运行结果就是父进程变成前台进程,可以正常echo
提出问题
为什么在handler里面再调用tcsetpgrp不会导致父进程再收到SIGTTOU信号或者父进程挂起呢?
解决问题
通过阅读 6.4 信号 发现linux内核24版源代码分析大全linux内核24版源代码分析大全linux怎么读, 这个现象属于信号嵌套,即执行A信号的处理程序时linux环境配置, 又收到了A信号, linux对此的解决方法是自动屏蔽当前正在处理的信号, 使得对同一种信号的处理不会嵌套发生.
信号属于软中断, 而这种设计正是借鉴了在中断服务中关闭中断以防止嵌套的经验. 联想到Software Define Network, 也是通过软件方式实现一些原本实现在硬件上的功能.
相关知识点
tcsetpgrp(): 设置终端前台进程组
If tcsetpgrp() is called by a member of a background process group in its session, and
the calling process is not blocking or ignoring SIGTTOU, a SIGTTOU signal is sent to
all members of this background process group.
另外通过设置signal(SIGTTOU, SIG_IGN);可以更简单的实现和样例代码相同的功能.