这又是一个有趣的概念,daemon在英语中是"精灵"的意思,就像我们经常在迪斯尼动画里见到的那些,有些会飞,有些不会,经常围着动画片的主人公转来转去,啰里啰唆地提一些忠告,时不时倒霉地撞在柱子上,有时候还会想出一些小小的花招,把主人公从敌人手中救出来,正因如此,daemon有时也被译作"守护神"。所以,daemon进程在国内也有两种译法,有些人译作"精灵进程",有些人译作"守护进程",这两种称呼的出现频率都很高。

与真正的daemon相似,daemon进程也习惯于把自己隐藏在人们的视线之外,默默为系统做出贡献,有时人们也把它们称作"后台服务进程"。daemon进程的寿命很长,一般来说,从它们一被执行开始,直到整个系统关闭,它们才会退出。几乎所有的服务器程序,包括我们熟知的Apache和wu-FTP,都用daemon进程的形式实现。很多Linux下常见的命令如inetd和ftpd,末尾的字母d就是指daemon。

为什么一定要使用daemon进程呢?Linux中每一个系统与用户进行交流的界面称为终端(terminal),每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端(Controlling terminal),当控制终端被关闭时,相应的进程都会被自动关闭。关于这点,读者可以用X-Window中的XTerm试验一下,(每一个XTerm就是一个打开的终端,)我们可以通过键入命令启动应用程序,比如:

$netscape

然后我们关闭XTerm窗口,刚刚启动的netscape窗口也会随之一同突然蒸发。但是daemon进程却能够突破这种限制,即使对应的终端关闭,它也能在系统中长久地存在下去,如果我们想让某个进程长命百岁,不因为用户或终端或其他的变化而受到影响,就必须把这个进程变成一个daemon进程。

如果想把自己的进程变成daemon进程,我们必须严格按照以下步骤进行:

  1. 调用fork产生一个子进程,同时父进程退出。我们所有后续工作都在子进程中完成。这样做我们可以:
    1. 如果我们是从命令行执行的该程序,这可以造成程序执行完毕的假象,shell会回去等待下一条命令;
    2. 刚刚通过fork产生的新进程一定不会是一个进程组的组长,这为第2步的执行提供了前提保障。

    这样做还会出现一种很有趣的现象:由于父进程已经先于子进程退出,会造成子进程没有父进程,变成一个孤儿进程(orphan)。每当系统发现一个孤儿进程,就会自动由1号进程收养它,这样,原先的子进程就会变成1号进程的子进程。

  2. 调用setsid系统调用。这是整个过程中最重要的一步。setsid的介绍见附录2,它的作用是创建一个新的会话(session),并自任该会话的组长(session leader)。如果调用进程是一个进程组的组长,调用就会失败,但这已经在第1步得到了保证。调用setsid有3个作用:把当前工作目录切换到根目录。如果我们是在一个临时加载的文件系统上执行这个进程的,比如:/mnt/floppy/,该进程的当前工作目录就会是/mnt/floppy/。在整个进程运行期间该文件系统都无法被卸下(umount),而无论我们是否在使用这个文件系统,这会给我们带来很多不便。解决的方法是使用chdir系统调用把当前工作目录变为根目录,应该不会有人想把根目录卸下吧。关于chdir的用法,参见附录1。 
    1. 让进程摆脱原会话的控制;
    2. 让进程摆脱原进程组的控制;
    3. 让进程摆脱原控制终端的控制;

    总之,就是让调用进程完全独立出来,脱离所有其他进程的控制。

  3. 当然,在这一步里,如果有特殊的需要,我们也可以把当前工作目录换成其他的路径,比如/tmp。
  4. 将文件权限掩码设为0。这需要调用系统调用umask,参见附录3。每个进程都会从父进程那里继承一个文件权限掩码,当创建新文件时,这个掩码被用于设定文件的默认访问权限,屏蔽掉某些权限,如一般用户的写权限。当另一个进程用exec调用我们编写的daemon程序时,由于我们不知道那个进程的文件权限掩码是什么,这样在我们创建新文件时,就会带来一些麻烦。所以,我们应该重新设置文件权限掩码,我们可以设成任何我们想要的值,但一般情况下,大家都把它设为0,这样,它就不会屏蔽用户的任何操作。 
    如果你的应用程序根本就不涉及创建新文件或是文件访问权限的设定,你也完全可以把文件权限掩码一脚踢开,跳过这一步。
  5. 关闭所有不需要的文件。同文件权限掩码一样,我们的新进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不被我们的daemon进程读或写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。需要指出的是,文件描述符为0、1和2的三个文件(文件描述符的概念将在下一章介绍),也就是我们常说的输入、输出和报错这三个文件也需要被关闭。很可能不少读者会对此感到奇怪,难道我们不需要输入输出吗?但事实是,在上面的第2步后,我们的daemon进程已经与所属的控制终端失去了联系,我们从终端输入的字符不可能达到daemon进程,daemon进程用常规的方法(如printf)输出的字符也不可能在我们的终端上显示出来。所以这三个文件已经失去了存在的价值,也应该被关闭。

下面,就然我们亲眼看一个daemon进程的诞生:

/* daemon.c */

#include<unistd.h>

#include<sys/types.h>

#include <sys/stat.h>

#define MAXFILE 65535

main()

{

  pid_t pid;  int i;

  pid=fork();

  if(pid<0)

  {

    printf("error in fork\n");

    exit(1);

  }

  else if(pid>0)

  /* 父进程退出 */

  exit(0);

  /* 调用setsid */

  setsid();

  /* 切换当前目录 */

  chdir("/");

  /* 设置文件权限掩码 */

  umask(0);

  /* 关闭所有可能打开的不需要的文件 */

  for(i=0;i<MAXFILE;i++)

  close(i);

  /*到现在为止,进程已经成为一个完全的daemon进程,你可以在这里添加任何你要daemon做的事情*/

  for(;;)

  sleep(10);

}

Daemon进程的更多相关文章

  1. Qualcomm 8X camera daemon进程浅析

    Camera 先看一下抽象层的主要流程: 首先启动一个守护进程 int qcamsvr_start(void)( qcamsvr.c) { 1. server_fd = open(server_dev ...

  2. 在Hudson Job中启动daemon进程

    场景 在Hudson中新建一个Job用于构建Web工程,在Job的构建脚本的最后会启动Jetty,观察发现Jetty启动之后一小段时间,进程就终止了.   环境 CentOS 6,Hudson 3.0 ...

  3. Linux中创建Daemon进程的三种方法

    什么是daemon进程? Unix/Linux中的daemon进程类似于Windows中的后台服务进程,一直在后台运行运行,例如http服务进程nginx,ssh服务进程sshd等.注意,其英文拼写为 ...

  4. Linux系统编程——Daemon进程

    目录 Daemon进程介绍 前提知识 Daemon进程的编程规则 Daemon进程介绍 Daemon运行在后台也称作"后台服务进程". 它是没有控制终端与之相连的进程.它独立与控制 ...

  5. PHP 如何创建守护(daemon)进程

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

  6. linux下的daemon进程

    转自:http://www.cnblogs.com/xuxm2007/archive/2011/07/29/2121280.html   #include <unistd.h> int d ...

  7. daemon进程fork一次和fork两次的区别?

    守护进程也称为精灵进程(Daemon),是运行在后台的一种特殊的进程.它独立于控制终端并且周期性的执行某种任务负等待处理某些发生的事件.因为他们没有控制终端,所以说他们是在后台运行的. 守护进程的特点 ...

  8. Linux Daemon进程

    模型 创建孤儿进程 fork() 创建新的会话 setid() 改变当前目录为根目录chdir() 重设文件权限掩码umask() 关闭文件描述符close() 创建孤儿进程 创建孤儿进程只需要将父进 ...

  9. linux下tomcat作为daemon进程运行

    在linux下如果想让tomcat在开机时自启动,可以将启动代码写到/etc/rc.local里面.但是,这样的话,tomcat将以root权限运行,这是不安全的.因此,要想办法让tomcat以非特权 ...

随机推荐

  1. 一个关于PCA的疑问

    我们知道PCA干的事情是把n维的样本投影到k维,同时丢失的信息能够达到最少. 为什么说principal component是covariance matrix的特征值中最大的前k个对应的特征向量上的 ...

  2. BZOJ2303: [Apio2011]方格染色 【并查集】

    Description Sam和他的妹妹Sara有一个包含n × m个方格的表格.她们想要将其的每个方格都染成红色或蓝色.出于个人喜好,他们想要表格中每个2 × 2的方形区域都包含奇数个(1 个或 3 ...

  3. 常用输入法快速输入自定义格式的时间和日期(搜狗/QQ/微软拼音)

    几个主流的输入法输入 rq 或者 sj 都可以得到预定义格式的日期或者时间.然而他们都是预定义的格式:当我们需要一些其他格式的时候该怎么做呢? 本文将介绍几个常用输入法自定义时间和日期格式的方法. 主 ...

  4. php 中的 Output Control 函数

    先看一个简单的例子 <?php ob_start(); echo 111; ob_clean(); echo 222; ob_start()开启ob缓存,然后111放进了ob缓存, 再调用ob_ ...

  5. 《DSP using MATLAB》示例Example 8.6

    代码: %% ------------------------------------------------------------------------ %% Output Info about ...

  6. iis 部署 未在本地计算机上注册“Microsoft.Jet.OleDb.4.0”提供程序

    C#读取Access数据库在VS调试时正常,发布到win7-64的IIS之后报错“未在本地计算机上注册“Microsoft.Jet.OleDb.4.0”提供程序”.原因是VS调试时模拟的是32位,发布 ...

  7. spring注解实现业务层事务管理,当业务层自调用时,事务失效问题解决

    前些日子一朋友在需要在目标对象中进行自我调用,且需要实施相应的事务定义,且网上的一种通过BeanPostProcessor的解决方案是存在问题的.因此专门写此篇帖子分析why. 1.预备知识 aop概 ...

  8. TCP和UDP通信(C#网络编程) ---- 系列文章

    文章系列目录 C#网络编程系列文章(一)之Socket实现异步TCP服务器 C#网络编程系列文章(二)之Socket实现同步TCP服务器 C#网络编程系列文章(三)之TcpListener实现异步TC ...

  9. linux多线程并发

    多线程并发 进程和线程的概念 进程 进程包括程序映象.地址空间等要素.内核采用PCB来管理进程.进程是内核进行调度的基本单元,每个独立的进程都有自己的代码段.数据段以及堆栈,它们有自己的虚拟地址空间, ...

  10. 20 几个知名公司的 Java 面试题汇总

    查看不同公司新鲜真实的Java面试题,摘自Glassdoor.com 巴克莱投资: 假设有一个 getNextparson() 方法返回 Person 对象,Person 类实现了 comparabl ...