linux系统编程之进程(八):守护进程详解及建立,daemon()使用

一,守护进程概述

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

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

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

守护进程的名称一般以d结尾,好比sshd、xinetd、crond等数据库

二,建立守护进程步骤

首先咱们要了解一些基本概念:服务器

进程组 :session

  • 每一个进程也属于一个进程组
  • 每一个进程主都有一个进程组号,该号等于该进程组组长的PID号 .
  • 一个进程只能为它本身或子进程设置进程组ID号

会话期: 并发

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

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下必须显式等待子进程结束才能释放僵尸进程。

三,建立守护进程

在建立以前咱们先了解setsid()使用:

  #include <unistd.h>

       pid_t setsid(void);

DESCRIPTION
       setsid()  creates a new session if the calling process is not a process
       group leader
The calling process is the leader of  the  new  session,
       the  process group leader of the new process group, and has no control-
       ling tty
The process group ID and session ID of the  calling  process
       are set to the PID of the calling process
The calling process will be
       the only process in this new process group and in this new session
.

//调用进程必须是非当前进程组组长,调用后,产生一个新的会话期,且该会话期中只有一个进程组,且该进程组组长为调用进程,没有控制终端,新产生的group ID 和 session ID 被设置成调用进程的PID

RETURN VALUE
       On success, the (new) session ID of the calling  process  is  returned.
       On  error,  (pid_t) -1  is  returned,  and errno is set to indicate the
       error.

如今根据上述步骤建立一个守护进程:

如下程序是建立一个守护进程,而后利用这个守护进程每一个一分钟向daemon.log文件中写入当前时间

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>

#define ERR_EXIT(m) \
do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}\
while (0);\

void creat_daemon(void);
int main(void)
{
    time_t t;
    int fd;
    creat_daemon();
    while(1){
        fd = open("daemon.log",O_WRONLY|O_CREAT|O_APPEND,0644);
        if(fd == -1)
            ERR_EXIT("open error");
        t = time(0);
        char *buf = asctime(localtime(&t));
        write(fd,buf,strlen(buf));
        close(fd);
        sleep(60);
            
    }
    return 0;
}
void creat_daemon(void)
{
    pid_t pid;
    pid = fork();
    if( pid == -1)
        ERR_EXIT("fork error");
    if(pid > 0 )
        exit(EXIT_SUCCESS);
    if(setsid() == -1)
        ERR_EXIT("SETSID ERROR");
    chdir("/");
    int i;
    for( i = 0; i < 3; ++i)
    {
        close(i);
        open("/dev/null", O_RDWR);
        dup(0);
        dup(0);
    }
    umask(0);
    return;
}

结果:

QQ截图20130713184143

结果显示:当我一普通用户执行a.out时,进程表中并无出现新建立的守护进程,但当我以root用户执行时,成功了,并在/目录下建立了daemon.log文件,cat查看后确实每一个一分钟写入一次。为何只能root执行,那是由于当咱们建立守护进程时,已经将当前目录切换我/目录,因此当我以后建立daemon.log文件是实际上是在/目录下,那确定不行,由于普通用户没有权限,或许你会问那为啥没报错呢?实际上是有出错,只不过咱们在建立守护进程时已经将标准输入关闭并重定向到/dev/null,因此看不到错误信息。

四,利用库函数daemon()建立守护进程

其实咱们彻底能够利用daemon()函数建立守护进程,其函数原型:

#include <unistd.h>

int daemon(int nochdir, int noclose);


DESCRIPTION
       The daemon() function is for programs wishing to detach themselves from
       the controlling terminal and run in the background as system daemons.

       If nochdir is zero, daemon()  changes  the  process’s  current  working
       directory to the root directory ("/"); otherwise,

       If  noclose is zero, daemon() redirects standard input, standard output
       and standard error to /dev/null; otherwise,  no  changes  are  made  to
       these file descriptors.

功能:建立一个守护进程

参数:

nochdir:=0将当前目录更改至“/”

noclose:=0将标准输入、标准输出、标准错误重定向至“/dev/null”

返回值:

成功:0

失败:-1

如今咱们利用daemon()改写刚才那个程序:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>

#define ERR_EXIT(m) \
do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}\
while (0);\

void creat_daemon(void);
int main(void)
{
    time_t t;
    int fd;
    if(daemon(0,0) == -1)
        ERR_EXIT("daemon error");
    while(1){
        fd = open("daemon.log",O_WRONLY|O_CREAT|O_APPEND,0644);
        if(fd == -1)
            ERR_EXIT("open error");
        t = time(0);
        char *buf = asctime(localtime(&t));
        write(fd,buf,strlen(buf));
        close(fd);
        sleep(60);
            
    }
    return 0;
}

当daemon(0,0)时:

QQ截图20130713190523

结果同刚才同样,也是只有root才能成功,普通用户执行时看不到错误信息

如今让daemon(0,1),就是不关闭标准输入输出结果:

QQ截图20130713190932

能够看到错误信息

如今让daemon(1,0),就是不重定向,结果以下:

QQ截图20130713191221

此次普通用户执行成功了,觉得没有切换到/目录下,有权限

其实咱们能够利用咱们刚才写的建立守护进程程序默认daemon()实现:

代码以下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>

#define ERR_EXIT(m) \
do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}\
while (0);\

void creat_daemon(int nochdir, int noclose);
int main(void)
{
    time_t t;
    int fd;
    creat_daemon(0,0);
    while(1){
        fd = open("daemon.log",O_WRONLY|O_CREAT|O_APPEND,0644);
        if(fd == -1)
            ERR_EXIT("open error");
        t = time(0);
        char *buf = asctime(localtime(&t));
        write(fd,buf,strlen(buf));
        close(fd);
        sleep(60);
            
    }
    return 0;
}
void creat_daemon(int nochdir, int noclose)
{
    pid_t pid;
    pid = fork();
    if( pid == -1)
        ERR_EXIT("fork error");
    if(pid > 0 )
        exit(EXIT_SUCCESS);
    if(setsid() == -1)
        ERR_EXIT("SETSID ERROR");
    if(nochdir == 0)
        chdir("/");
    if(noclose == 0){
            int i;
    for( i = 0; i < 3; ++i)
    {
        close(i);
        open("/dev/null", O_RDWR);
        dup(0);
        dup(0);
    }

    umask(0);
    return;
}
相关文章
相关标签/搜索