终端会话和孤儿进程组(POSIX-2.2.2.52)--引出问题

希望shell和一个程序都同时接收一个ctrl-c,最好的办法就是将它们设置到一个进程组当中,并且把这个进程组设置为终端上的前台进程组,如下所示,其中2774是该终端上bash进程的pid:
void handler(int n)
{
printf("GOT SIGINT/n");
}
int main()
{
setpgid(0, 2774); //将test和bash设置成同一个进程组
signal(SIGINT, handler);
while (1) {
sleep(1);
//kill(0, SIGINT); //先注释掉
}
}
编译为test,在运行test之前先设置一下bash的trap,使得bash也能捕捉到SIGINT信号:
trap 'echo got sigint' INT
希望的情况是,运行test后按下ctrl-c,屏幕上显示GOT SIGINT和got sigint,可是一个也没有显示,为何?
这就涉及到了控制终端,进程组等概念了。bash没有打印got sigint是因为当bash调用fork-exec启动test的时候,已经调用了ioctl(fd, TIOCSPGRP, &子进程pid);而ioctl会将子进程即test的pid设置成tty的pgrp字段,这个事实通过两种方式可以了解到,第一可以通过strace命令,其次可以通过看一下bash和linux内核的源码,内核在接收到ctrl-c的时候会将此键码作为普通字符放入终端的接收缓冲中去,就是调用终端行规程的receive_buf。然后receive_buf最终发现ctrl-c控制字符后会调用isig函数,后者继续调用kill_pg(tty->pgrp, sig, 1);最终tty->pgrp决定了要发送信号给谁。bash既然已经将tty的pgrp改成了test的pid,而test又把自己交给了bash,在调用setpgid的时候,test会将自己从pgid表上detach,因此for_each_task_pid(tty->pgrp, PIDTYPE_PGID, p, l, pid)将一个进程也找不到,所以就谁也收不到了。
现在放开kill代码行的注释,再次编译运行,自己发送信号,GOT SIGINT成功显示了,可是bash的got sigint却没有显示,这是因为bash在wait子进程的结束,没有打印got sigint是因为此时终端被前台的test独占了,SIGINT其实已经pendding到bash了,不信的话将test结束掉,看看got sigint是不是打印出来了。既然是bash调用ioctl将tty的pgrp给改了,那么改回来是不是就可以了呢?倒是可以试一下,重新注释掉kill行并且添加下面的代码:
tcsetpgrp(fd, 2774);
或者下面的:
ioctl(fd, TIOCSPGRP, &id);
结果连调用都不成功...为何bash就可以将终端的的pgrp设置成别人的而test就不行呢?难道bash是什么特权进程吗?unix/linux中是没有特权进程的,所以还要从test本身来找原因,毕竟它非常短小。我们看到程序一开始有个setpgid的调用,这里面有点说法。内核是不负责设置pgid或者session的,这些都是bash的事情,内核函数copy_process中可以看出,只要不是创建线程,内核会将所有的进程都设置到一个进程组中,然而事实上系统的众多进程却不是属于同一个进程组,这就是bash在中间做了点事情,bash在启动新进程时会调用setpgid将新进程的pgid设置成其pid,因此所有的子进程都单独占有一个进程组,关键问题就是为何调用了setpgid之后就不能能再设置终端的pgrp了呢?这里面的原因在于POSIX的一个规定,那就是不能将孤儿进程组设置成tty的前台进程组,所谓的孤儿进程组就是组内的所有成员要么自己和父亲在同一个组,这样的话受影响是一致的,要么就是自己和父亲属于不同的session,如此一来终端信号将在父子之间隔离,而我们的test中经过第一行那么一设置之后,pgrp组就成了孤儿进程组,里面一共就两个进程,一个bash,一个test,因此后面的TIOCSPGRP调用将不会成功。
最后,如果非要实现ctrl-c影响两个进程的话,办法是有的,那就是执行test &,后面要加一个&号,bash在执行这一类进程的时候将不会调用TIOCSPGRP的ioctl让子进程独占终端,相反bash会让自己占据终端,这样执行后按下ctrl-c,将同时输出got sigint和GOT SIGINT

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值