在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. jQuery插件——可以动态改动颜色的jQueryColor

    1.请选择代码框中所有代码后, 保存为 jquery.color.js /*! * jQuery Color Animations v@VERSION * https://github.com/jqu ...

  2. tomcat sso 配置

    源: http://www.oecp.cn/hi/single/blog/349 为了实现单点登录,做了个CAS SSO单点登录实例,经过反复的琢磨和修改终于成功了,现将CAS SSO单点登录实例详细 ...

  3. JQuery实战--能够编辑的表格

    廊坊下雪了.15年的第二场雪.比14的来的稍晚一些.停靠在11教门前的自行车.成了廊坊师范学院最漂亮的风景线.还记得以前学习css的时候.就以前接触过怎样编写设计一些表格和表单的样式,比如怎样设计表格 ...

  4. Android图片加载框架最全解析(三),深入探究Glide的缓存机制

    在本系列的上一篇文章中,我带着大家一起阅读了一遍Glide的源码,初步了解了这个强大的图片加载框架的基本执行流程. 不过,上一篇文章只能说是比较粗略地阅读了Glide整个执行流程方面的源码,搞明白了G ...

  5. Spark GraphX图处理编程实例

    所构建的图如下: Scala程序代码如下: import org.apache.spark._ import org.apache.spark.graphx._ // To make some of ...

  6. 数据库实例: STOREBOOK > 用户 > 编辑 用户: MGMT_VIEW

    ylbtech-Oracle:数据库实例: STOREBOOK  >  用户  >  编辑 用户: MGMT_VIEW 编辑 用户: MGMT_VIEW 1. 一般信息返回顶部 1.1, ...

  7. 使用HTML5开发离线应用 - cache manifest

    HTML5 是目前正在讨论的新一代 HTML 标准,它代表了现在 Web 领域的最新发展方向.在 HTML5 标准中,加入了新的多样的内容描述标签,直接支持表单验证.视频音频标签.网页元素的拖拽.离线 ...

  8. 我所遭遇过的游戏中间件--Kynapse

    我所遭遇过的游戏中间件--Kynapse Autodesk Kynapse游戏中间件是一款面向游戏开发.非玩家控制角色实时模拟的领先的人工智能解决方案.Kynapse具有先进的路径查找功能,比如三维路 ...

  9. 第七章 JVM性能监控与故障处理工具(1)

    1.定位系统问题 依据 GC日志 堆转储快照(heapdump/hprof文件) 线程快照(threaddump/javacore文件) 运行日志 异常堆栈 分析依据的工具 jps:显示指定系统内的所 ...

  10. ExcelReader(解析Excel的工具类)

    package cn.com.css.common.util; import java.io.IOException; import java.io.InputStream; import java. ...