什么是守护进程?

守护进程是生存期长的一种进程,它们常常在系统引导装入时启动,仅在系统关闭时在终止。它们没有控制终端并且在后台运行。Linux

系统中有很多守护进程用以执行系统的日常事物,而且服务器程序一般都作为守护进程运行。大多数守护进程都以超级用户特权运行,而

且用户层守护进程的父进程是init进程。

如果你想查看一下你系统中有哪些守护进程,可以在终端下输入ps -x命令查看,TTY?的列说明此进程没有控制终端,即守护进程。

一些必要了解的基本概念

进程组:进程组是一个或多个进程的集合。每个进程都属于一个进程组,同一个进程组的各进程接受来自同一终端的各种信号。每个进程组

都有一个唯一的进程组ID。每个进程组都有一个组长进程,组长进程的进程组ID等于其进程ID。

会话:会话是一个或多个进程组的集合。会话通常是由shell的管道将几个进程编成一组的。

例如:在shell下输入如下命令:

program1 | program2 &

program3 | program4

则在此刻shell中包括3个进程组:登录shell组,由program1和program2组成的进程组,由program3和program4组成的进程组。前两个进程组

是后台进程组,第三个进程组为前台进程组。这三个进程组组成一个会话。

可调用setsid函数建立一个新会话,如果调用这个函数的进程已经是一个进程组的组长进程,则此函数返回出错。

setsid函数会做以下三件事:

  1. 时调用进程变成新会话的会话首进程,此时该进程是新会话的唯一进程。

  2. 使调用进程成为一个新进程组的组长进程,新进程组的组ID是该调用进程的进程ID。

  3. 调用进程没有控制终端。

创建守护进程的步骤

启动任意一个程序并让它成为守护进程运行需要以下步骤:

1、 用umask将文件模式创建屏蔽字设为一个已知值,由继承得来的文件模式创建屏蔽字可能会被设置为拒绝某些权限。如果守护进程要创建

文件可能要设置特定的权限。

2、 调用fork然后使父进程exit。这样父进程终止,而子进程则变为后台进程(也为孤儿进程)。虽然子进程继承父进程的进程组ID,但子进

程获得了一个新的进程ID,这就保证了子进程不是一个进程组的组长进程,这为子进程调用setsid函数创造条件。

3、 调用setsid创建一个新的会话,1)使调用进程成为新会话的首进程。2)成为一个新进程组的组长。3)没有控制终端。

4、 再次调用fork并终止父进程,继续使用子进程(第二个子进程)中的守护进程。这就保证了该守护进程不是会话首进程从而可以防止它取

得控制终端。因为当会话首进程打开第一个尚未与一个会话相关联的终端设备时,如果在调用open函数时没有指定O_NOCTTY标志,系统会将

此控制终端分配给会话,这样就不满足守护进程无控制终端的要求了。

5、 忽略SIGHUP信号,以为当会话首进程(第一个fork产生的第一个子进程)终止时,会话中的所有进程(在此指第二个子进程)都会收到SIGHUP

信号。

6、 将当前工作目录改为根目录以防止守护进程当前的工作目录在一个挂载的文件系统中。

7、 关闭所有打开的文件描述符并将标准输入,标准输出和标准错误输出重定向到/dev/null中。这样使任何一个试图读标准输入,写标准输出或

标准错误的库函数都不会产生任何效果。

8、 由于守护进程没有控制终端,那么它该如何输出出错消息呢。答案是将错误消息写到日志文件中去,调用syslog函数写出错消息。

实现代码

《UNIX环境高级编程 第三版》和《UNIX网络编程 卷一 第三版》都给出了实现代码,不过我认为前者更好。稍加修改后的代码如下:

void daemonize(const char *cmd)   // 参数cmd是程序的名字
{
int i, fd0, fd1, fd2;
pid_t pid;
struct rlimit rl;
struct sigaction sa; // 设置文件创建屏蔽字
umask(0); // getrlimit函数用于获取进程可用的最大的文件描述符
if(getrlimit(RLIMIT_NOFILE, &rl) < 0)
{
fprintf(stderr, "%s: can't get file limit", cmd);
exit(1);
} // 成为会话首进程,失去控制终端
if((pid = fork()) < 0)
{
fprintf(stderr, "%s: can't fork", cmd);
exit(1);
}
else if(pid != 0)
exit(0); // 终止父进程
setsid(); // 忽略产生的SIGHUP信号
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if(sigaction(SIGHUP, &sa, NULL) < 0)
{
fprintf(stderr, "%s: can't ignore SIGHUP", cmd);
exit(1);
}
if((pid = fork()) < 0)
{
fprintf(stderr, "%s: can't fork", cmd);
exit(1);
}
else if(pid != 0)
exit(0); // 终止父进程 // 改变当前工作目录为根目录
if(chdir("/") < 0)
{
fprintf(stderr, "%s: can't change directory to /", cmd);
exit(1);
} // 关闭所有文件描述符
if(rl.rlim_max == RLIM_INFINITY)
rl.rlim_max = 1024; // 如果进程可用的文件描述符为无限制则设为1024
for(i = 0; i < rl.rlim_max; ++i)
close(i); // 将标准输入,标准输出和标准错误输出重定向到/dev/null
fd0 = open("/dev/null", O_RDWR);
fd1 = dup(0);
fd2 = dup(0); // 初始化日志文件
openlog(cmd, LOG_CONS, LOG_DAEMON);
if(fd0 != 0 || fd1 != 1 || fd2 != 2)
{
syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0, fd1, fd2);
exit(1);
}
}

Reference:

《UNIX环境高级编程 第三版》

《UNIX网络编程 卷一 第三版》

Linux守护进程的更多相关文章

  1. .NET跨平台实践:用C#开发Linux守护进程

    Linux守护进程(Daemon)是Linux的后台服务进程,它脱离了与控制终端的关联,直接由Linux init进程管理其生命周期,即使你关闭了控制台,daemon也能在后台正常工作. 一句话,为L ...

  2. .NET跨平台实践:用C#开发Linux守护进程(转)

    Linux守护进程(Daemon)是Linux的后台服务进程,它脱离了与控制终端的关联,直接由Linux init进程管理其生命周期,即使你关闭了控制台,daemon也能在后台正常工作. 一句话,为L ...

  3. [转]❲阮一峰❳Linux 守护进程的启动方法

    ❲阮一峰❳Linux 守护进程的启动方法 "守护进程"(daemon)就是一直在后台运行的进程(daemon). 本文介绍如何将一个 Web 应用,启动为守护进程. 一.问题的由来 ...

  4. Server Develop (七) Linux 守护进程

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

  5. Linux 守护进程和超级守护进程(xinetd)

    一 .Linux守护进程 Linux 服务器在启动时需要启动很多系统服务,它们向本地和网络用户提供了Linux的系统功能接口,直接面向应用程序和用户.提供这些服务的程序是由运行在后台的守护进程来执行的 ...

  6. Linux守护进程详解(init.d和xinetd) [转]

    一 Linux守护进程 Linux 服务器在启动时需要启动很多系统服务,它们向本地和网络用户提供了Linux的系统功能接口,直接面向应用程序和用户.提供这些服务的程序是由运行在后台 的守护进程来执行的 ...

  7. Linux守护进程的编程实现

    Linux 守护进程的编程方法 守护进程(Daemon)是执行在后台的一种特殊进程.它独立于控制终端而且周期性地执行某种任务或等待处理某些发生的事件.守护进程是一种非常实用的进程.Linux的大多数s ...

  8. Linux守护进程详解(init.d和xinetd)

    一 Linux守护进程 Linux 服务器在启动时需要启动很多系统服务,它们向本地和网络用户提供了Linux的系统功能接口,直接面向应用程序和用户.提供这些服务的程序是由运行在后台的守护进程来执行的. ...

  9. C#开发Linux守护进程

    用C#开发Linux守护进程   Linux守护进程(Daemon)是Linux的后台服务进程,它脱离了与控制终端的关联,直接由Linux init进程管理其生命周期,即使你关闭了控制台,daemon ...

  10. 笔记整理--Linux守护进程

    Linux多进程开发(三)进程创建之守护进程的学习 - _Liang_Happy_Life__Dream - 51CTO技术博客 - Google Chrome (2013/10/11 16:48:2 ...

随机推荐

  1. MyBatis与Hibernate的比较

    Hibernate 与Mybatis都是流行的持久层开发框架,但Hibernate开发社区相对多热闹些,支持的工具也多,更新也快,当前最高版本4.1.8.而Mybatis相对平静,工具较少,当前最高版 ...

  2. MyBatis入门学习教程-调用存储过程

    一.提出需求 查询得到男性或女性的数量, 如果传入的是0就女性否则是男性 二.准备数据库表和存储过程 create table p_user( id int primary key auto_incr ...

  3. MySQL服务 - MySQL程序的配置文件、参数、变量查看

    查看配置文件及读取顺序 MySQL的配置文件以.cnf结尾,可能会有多个,而不同版本的MySQL程序的读取配置文件的路径也都不同,要想获取MySQL读取配置文件的顺序可以通过以下指令查看: shell ...

  4. 用git管理自己读的书

    福昕阅读器,可以方便地做书签和备注.使用git去管理图书和示例代码,一方面能够很好地保存这些资料,方便查找:另外一方面,无论在家,还是在公司,都能很好地同步自己学习的进度. 福昕阅读器5(视图--&g ...

  5. Git 版本库添加空目录方法

    直接在版本库工作目录下创建空目录后,使用git status查看,发现空目录没有被版本库识别. 正确的添加空目录的方法: 在空目录下创建.gitkeep文件:touch .gitkeep 然后重新执行 ...

  6. 那些年蹚过的坑(c++)

    1 main中的参数 尽量用英文双引号括起来,否则可能会出现字符串截断的情况(linux gcc 4.4 不加英文引号的话,遇到英文;会丢弃;后面的部分) 2 包含ipv6地址结构体(in6_addr ...

  7. 如何用 JIRA REST API 创建 Issue

    简介 最近需要把一个Excel里的issues list全部到JIRA上create 一遍, 总不能手动创建百十来个issues吧, 本文讲述一下如果调用JIRA提供的Rest API 来自动创建is ...

  8. Tornado实战项目(伪JD商城)

    预备知识 在之前tornado商城项目中,在开始之前需要引入一些项目设计知识,如接口,抽象方法抽象类,组合,程序设计原则等,个人理解项目的合理设计可增加其灵活性, 降低数据之间的耦合性,提高稳定性,下 ...

  9. Esfog_UnityShader教程_遮挡描边(原理篇)

    咳咳,有段时间没有更新了,最近有点懒!把不少精力都放在C++身上了.闲言少叙,今天要讲的可和之前的几篇有所不同了,这次是一个次综合应用.这篇内容中与之前不同主要体现在下面几点上. 1.之前我们写的都是 ...

  10. SQLServer异步调用,批量复制

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...