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

在Linux/UNIX系统引导的时候会开启很多服务,这些服务称为守护进程(也叫Daemon进程)。守护进程是脱离于控制终端并且在后台周期性地执行某种任务或等待处理某些事件的进程,脱离终端是为了避免进程在执行过程中的信息在任何终端上显示并且进程也不会被任何终端所产生的中断信息所终止。

创建守护进程的一般步骤

(1) 创建子进程,退出父进程

为了脱离控制终端需要退出父进程,之后的工作都由子进程完成。在Linux中父进程先于子进程退出会造成子进程成为孤儿进程,而每当系统发现一个孤儿进程时,就会自动由1号进程(init)收养它,这样,原先的子进程就会变成init进程的子进程。

ps –ef | grep ProcName  通过PID/PPID查看进程的父子关系

(2) 在子进程中创建新的会话

使用系统函数setsid来完成。

man 2 setsid    查看关于setsid函数的说明

setsid – creates a session and sets theprocess group ID

#include <unistd.h>

pid_t setsid(void);

setsid() creates a new session if thecalling process is not a process group leader. The calling process is theleader of the new session, the process group leader of the new process group,and has no controlling tty. The process group ID and session ID of the callingprocess are set to the PID of the calling process. The calling process will bethe only process in this new process group and in this new session.

进程组:是一个或多个进程的集合。进程组有进程组ID来唯一标识。除了进程号PID之外,进程组ID也是一个进程的必备属性。每个进程组都有一个组长进程,其组长进程的进程号等于进程组ID,且该进程组ID不会因组长进程的退出而受到影响。

setsid函数作用:用于创建一个新的会话,并担任该会话组的组长。调用setsid有3个作用

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

(b) 让进程摆脱原进程组的控制;

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

使用setsid函数的目的:由于创建守护进程的第一步调用了fork函数来创建子进程再将父进程退出。由于在调用fork函数时, 子进程拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变,因此,这还不是真正意义上的独立开了。使 用setsid函数后,能够使进程完全独立出来,从而摆脱其他进程的控制。

(3) 改变当前目录为根目录

使用fork创建的子进程继承了父进程的当前的工作目录。由于在进程运行中,当前目录所在的文件系统是不能卸载的,这对以后的使用会造成诸多的麻 烦。因此,通常的做法是让根目录”/”作为守护进程的当前工作目录。这样就可以避免上述的问题。如有特殊的需求,也可以把当前工作目录换成其他的路径。改 变工作目录的方法是使用chdir函数。

(4) 重设文件权限掩码

文件权限掩码:是指屏蔽掉文件权限中的对应位。例如,有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限(对应二进制 为,rwx, 101)。由于fork函数创建的子进程继承了父进程的文件权限掩码,这就给子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为0(即,不屏蔽 任何权限),可以增强该守护进程的灵活性。设置文件权限掩码的函数是umask。通常的使用方法为umask(0)。

(5) 关闭文件描述符

用fork创建的子进程也会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读写,但它们一样消耗系统资源,而且可 能导致所在的文件系统无法卸载。在使用setsid调用之后,守护进程已经与所属的控制终端失去了联系,因此从终端输入的字符不可能达到守护进程,守护进 程中用常规方法(如printf)输出的字符也不可能在终端上显示出来。所以,文件描述符为0、1、2(即,标准输入、标准输出、标准错误输出)的三个文 件已经失去了存在的价值,也应该关闭。

(6) 守护进程退出处理

当用户需要外部停止守护进程时,通常使用kill命令停止该守护进程。所以,守护进程中需要编码来实现kill发出的signal信号处理,达到进程正常退出。

下面是一个简单的实现:

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

#define MAXFILE 65535  

volatile sig_atomic_t _running = 1;
int fd;  

// signal handler
void sigterm_handler(int arg)
{
    _running = 0;
}  

int main()
{
    pid_t pid;
    char *buf = "This is a Daemon, wcdj\n";  

    /* 屏蔽一些有关控制终端操作的信号
     * 防止在守护进程没有正常运转起来时,因控制终端受到干扰退出或挂起
     * */
    signal(SIGINT,  SIG_IGN);// 终端中断
    signal(SIGHUP,  SIG_IGN);// 连接挂断
    signal(SIGQUIT, SIG_IGN);// 终端退出
    signal(SIGPIPE, SIG_IGN);// 向无读进程的管道写数据
    signal(SIGTTOU, SIG_IGN);// 后台程序尝试写操作
    signal(SIGTTIN, SIG_IGN);// 后台程序尝试读操作
    signal(SIGTERM, SIG_IGN);// 终止  

    // test
    //sleep(20);// try cmd: ./test &; kill -s SIGTERM PID  

    // [1] fork child process and exit father process
    pid = fork();
    if(pid < 0)
    {
        perror("fork error!");
        exit(1);
    }
    else if(pid > 0)
    {
        exit(0);
    }  

    // [2] create a new session
    setsid();  

    // [3] set current path
    char szPath[1024];
    if(getcwd(szPath, sizeof(szPath)) == NULL)
    {
        perror("getcwd");
        exit(1);
    }
    else
    {
        chdir(szPath);
        printf("set current path succ [%s]\n", szPath);
    }  

    // [4] umask 0
    umask(0);  

    // [5] close useless fd
    int i;
    //for (i = 0; i < MAXFILE; ++i)
    for (i = 3; i < MAXFILE; ++i)
    {
        close(i);
    }  

    // [6] set termianl signal
    signal(SIGTERM, sigterm_handler);  

    // open file and set rw limit
    if((fd = open("outfile", O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0)
    {
        perror("open");
        exit(1);
    }  

    printf("\nDaemon begin to work..., and use kill -9 PID to terminate\n");  

    // do sth in loop
    while(_running)
    {
        if (write(fd, buf, strlen(buf)) != strlen(buf))
        {
            perror("write");
            close(fd);
            exit(1);
        }  

        usleep(1000*1000);// 1 s
    }
    close(fd);  

    // print data
    if((fd = open("outfile", O_RDONLY)) < 0)
    {
        perror("open");
        exit(1);
    }
    char szBuf[1024] = {0};
    if(read(fd, szBuf, sizeof(szBuf)) == -1)
    {
        perror("read");
        exit(1);
    }
    printf("read 1024 bytes:\n%s\n", szBuf);  

    close(fd);  

    return 0;
}
 /*
   gcc -Wall -g -o test test.c
   ps ux | grep -v grep | grep test
   tail -f outfile
   kill -s SIGTERM PID
 */

参考:

[1] 守护进程
[2] 编写Linux/Unix守护进程
[3] Linux 信号signal处理函数
[4] The usage of sig_atomic_t in linux signal mask function

时间: 2024-08-08 01:57:55

Linux下一个简单守护进程的实现 (Daemon)的相关文章

Linux下一个简单的日志系统的设计及其C代码实现

1.概述 在大型软件系统中,为了监测软件运行状况及排查软件故障,一般都会要求软件程序在运行的过程中产生日志文件.在日志文件中存放程序流程中的一些重要信息, 包括:变量名称及其值.消息结构定义.函数返回值及其执行情况.脚本执行及调用情况等.通过阅读日志文件,我们能够较快地跟踪程序流程,并发现程序问题. 因此,熟练掌握日志系统的编写方法并快速地阅读日志文件,是对一个软件开发工程师的基本要求. 本文详细地介绍了Linux下一个简单的日志系统的设计方法,并给出了其C代码实现.本文为相关开发项目Linux

浅谈Linux下的syslog守护进程

syslog是Linux下默认的日志守护进程.任何希望生成日志信息的程序都可以通过syslog 接口实现该目的. 同时,几乎所有的网络设备都可以通过syslog协议,将日志信息以UDP的形式传送给远端服务器,远端接收日志服务器必须通过syslogd监听UDP端口514,并根据 syslog.conf 配置文件张的配置处理.配置文件 /etc/rsyslog.conf 中规定了syslogd如何根据设备和信息的重要级别来报告信息. 这里只需要有个大致了解即可,下面都会提到这些东西. 关于配置文件:

Linux下tomcat作为守护进程运行(开机启动、以指定的用户运行、解决非root身份不能绑定1024以下端口的问题)的配置方法

如题. 参考资料: http://www.jdiy.org/read.jd?id=y0haaynq1w http://blog.csdn.net/shw2004/article/details/5782318

Linux 下一个很棒的命令行工具

导读 Taskwarrior 是 Ubuntu/Linux 下一个简单而直接的基于命令行的 TODO 工具.这个开源软件是我曾用过的最简单的基于命令行的工具之一.Taskwarrior 可以帮助你更好地组织你自己,而不用安装笨重的新工具——这有时丧失了 TODO 工具的目的. Taskwarrior是一个开源.跨平台.基于命令行的 TODO 工具,它帮你在终端中管理你的 to-do 列表.这个工具让你可以轻松地添加任务.展示列表.移除任务.而且,在你的默认仓库中就有,不用安装新的 PPA.在 U

Linux系统编程之--守护进程的创建和详解【转】

本文转载自:http://www.cnblogs.com/mickole/p/3188321.html 一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务.Linux系统的大多数服务器就是通过守护进程实现的.常见的守护进程包括系统日志进程syslogd. web服务器httpd.邮件服务器sendmail和数据库服务器

Memcahce(MC)系列(两)Linux下一个Memcache安装

Linux下一个memcache安装 memcache是高性能.分布式的内存对象缓存系统,用于在动态应用中降低数据库负载.提升訪问速度.眼下用memcache解决互联网上的大用户读取是很流行的一种使用方法,在互联网企业中有着广泛的应用. ?关于memcache的具体介绍,有兴趣的朋友请參考这篇文章:http://blog.csdn.net/xifeijian/article/details/21994941 1.下载memcache 墙裂推荐官方下载(http://memcached.org/)

Linux下搭建简单的DHCP服务器

在RHEL6.5下搭建简单的DHCP服务器 实验目标 1.为192.168.10.0/24网段的客户机自动配置网络参数 用来给客户机自动分配地址的IP地址范围是:192.168.10.50-192.168.10.100.192.168.10.120-192.168.10.200 客户机的默认网关地址设为192.168.10.1 客户机所使用的DNS服务器设为202.106.0.20.8.8.8.8,默认域后缀是"tarena.com" 将默认租期设为2小时,最大租期设置为4小时 2.为

Linux下取代top的进程管理工具 htop

一.htop 简介 This is htop, an interactive process viewer for Linux. It is a text-mode application (for console or X terminals) and requires ncurses. Comparison between htop and top In 'htop' you can scroll the list vertically and horizontally to see all

Linux编程之《守护进程》

Intro ----- 守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程.它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.守护进程常常在系统引导装入时启动,在系统关闭时终止.Linux系统有很多守护进程,大多数服务都是通过守护进程实现的,同时,守护进程还能完成许多系统任务,例如,作业规划进程crond.打印进程lqd等(这里的结尾字母d就是Daemon的意思). 由于在Linux中,每一个系统与用户进行交流的界面称为终端,每一个