编写一个守护进程

守护进程-----也就是常说的Daemon进程,是linux后台的服务进程,是一个生存期较长的进程,独立于终端并且周期性的执行某种任务或等待处理某些任务。守护进程在系统引                        导时载入时启动,在系统关闭时终止。linux下的许多系统服务都是守护进程完成的。守护进程还能完成许多系统任务,如作业规划进程crond,打印进程lqd(d就是                            daemon的意思)

编写守护进程主要有五个步骤:

1.创建子进程,父进程退出

守护进程需要脱离shell终端,造成一种程序关闭的假象,所有的工作都在子进程中完成,创建子进程,父进程退出,会造成子进程成为一个孤儿进程,在linux中当系统发现一个孤儿进程,就会1号进程(init进程)收养它,这样子进程就变成init子进程了。代码如下:

pid = fork() ;
if (pid > 0)
    exit(0) ;//父进程退出
2.在子进程中创建新会话

这一步实现简单,但很重要,先了解两个概念

进程组-------进程组就是多个进程,进程组由进程组ID标识,进程组ID 也是一个进程的必备属性,每个进程组都有一个组长进程,组长进程的ID等于进程组的ID,进程ID不会因为                        组长进程的退出而受到影响

会话期-------会话组是多个进程组组成的,一个会话开始于用户登录,终止于用户退出,在此期间用户运行的所有进程都属于这个会话期,关系如下:


setsid()-------创建一个新的会话,并担任会话组的组长,具体有三个作用

(1)让进程摆脱原会话的控制

(2)让进程原进程组的控制

(3)让进程摆脱原控制终端的控制

ps:虽然创建子进程父进程退出了,但子进程复制了父进程的会话期,进程组和控制终端,所以要让子进程完全的独立出来就需要调用setsid()函数,摆脱所有其他进程的控制

setsid()语法如下:


3.改变当前目录为根目录

由于子进程继承了父进程的当前工作目录,由于在进程运行过程中当前工作目录的文件系统(如”/mnt/usb“)是不能卸载的,这回给以后使用造成麻烦(比如系统需要进入单用户模式),所以通常的做法是让"/"作为当前目录,当然也可以把当前目录换成其它目录,如/tmp,使用函数chdir()即可。

4.重设文件权限掩码

文件权限掩码是指屏蔽掉文件权限中的对应位,比如一个文件权限掩码是050它就屏蔽了文件拥有者得可读与可写权限,fork()创建的子进程会继承父进程的文件权限掩码,所以我们把文件权限掩码设为0,可以增加子进程的灵活性,使用umask(0) 即可。

5.关闭文件描述符,同文件权限掩码一样,子进程会进程父进程的一些已经打开的文件,这些打开的文件可能永远都不会被守护进程读与写,但它会占用系统资源,而且会导致所在文件系统无法卸载,在上面第二步之后子进程就会与shell终端脱离,所以文件描述符为0, 1, 2的三个文件就失去了价值,所以也应当关闭通常使用以下方式:

for(i = 0; i < MAXFILE; i++)
     close(i);
这样一个守护进程就建立起来了,具体过程如下


这里有个例子,这个守护进程每隔10s向/tmp/daemon.log写入一句话

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
    pid_t pid ;
    int i, fd ;
    char *buf = "This is a daemon\n" ;
    pid = fork() ;//第一步,创建子进程
    if(pid < 0)
    {
        printf("fork error\n") ;
        exit(1) ;
    }
    else if (pid > 0)
        exit(0) ;
    setsid() ;//第二步,创建新会话
    chdir("/") ;//改变当前工作目录为跟目录
    umask(0) ;//设置文件权限掩码
    for(i = 0; i < getdtablesize(); ++i)
    {
        close(i) ;//关闭文件权限描述符
    }
	//守护进程每隔10s打日志
    while(1)
    {
        if (fd = open("/tmp/daemon.log", O_CREAT|O_WRONLY|O_APPEND, 0600) < 0)
        {
            printf("open file error\n") ;
            exit(1) ;
        }
        write(fd, buf, strlen(buf)+1) ;
        close(fd) ;
        sleep(10) ;
    }
    exit(0) ;
}
我们可以使用tail -f /tmp/daemon.log或者直接到目录下查看,运行如下

*********************************************

守护进程由于完全脱离终端,即使使用gdb也是无法调试的,一种通用方法是使用syslog服务,将程序的出错信息输入到系统的日志文件中(如:/var/log/messages)从而可以直观的看到程序的问题所在

syslog是linux系统日志管理服务,通过守护进程syslogd维护,进程在启动时会读取配置文件"/etc/syslog.conf"该文件决定了不同种类的消息送向何处,如紧急消息会送向系统管理员并在控制台上显示,而警告信息会记录在一个文件中

这个机制会提供三个syslog相关的函数openlog(),syslog(),closelog()------openlog()函数用于打开系统日志服务的一个链接,syslog()用于向日志文件中写入信息其中会规定消息的等级,消息输出格式等,closelog()用于关闭日志服务

以下是openlog()的语法:




以下是syslog()的语法:


closelog()函数语法:


下面是使用syslog系统调试的例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <syslog.h>
int main()
{
    pid_t pid ;
    int i, fd ;
    char *buf = "This is a daemon\n" ;
    pid = fork() ;
    if(pid < 0)
    {
        printf("fork error\n") ;
        exit(1) ;
    }
    else if (pid > 0)
        exit(0) ;
    openlog("daemon_syslog", LOG_PID, LOG_DAEMON) ;
    if(setsid() < 0 )
    {
        syslog(LOG_ERR, "%s", "setsid") ;
        exit(1) ;
    }
    if (chdir("/") < 0)
    {
        syslog(LOG_ERR, "%s", "chdir") ;
        exit(1) ;
    }

    umask(0) ;
    for(i = 0; i < getdtablesize(); ++i)
    {
        close(i) ;
    }
    while(1)
    {
        if (fd = open("/tmp/daemon.log", O_CREAT|O_WRONLY|O_APPEND, 0600) < 0)
        {
            printf("open file error\n") ;
            exit(1) ;
        }
        syslog(LOG_ERR, "%s", "open\n") ;
        write(fd, buf, strlen(buf) + 1) ;
        close(fd) ;
        sleep(10) ;
    }
    closelog() ;
    exit(0) ;
}
程序会将错误信息写入系统日志里面,open函数必须具有root权限,系统才会将错误信息写入系统日志/var/log/messages,这个日志必须要在root下查看,普通用户查看不到的





  • 5
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值