如何编写一个守护进程daemon

大体步骤如下

1.   调用fork()函数创建子进程后,让父进程立即exit(),这样产生的子进程变成孤儿进程,由init进程接管。

2.   调用setsid()函数,使得新创建的进程脱离控制终端,同时创建新的进程组,并成为该进程组的首进程。在linux系统中,所有的进程都属于各自的进程组,进程组是一个或多个进程的集合,一个进程组中至少有一个进程成员,否则就消亡了。每个进程组都有一个进程组ID,是由领头进程的进程号决定的,会话则是一个或多个进程组的集合,每个会话都有一个领头进程,会话和进程组是linux内核用于管理多用户情况下用户进程的方法,每个进程都属于一个进程组,而进程组又属于某个会话,当用户从终端登录系统时,系统会创建一个新的会话,在该终端上启动的进程都会被系统划归到会话的进程组中。会话中的进程通过该会话中的领头进程与一个终端相连,该终端是会话的控制终端,一个会话只能有一个控制终端,如果会话存在一个控制终端时,则它必然拥有一个前台进程组,属于该组的进程可以从从控制终端获得输入。由于守护进程没有控制终端,而使用fork()函数创建的子进程会继承父进程的控制终端,会话和进程组,因此,必须用setsid()创建新的会话,以脱离父进程的影响。Setsid函数将创建新的会话,并使得调用setsid函数的进程成为新会话的领头进程。调用setsid函数的进程是新创建会话中的唯一的进程组,进程组ID为调用进程的进程号。Setsid函数产生这一结果还有个条件,即调用进程不为一个进程的领头进程。由于在第一步调用fork的父进程退出,使得子进程不可能是进程组的领头进程。该会话的领头进程没有控制终端与其相连,至此,满足了守护进程没有控制终端的要求

3.   更改当前工作目录

使用fork()函数创建的子进程会继承父进程的当前工作目录,当进程工作没有结束时,其工作目录是不能被卸载的.为了防止此问题,守护进程一般要chdir()了数将其工作目录更改到别的目录下(一般为/根目录,因为根目录是永远不会被卸载的,除非关机).

4.   关闭文件描述符,并重定向标准输入,输出和错误

子进程会继承父进程某些打开了的文件描述符,如果不使用这些文件描述符,则需要关闭它们.守护进程是运行在系统后台的,不应该在终端有任何的输出信息,可以使用dup()函数将其重定向到/dev/null空设备上.

5.   设置守护进程的文件权限创建掩码

守护进程会创建一些临时文件,出于性的考虑,往往不希望这些文件被别的用户查看,这时可以用umask()函数修改文件权限,创建掩码的取值.

上面说的是创建守护进程的大体步骤,解释其中的两点:

1、setsid的作用

一、让进程摆脱原会话的控制

二. 让进程摆脱原进程组的控制

三. 让进程摆脱原控制终端的控制

setsid()就是将进程和它当前的对话过程和进程组分离开,并且把它设置成一个新的对话过程的领头进程。也就是说让调用setsid()函数的这个进程脱离原有的进程组,自立门户。同时需要注意一点就是,调用这个函数的进程不能是一个进程组的进程组长,不然进程组长自立门户一户,这个进程组的其他进程也就会消亡(这个和平时自己创建父子进程,然后父子进程各自做各自的事情还不太一样)。这也就解释了上面红色字体的那句话。在子进程中调用setsid,不管这个时候父进程退出与否,子进程都不会是它当前所在进程组的进程组长。所以调用这个函数的是子进程,下面这个例子就是在父进程中调用setsid的副作用,会使父进程所在的进程组的其他子进程销毁

pid_t pid = fork();

if (pid == 0) {

...

int result = execl(path, "adb", "fork-server", "server", NULL);

} else {

// run a program in a new session

setsid();//之前parent和child运行在同一个session里,而且parent是session头,

//所以作为session头的parent如果exit结束执行的话,那么会话session组中的所有进程将都被杀死;

//所以执行setsid()之后,parent将重新获得一个新的会话session组id,child将仍持有原有的会话session组,

//这时parent退出之后,将不会影响到child了。

}

会话session是一个或多个进程组的集合。进程调用setsid函数建立一个新会话。如果调用此函数的进程不是一个进程组的组长,则此函数就会创建一个新会话,该进程变成会话的首进程,然后该进程成为一个新进程组的组长进程,该进程没有控制终端。因为会话首进程是具有唯一进程ID的单个进程,所以可以将会话首进程的进程ID视为会话Id。

2、其实创建daemon进程是调用fork()两次的。

一、如果开启daemon进程的父进程本身可能不单单是为了创建daemon,假如父进程在某个地方一直阻塞,这时守护进程一直在运行,父进程却没有正常退出。如果守护进程因为正常或非正常原因退出了,就会变成ZOMBIE进程。

如果fork两次呢?父进程先fork出一个儿子进程,儿子进程再fork出孙子进程做为守护进程,然后儿子进程立刻退出,守护进程被init进程接管,这样无论父进程做什么事,无论怎么被阻塞,都与守护进程无关了。所以,fork两次的守护进程很安全,避免了僵尸进程出现的可能性。

二、第一个子进程调用setsid()函数,调用以后第一个子进程才成为了新的进程组的进程组长(注意,调用setsid()函数的第一个子进程在调用这个函数之前不是进程组的进程组长)第一子进程成为新的会话组长和进程组长进程组长有权利申请打开一个控制终端,第二次fork的意义就在此,关闭掉第一子进程,使第二子进程成为守护进程,并且因为没有进程组长,  所以守护进程不会被关闭。

下面是个代码示例:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <time.h>
    #include <syslog.h>
    #include <signal.h>
    #include <sys/param.h>
    #include <fcntl.h>
    int init_daemon(void)
    {
        int pid;
        int i;
        int fd;
        /*忽略终端I/O信号,STOP信号*/
        signal (SIGTTOU, SIG_IGN);
        signal (SIGTTIN, SIG_IGN);
        signal (SIGTSTP, SIG_IGN);
        signal (SIGHUP, SIG_IGN);
        printf(“ppid = %d\n”,getppid());
        pid = fork();
        fd = open(“/dev/tty”,O_RDONLY);
        printf(“!fd = %d\n”,fd);
        close(fd);
        if (pid > 0)
        {
            printf(“Parent process pid = %d\n”,getpid());
            exit(0);  //结束父进程,使得子进程成为后台进程
        }
        else if (pid < 0)
            return -1;
        printf(“First Child process pid = %d\n”,getpid());
        //当前进程为第一子进程
        //建立一个新的进程组,在这个新的进程组中,子进程成为这个进程组的首进程,以使该进程脱离所有终端
        printf(“pgid = %d\n”,getpgid(getpid()));
        //printf(“pid = %d\n”,getpid());
        setsid();
        printf(“pgid = %d\n”,getpgid(getpid()));
        //printf(“pid = %d\n”,getpid());
        fd = open(“/dev/tty”,O_RDONLY);
        printf(“fd = %d\n”,fd);
        close(fd);
        //再次新建一个子进程,退出父进程(第一子进程),保证该进程不是进程组长,同时让该进程无法再打开一个新的终端
        pid = fork();
        fd = open(“/dev/tty”,O_RDONLY);
        printf(“#fd = %d\n”,fd);
        close(fd);
        if (pid > 0)
        {
            printf(“First Child process pid = %d\n”,getpid());
            exit(0);
        }
        else if (pid < 0)
          return -1;
        //关闭所有从父进程继承的不再需要的文件描述符
        for (i=0; i < NOFILE; close(i++));
        //改变工作目录,使得进程不与任何文件系统联系
        chdir(“/”);
        //将文件屏蔽字设置为0
        umask(0);
        //忽略SIGCHLD信号
        signal(SIGCHLD,SIG_IGN);
        return 0;
    }
    int main(int argc, char *argv[])
    {
        time_t now;
        init_daemon();
        syslog(LOG_USER|LOG_INFO,”测试守护进程!\n”);
        while(1)
        {
            sleep(8);
            time(&now);
            syslog(LOG_USER|LOG_INFO,”系统时间:\t%s\t\t\n”,ctime(&now));
        }
    }
时间: 2024-10-11 13:37:21

如何编写一个守护进程daemon的相关文章

【Linux学习】编写一个守护进程

题目: 编写一个守护进程,每隔3秒钟将当前时间输出.要求: 不能使用init_daemon系统调用. #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <time.h> #include <unistd.h> int main(void) { pid_t pid; pid = fork(); if(pid > 0) { exit(0); } if(0 ==

简单实现一个守护进程(Daemon)

学习进程就不得不提到一个运行在后台的特殊进程--守护进程.(也称精灵进程).Linux系统启动时会启动很多系统服务进程,这些系统服务进程没有控制终端,不能直接和用户交互.其它进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止,但系统服务进程不受用户登录注销的影响,它们一直在运行着而我们就将这种进程称为守护进程. 守护进程独立于控制终端并周期性的执行某种任务或者等待处理某些发生的事件.守护进程是一种很有用的进程.Linux的大多数服务器就是用1守护进程实现的,比如,Internet服务

编写一个守护进程

编写守护进程程序时需遵循一些基本规则,以防止产生不必要的交互作用,下面先说明这些规则,然后给出一个按照这些规则编写的函数deamonize. (1)首先,调用umask将文件创建屏蔽字设置为一个已知值(通常为0). (2)调用fork,然后是父进程exit. (3)调用setsid创建一个新会话. (4)将当前工作目录更改为根目录. (5)关闭不需要的文件描述符. (6)将文件描述符0,1,2指向 /dev/null 以下是一个记录日志的守护进程. 1 #include "apue.h"

python 守护进程(daemon)

#!/usr/bin/env python # -*- coding:utf-8 -*- import sys, os '''将当前进程fork为一个守护进程    注意:如果你的守护进程是由inetd启动的,不要这样做!inetd完成了    所有需要做的事情,包括重定向标准文件描述符,需要做的事情只有chdir()和umask()了 ''' def daemon(stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):     #

创建一个守护进程

一.概述:守护进程也称精灵进程(Daemon),是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.守护进程是随系统启动而存在,随系统关闭而消失的进程(也就是开机之后就会存在,关机才消失).守护进程是一种很有用的进程,Linux的大多数服务器就是用守护进程实现的. 在终端运行命令:ps axj | head 参数a表示不仅列出当前用户进程,也列出所有其它用户进程. 参数x表示不仅列出有控制终端的进程,也列出所有无控制终端的进程. 参数j表示列出与作业控制

小何讲进程: 编写Linux守护进程方法详解

守护进程概述 守护进程,也就是通常所说的Daemon进程,是Linux中的后台服务进程. 它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些事件的发生. 守护进程常常在系统引导载入时启动,在系统关闭时终止. Linux有很多系统服务,大多数服务都是通过守护进程实现的.守护进程的名字通常以d结尾,字母d就是Daemon的意思. 由于在Linux中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端

从时间进程日志上面了解守护进程daemon的使用

说起进程呢? 在俺心中永远有种高大上的感觉,哈哈,自从了解到了进程方面的东西,也觉得进程这个执行过程真的很神奇,但是在了解到了还有守护进程方面的东东的时候又觉得貌似更加的让俺魂不守舍啦!觉得自己可以做到  觉得从一名屌丝立马逆袭成了高大上啦.O(∩_∩)O哈哈~ 那么,进程守护具体是什么呢?相信看这篇文章的牛牛们都对这方面有一定的了解啦.所以就带着复习或者巩固或者挑刺的心理来看看吧. 上面就是大致的创建守护进程的步骤啦,用语言来描述的话,共需要5步即可建立一个守护进程: 1>用fork()函数创

一个守护进程实例

void DaemonInit(void){ //LOG::INF("[ServerMeeting] 服务初始化.\n"); int pid; //如果是父进程,结束父进程,子进程继续 if(pid=fork()) { exit(0); } /*else if(pid<0) // 不可能进入 { exit(1); //fork失败,退出 }*/ //是第一子进程,后台继续执行 setsid();//第一子进程成为新的会话组长和进程组长 //并与控制终端分离 if(pid=fork

python实现的守护进程(Daemon)的代码

将开发过程经常用的一些代码段记录起来,下边代码段是关于python实现的守护进程(Daemon)的代码,希望对小伙伴有一些用. def createDaemon(): "'Funzione che crea un demone per eseguire un determinato programma-"' import os # create - fork 1 try: if os.fork() > 0: os._exit(0) # exit father- except OS