setsid & 后台进程

当进程是会话的领头进程时setsid()调用失败并返回(-1)。
setsid()调用成功后,返回新的会话的ID,调用setsid函数的进程成为新的会话的领头进程,并与其父进程的会话组和进程组脱离。
由于会话对控制终端的独占性,进程同时与控制终端脱离。



一,守护进程概述
Linux Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括系统日志进程syslogd、 web服务器httpd、邮件服务器sendmail和数据库服务器mysqld等。



守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机都保持运行。守护进程经常以超级用户(root)权限运行,因为它们要使用特殊的端口(1-1024)或访问某些特殊的资源。



一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出,无论是向标准输出设备stdout还是标准出错设备stderr的输出都需要特殊处理。



守护进程的名称通常以d结尾,比如sshd、xinetd、crond等



二,创建守护进程步骤
首先我们要了解一些基本概念:



进程组 :



每个进程也属于一个进程组
每个进程主都有一个进程组号,该号等于该进程组组长的PID号 .
一个进程只能为它自己或子进程设置进程组ID号
会话期:



会话期(session)是一个或多个进程组的集合。



setsid()函数可以建立一个对话期:



如果,调用setsid的进程不是一个进程组的组长,此函数创建一个新的会话期。



(1)此进程变成该对话期的首进程



(2)此进程变成一个新进程组的组长进程。



(3)此进程没有控制终端,如果在调用setsid前,该进程有控制终端,那么与该终端的联系被解除。 如果该进程是一个进程组的组长,此函数返回错误。



(4)为了保证这一点,我们先调用fork()然后exit(),此时只有子进程在运行



现在我们来给出创建守护进程所需步骤:



编写守护进程的一般步骤步骤:



(1)在父进程中执行fork并exit推出;



(2)在子进程中调用setsid函数创建新的会话;



(3)在子进程中调用chdir函数,让根目录 ”/” 成为子进程的工作目录;



(4)在子进程中调用umask函数,设置进程的umask为0;



(5)在子进程中关闭任何不需要的文件描述符



说明:




  1. 在后台运行。
    为避免挂起控制终端将Daemon放入后台执行。方法是在进程中调用fork使父进程终止,让Daemon在子进程中后台执行。
    if(pid=fork())
    exit(0);//是父进程,结束父进程,子进程继续

  2. 脱离控制终端,登录会话和进程组
    有必要先介绍一下Linux中的进程与控制终端,登录会话和进程组之间的关系:进程属于一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。
    控制终端,登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们,使之不受它们的影响。方法是在第1点的基础上,调用setsid()使进程成为会话组长:
    setsid();
    说明:当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。

  3. 禁止进程重新打开控制终端
    现在,进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端:
    if(pid=fork())
    exit(0);//结束第一子进程,第二子进程继续(第二子进程不再是会话组长)

  4. 关闭打开的文件描述符
    进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们:
    for(i=0;i 关闭打开的文件描述符close(i);>

  5. 改变当前工作目录
    进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如/tmpchdir(“/”)

  6. 重设文件创建掩模
    进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除:umask(0);

  7. 处理SIGCHLD信号
    处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将SIGCHLD信号的操作设为SIG_IGN。
    signal(SIGCHLD,SIG_IGN);
    这样,内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程。



deamon进程的标准实行,关闭了标准输入输出后,重新打开了/dev/null,就是黑洞,然后dup(0),dup(0),相当于将fd=0,1,2,都指向了黑洞



if (fork() != 0) {
exit(0);
}
setsid();
if (fork() != 0) {
exit(0);
}



close(0);
close(1);
close(2);
open(“/dev/null”, O_RDWR);
dup(0);
dup(0);
return true;
}

#include <sys/file.h>  
#include   
#include   
#include   
#include <sys/file.h>  
                                          
int main (int argc, char ** argv)  
{  
     if ( fork() > 0 ) {  
          printf ( "parent begin\n" ) ;  
          sleep(10);  
            
        printf ( "parent exit\n" ) ;  
        exit ( 0 ) ;  
     }  
       
     printf ( "child begin\n" ) ;  
     sleep(100);  
       
     printf ( "child exit\n" ) ;  
     return 0 ;  
}  
打开另外一个终端窗口,查看进程存在,testSetSid同在一个进程组:
关闭运行testSetSid的终端运行窗口,再查看进程,进程组退出,父子进程都已不在:



说明:父子进程都受终端影响,终端退出,父子进程也会退出
如果我们在子进程开始时调用setsid():
关闭运行testSetSid的终端窗口,查看进程,父进程退出,子进程继续存在:



setsid [options] program [arguments]
可以写一个脚本然后在后台运行
setsid ./shell.sh



open(“/dev/null”,o_rdwr)作用
 open(”/dev/null”,..)



很多程序在一开始的时候要做这样的处理
/* 



  • make sure we have stdin/stdout/stderr reserved
    */



while((t = open(“/dev/null”,O_RDWR)) < 3 && T >= 0)
if(t >= 3) close(t)



写入/dev/null的东西会被系统丢掉
就像注释写的那样,对stdin/stdout/stderr进行保护
代码中利用while先把文件描述符0,1,2分配出去,以后再分配
的时候就不会将stdin/stdout/stderr打开,以达到保护目的。



当用户注销(logout)或者网络断开时,终端会收到 HUP(hangup)信号从而关闭其所有子进程。因此,我们的解决办法就有两种途径:要么让进程忽略 HUP 信号,要么让进程运行在新的会话里从而成为不属于此终端的子进程。



(hangup 名称的来由:在 Unix 的早期版本中,每个终端都会通过 modem 和系统通讯。当用户 logout 时,modem 就会挂断(hang up)电话。 同理,当 modem 断开连接时,就会给终端发送 hangup 信号来通知其关闭所有子进程)



1.nohup:让提交的命令忽略 hangup 信号
使用:在要处理的命令前加上 nohup 即可,标准输出和标准错误缺省会被重定向到 nohup.out 文件中。一般我们可在结尾加上”&”来将命令同时放入后台运行,也可用”> filename”来更改缺省的重定向文件路径。
2.setsid:在新session执行命令(使进程不属于接受 HUP 信号的终端的子进程,那么就不会受到 HUP 信号的影响)



Category linux