守护进程初始化过程其他都还是比较好理解,fork两次这儿很不直观。这一节用实际代码示例说明,终端对进程的影响以及fork两次+setsid的确获取不了终端了。
1.关闭terminal发送sighup给该终端对应所有会话的所有进程;
void action(int num,siginfo_t *psi,void *p){
//收到之后信息写入文件
int fd = open("log",O_RDWR | O_APPEND | O_CREAT,S_IRWXU);
string out("received SIGUP from ");
//打印发信号进程id和本进程id
out += to_string(psi->si_pid) + ",pid:" + to_string(getpid()) + "\n";
write(fd,out.c_str(),out.length());
exit(0);
}
int main(){
struct sigaction act;
memset(&act,0,sizeof(struct sigaction));
act.sa_sigaction = action;
act.sa_flags = SA_SIGINFO;
//安装SIGHUP处理函数,fork之前安装,父子进程都生效
sigaction(SIGHUP,&act,NULL);
if(fork() != 0){
printf("parent session id:%u,pid:%u,pgid:%u\n",getsid(0),getpid(),getpgid(0));
sleep(SLEEPTIME);
exit(0);
}
printf("child session id before:%u,pid:%u,pgid:%u\n",getsid(0),getpid(),getpgid(0));
//printf("setting a new session:%d\n",setsid());
//printf("child session id after:%u,pid:%u,pgid:%u\n",getsid(0),getpid(),getpgid(0));
sleep(SLEEPTIME);
}
编译后运行,父进程和子进程都会sleep等待,这时关闭终端(securecrt右上角小红叉)观察日志。此时,父子进程都收到来自29247的SIGHUP信号,29247对应该运行该a.out的bash。
received SIGHUP from 29247,pid:2516
received SIGHUP from 29247,pid:2517
在此之前从另一个终端ps aux |grep out:
ubuntu 2516 0.0 0.0 12540 820 pts/4 S+ 15:34 0:00 ./a.out
ubuntu 2517 0.0 0.0 12536 160 pts/4 S+ 15:34 0:00 ./a.out
ps axj |grep 29247:
29246 29247 29247 29247 pts/10 29247 Ss+ 500 0:00 -bash
上述事实说明关闭终端,终端对应bash会发送SIGHUP信号给本终端关联的进程,无论是直属子进程还是子进程的子进程。至于底层点的initd\rlogin\tty\ptm\pts\bash的恩怨情仇这儿不管。
2.setsid从本终端脱离后不会收到SIGHUP
上述代码中注释的两行取消注释后:
child session id before:22481,pid:3469,pgid:3468
setting a new session:3469
child session id after:3469,pid:3469,pgid:3469
22481 3468 3468 22481 pts/2 3468 S+ 500 0:00 ./a.out //父进程终端还是pts/2
3468 3469 3469 3469 ? -1 Ss 500 0:00 ./a.out //子进程终端没了,显示?
关闭终端之后子进程不退出。
3.setsid之后子进程作死还是能获得新的终端
把上面代码最后一个sleep替换成下面代码。
int mfd = posix_openpt(O_RDWR);
if(mfd < 0)
perror("open pts error:");
printf("open pts done\n");
if(grantpt(mfd) == -1)
perror("grantpt pts error:");
printf("grantpt pts done\n");
if(unlockpt(mfd) == -1)
perror("unlockpt pts error:");
char *name = ptsname(mfd);
printf("device name is:%s\n",name);
int sfd = open(name,O_RDWR);
dup2(sfd,STDIN_FILENO);
dup2(sfd,STDOUT_FILENO);
dup2(sfd,STDERR_FILENO);
if(ioctl(sfd,TIOCSCTTY,NULL) == -1)
perror("ioctl error:");
sleep(SLEEPTIME);
close(sfd);
close(mfd);
ps aux|grep out之后得到:
13235 18358 18358 13235 pts/2 18358 S+ 500 0:00 ./a.out
18358 18359 18359 18359 pts/6 18359 Ss+ 500 0:00 ./a.out
可见子进程已经获得了新终端。
4.setsid之后再次fork获得新终端失败
再次fork可以使得子进程不是当前会话的首进程,从而无法获得新终端:
ubuntu 4478 0.0 0.0 12540 820 pts/2 S+ 14:55 0:00 ./a.out
ubuntu 4479 0.0 0.0 0 0 ? Zs 14:55 0:00 [a.out] <defunct>
ubuntu 4480 0.0 0.0 21100 816 ? S 14:55 0:00 ./a.out
可见,孙进程设置终端失败(显示?)。
原文链接:https://blog.youkuaiyun.com/liu3daniel/article/details/73717548