守护进程为什么需要两次fork

        守护进程初始化过程其他都还是比较好理解,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.csdn.net/liu3daniel/article/details/73717548

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值