Linux系统编程之守护进程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/puliao4167/article/details/86516256

守护进程简介及特点

    守护进程又称精灵进程(DAEMON),是一类具有如下特点的进程。

  1. 其生命周期很长,通常在系统启动的时候会创建并一直运行,当系统关闭时才终止,不随着用户登录、注销等改变其状态。
  2. 他是在后台运行且不拥有控制终端的进程。

守护进程创建过程及原因

    要把一个进程变成守护进程,要完成以下步骤:

  1. 执行一个fork,然后父进程退出,子进程接着运行。这样做就是为了让子进程变成孤儿进程,从而使其父进程为init进程,原因有两个:①子进程确保不会成为一个进程组的首进程,因为从父进程中继承来的进程组id和自己进程id不同(只有不是进程组的首进程才可以执行setsid())②若守护进程是从命令行启动的,当父进程终止被shell发现,shell就会显示出另一个shell提示符并让子进程继续在后台运行。
  2. 子进程调用setsid()开启一个新会话。setsid()做两个事情:①调用进程成为新会话的首进程和该会话中新进程组的首进程,调用进程的进程组id和会话id都会被设置为该进程的id。②调用进程没有控制终端且所有之前到控制终端的链接都会被断开
  3. 清除进程的umask,以确保守护进程创建文件和目录时候拥有所需的权限。
  4. 修改进程的当前工作目录,一般改为根目录。因为在系统关闭的时候,乳沟守护进程的当前工作目录为不包含/的文件系统,那么将无法卸载该文件系统
  5. 关闭守护进程从父进程中继承的所有打开的文件描述符。因为守护进程失去了控制终端且在后台运行,所以文件描述符0/1/2打开就没有意义了,是一种资源的浪费。
  6. 守护进程会打开/dev/null并使用dup2()使所有这些描述符指向这个设备。因为防止后续调用这些描述符时会发生错误。(/dev/null是一个虚拟设备,其会将写入数据丢弃,读取数据时返回结束的错误)

查看守护进程

    ps -ef | grep proc_name或ps aux可以查看所有进程

守护进程与后台进程的区别

    守护进程就是和终端完全没有关系,即使终端关闭或者用户切换,守护进程依旧运行,而后台进程只是终端进行的fork,依旧受到终端的控制,随着终端的终止而终止。

syslog

    由于守护进程已经脱离了终端的控制,所以无法像其他程序一样将消息输入到关联终端上,因此解决方法就是将消息写入到一个特定用于应用程序的日志文件中,可采用syslog工具,其是一个集中式的日志工具,系统的应用程序都可以利用这个工具记录日志消息。这个的用法详见TLPI

#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#define BD_NO_CHDIR 01
#define BD_NO_CLOSE_FILE 02
#define BD_NO_REOPEN_STD_FDS 04
#define BD_NO_UMASK 010
#define BD_MAX_CLOSE 8192
int becomeDaemon(int flag);

int becomeDaemon(int flags)
{
    int fd;
    switch(fork()){
    case -1: return -1;
    case 0: break;
    default: _exit(0);
    }

    if(setsid()==-1){
        return -1;
    }
    
    switch(fork()){
    case -1: return -1;
    case 0: break;
    default: _exit(0);
    }

    if(!(flags& BD_NO_UMASK))
    {
        umask(0);
    }

    if(!(flags& BD_NO_CHDIR))
    {
        chdir("/");
    }
    if(!(flags& BD_NO_CLOSE_FILE))
    {
        int maxfd=sysconf(_SC_OPEN_MAX);
        if(maxfd==-1){
            maxfd=BD_MAX_CLOSE;

        }
        
        for(fd=0;fd<maxfd;fd++){
            close(fd);
        }
    }

    if(!(flags& BD_NO_REOPEN_STD_FDS)){
        close(STDIN_FILENO);
        fd=open("/dev/null",O_RDWR);
        if(fd!=STDIN_FILENO){
            return -1;
        }
        if(dup2(STDIN_FILENO,STDOUT_FILENO)!=STDOUT_FILENO){
            return -1;
        }
        if(dup2(STDIN_FILENO,STDERR_FILENO)!=STDERR_FILENO){
            return -1;
        }
    }
    return 0;
}

int main(int argc, char const *argv[])
{
    becomeDaemon(0);
    sleep(20);
    return 0;
}

参考 《TLPI》《APUE》

猜你喜欢

转载自blog.csdn.net/puliao4167/article/details/86516256