守护进程(daemon)也叫精灵进程,它是运行在后台的与终端无关的一种特殊进程


#include <unistd.h>

pid_t setsid(void);


setsid()函数调用时,要保证当前进程不是进程组组长,否则出错返回-1

可以调用fork()做到这一点,fork出来的子进程和当前进程属于同一个进程组,而一个进程组组长是该进程组的第一个进程,所以保证fork出来的子进程一定不是进程组组长,接着调用setsid()函数


setsid()函数调用成功后,当前进程独自成一个会话(sid为当前进程id),且为进程组组长(gid也为进程id),如果当前进程有控制终端,则它会失去这个终端

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

void create_daemon()
{
    pid_t id=-1;
    struct sigaction sa; 
    sa.sa_handler=SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags=0;
    
    if(sigaction(SIGCHLD,&sa,NULL)<0){//注册子进程退出忽略信号
         return;
    }   
    
    if((id=fork())<0){//保证当前进程不是组长进程,否则下面setdid会失败
        perror("fork");
        exit(-1);
    }
    else if(id!=0){//父进程退出
        exit(0);
    }

    umask(0);
    if(-1==setsid()){//当前进程成为了守护进程,和终端无关
        perror("setsid");
        exit(1);
    }

    if(-1==(id=fork())){//保证进程不会再次打开终端
        perror("fork");
    }
    else if(0!=id){//父进程退出
        exit(0);
    }

    if(-1==chdir("/")){//把当前守护进程的工作目录改为根目录
        perror("chdir");
        exit(-1);
    }
        
    close(0);//关闭不必要的文件描述符

    int fd=-1;
    fd=open("/dev/null",O_RDWR);
    dup2(fd,1);
    dup2(fd,2);
}

int main()
{
    create_daemon();
    while(1){
        ;
    }

    return 0;
}

运行结果:

wKioL1c0GFezlLe3AAA6_lMYOac373.png

运行程序后用ps ajx | grep -E 'my_daemon'命令后有两个进程,其中第一行就是我们创建的守护进程

它的父进程为1号init进程,因为它的父进程退出后它成为了孤儿进程,被init进程收养

PGID和SID都为2816,而它的PID为2817,可看出它不是该会话的会话首进程,也不是它所属进程组的组长进程 

TTY列是?说明该进程和终端无关


ps ajx | head -n1 命令可查看头部信息