当前位置: 首页 > Linux

[转]Linux守护进程的原理与实现

时间:2023-04-06 23:25:07 Linux

原文地址:http://blog.csdn.net/zsf8701/...一、守护进程概述在Linux或unix操作系统中,当系统启动时,它会启动很多服务,这些服务被称为守护进程。为了增加灵活性,root可以选择系统启动的模式。这些模式称为运行级别,每个运行级别以特定方式配置系统。守护进程是在后台运行的进程,与终端分开。守护进程之所以与终端分离,是为了防止进程执行过程中的信息显示在任何终端上,进程不会被任何终端产生的终端信息打断。2.守护进程简介守护进程,又称Daemon进程,是Linux中的一个后台服务进程。它是一个长期存在的进程,通常独立于控制终端,定期执行某些任务或等待某些事件发生。守护进程通常在系统启动时启动,在系统关闭时终止。Linux系统中有很多守护进程,大部分服务都是通过守护进程实现的。同时守护进程还可以完成很多系统任务,例如作业计划进程crond、打印进程lqd等(这里的结尾字母d表示Daemon)。因为在Linux中,每个系统与用户进行交流的接口称为终端,每一个从这个终端开始运行的进程都会依附在这个终端上。这个终端被称为这些进程的控制终端。当控制终端关闭时,相应的进程也会自动关闭。但是守护进程可以突破这个限制。它从被执行开始运行,直到整个系统关闭才退出。如果你想让一个进程不受用户或终端或其他地方变化的影响,那么你必须把这个进程变成守护进程。3.创建守护进程创建子进程,父进程退出这是编写守护进程的第一步。由于守护进程与控制终端分离,第一步完成后,会造成一个程序在Shell终端运行完毕的错觉。后续的所有工作都在子进程中完成,用户可以在Shell终端中执行其他命令,从而在形式上实现了与控制终端的分离。在Linux中,父进程先于子进程退出会导致子进程成为孤儿进程,而每当系统发现孤儿进程时,就会自动被1号进程(init)收养,从而使原来的子进程将成为init进程的子进程。在子进程中创建新会话的步骤是创建守护进程最重要的一步。虽然它的实现很简单,但意义却很重大。这里使用系统函数setsid。在详细介绍setsid之前,首先要了解两个概念:进程组和会话周期。进程组:是一个或多个进程的集合。进程组由进程组ID唯一标识。除了进程号(PID)之外,进程组号也是进程的必要属性。每个进程组都有一个leader进程,进程号等于进程组ID。并且进程组ID不会因为组长进程的退出而受到影响。会话周期:会话周期是一个或多个进程组的集合。通常,一个会话从用户登录时开始,到用户注销时结束,在此期间用户运行的所有进程都属于这个会话。接下来可以具体介绍一下setsid的相关内容:setsid函数作用:setsid函数用于创建一个新的会话,并作为会话组的组长。调用setsid有以下三个作用:让进程摆脱原来会话的控制让进程摆脱原来进程组的控制让进程摆脱原来控制终端的控制那么,为什么要调用创建守护进程时的setsid函数?因为创建守护进程第一步调用fork函数创建子进程,然后退出父进程。因为在调用fork函数时,子进程完全复制了父进程的会话周期、进程组、控制终端等。虽然父进程退出了,但是会话周期、进程组、控制终端等都没有改变。并不是真正的独立,但是setsid函数可以让进程完全独立,从而摆脱其他进程的控制。将当前目录更改为根目录也是必要的步骤。使用fork创建的子进程继承父进程的当前工作目录。由于在进程运行过程中无法卸载当前目录所在的文件系统(如“/mnt/usb”),这会给以后的使用带来很多麻烦(比如系统需要进入single-出于某种原因的用户模式)。因此,通常的做法是使用“/”作为守护进程的当前工作目录,从而避免出现上述问题。当然,如果有特殊需要,也可以将当前工作目录更改为其他路径,比如/tmp。更改工作目录的常用函数chdir。重置文件权限掩码文件权限掩码是指屏蔽掉文件权限中的相应位。例如,一个文件权限掩码是050,它屏蔽了文件组所有者的可读和可执行权限。由于使用fork函数创建的子进程继承了父进程的文件权限掩码,这就给子进程使用文件带来了很大的麻烦。因此,将文件权限掩码设置为0可以大大增强守护进程的灵活性。设置文件权限掩码的函数是umask。这里,通常的使用方法是umask(0)。关闭文件描述符与文件权限代码相同。使用fork函数创建的子进程会继承父进程打开的一些文件。这些打开的文件可能永远不会被守护进程读取或写入,但它们仍然会消耗系统资源,并可能导致它们所在的文件系统无法挂载。在上面的第二步之后,守护进程与它所属的控制终端失去了联系。因此,终端输入的字符无法到达守护进程,守护进程中通过常规方式(如printf)输出的字符也无法在终端显示。因此,文件描述符为0、1、2的三个文件(通常称为输入、输出、错误)已经失去了存在价值,应该关闭。通常一个文件描述符是这样关闭的:[cpp]viewplaincopy====================================for(i=0;i0){//守护进程创建成功后,退出父进程exit(0);}if(-1===posix_setsid()){//让进程摆脱对原进程的控制thrownewException("setsidfail");}//再次fork避免SVR4系统重新获得终端的控制权。$pid=pcntl_fork();if(-1===$pid){thrownewException("forkfail");}elseif(0!==$pid){退出(0);}}