在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

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

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

    1.概述 在大型软件系统中,为了监测软件运行状况及排查软件故障,一般都会要求软件程序在运行的过程中产生日志文件.在日志文件中存放程序流程中的一些重要信息, 包括:变量名称及其值.消息结构定义.函数返回 ...

  2. Linux 下Qt实现守护进程实例(转)

     原文地址:Linux守护进程的编程方法(含实例) 作者:lingdxuyan 参考文献 Linux信号列表(zz) Linux 守护进程的编程方法 linux上编写守护进程的例程 Linux下后台守 ...

  3. Linux下一个简单sniffer的实现

    Sniffer(嗅探器)是一种基于被动侦听原理的网络分析方式.将网络接口设置在监听模式,便可以将网上传输的源源不断的信息截获.对于网络监听的基本原理我们不在赘述,我们也不开启网卡的混杂模式,因为现在的 ...

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

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

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

    导读 Taskwarrior 是 Ubuntu/Linux 下一个简单而直接的基于命令行的 TODO 工具.这个开源软件是我曾用过的最简单的基于命令行的工具之一.Taskwarrior 可以帮助你更好 ...

  6. Windows 下针对python脚本做一个简单的进程保护

    前提: 大家运行的脚本程序经常会碰到系统异常关闭.或被其他用户错杀的情况.这样就需要一个进程保护的工具. 本文结合windows 的计划任务,实现一个简单的进程保护的功能. 利用py2exe生产 ex ...

  7. 一只简单的网络爬虫(基于linux C/C++)————守护进程

    守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程.它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.守护进程常常在系统引导装入时启动, ...

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

    本文转载自:http://www.cnblogs.com/mickole/p/3188321.html 一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终 ...

  9. linux系统编程:守护进程详解及创建,daemon()使用

    一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.它不需要用户输入就能运行而且提供某种服务,不是对整个 ...

随机推荐

  1. UITableViewCell状态切换效果

    UITableViewCell状态切换效果 效果图 源码 https://github.com/YouXianMing/Animations // // TableViewTapAnimationCo ...

  2. MyEclipse项目如何导入到Eclipse

    1.请首先确保你的eclipse是javaee版本的,或者已经安装看wtp插件 2.然后修改eclipse工程下的.project文件: 3.在<natures></natures& ...

  3. tomcat完整配置

    规划: 网站网页目录:/web/www 域名:www.test1.com 论坛网页目录:/web/bbs URL:bbs.test1.com/bbs 网站管理程序:$CATALINA_HOME/wab ...

  4. dao层的泛型实现(2种方法)

    一: package com.wzs.test2.dao; import java.util.List; public interface CommonDAO { public <T> v ...

  5. 硬件负载均衡F5和软负责均衡Nginx

    请直接搜索相关文章了解:http://www.ideadata.com.cn/wisdomAction/readWisdom.do?id=75   F5,硬件 优点:能够直接通过智能交换机实现,处理能 ...

  6. 学习笔记:AC自动机

    话说AC自动机有什么用......我想要自动AC机 AC自动机简介:  首先简要介绍一下AC自动机:Aho-Corasick automation,该算法在1975年产生于贝尔实验室,是著名的多模匹配 ...

  7. Objective-C:字符串的反转Reverse

    OC中字符串的反转方式可以用两种方式来处理: 第一种:从头到尾取出字符串的每一个字符,然后将其从尾到头添加到可变的字符串中,最后输出即可. 第二种:将OC内部的字符串转换为C语言中的字符串,然后动态分 ...

  8. WhyDX9:翻写D3D红龙书中的程序

    之前写过一套学习OpenGL的框架:WhyGL,即然有了GL那也应该再写个学习D3D的框架:WhyDX9.两个程序的架构相同,然后这个程序是将D3D红龙那本书的DEMO翻写了一遍.先将可执行程序发一下 ...

  9. [asp.net入门]利用ADO.NET处理数据的简单之处

    由于项目需要,要往数据库中导入一些历史数据,而这些历史数据都是线下人工记录的,所以有很多不规范的地方,比如:同一个公司的名称在不同的记录中可能相差那么几个字,而且每条数据不是每个字段都是完整的,等等诸 ...

  10. 运用CSS改进网站设计的3个技巧

    CSS是一种分离表述编码和实际内容的一种最有用的方式,也是一种使搜索引擎最优化的方式,通常,网站设计中会建立一整套的css样式标准,这些标准存储在一个扩展名为css的单独文件中.然后利用HTML标签设 ...