孤儿进程:
在操作系统领域中,孤儿进程指的是在其父进程执行完成或被终止后仍继续运行的一类进程。<维基百科>
为避免孤儿进程退出时无法释放所占用的资源而僵死,孤儿进程一旦产生,将会立即由系统进程init收养。init的进程ID为1,因为被收养的孤儿进程的父进程ID更新为1.
孤儿进程组:
当一个终端控制进程(即会话首进程)终止后,那么这个终端可以用来建立一个新的会话。这可能会产生一个问题,原来旧的会话(一个或者多个进程组的集合)中的任一进程可再次访问这个的终端。为了防止这类问题的产生,于是就有了孤儿进程组的概念。当一个进程组成为孤儿进程组时,posix.1要求向孤儿进程组中处于停止状态的进程发送SIGHUP(挂起)信号,系统对于这种信号的默认处理是终止进程,然而如果无视这个信号或者另行处理的话那么这个挂起进程仍可以继续执行。
孤儿进程的应用:
刻意使进程成为孤儿进程,使之与用户会话脱钩,并转至后台运行。这就是守护进程。
Daemon守护进程:
daemon在英文中是“精灵”的意思,因此也有人称守护进程为精灵进程。daemon进程是后台服务进程,寿命很长,从开始到系统关闭,一直在运行。几乎所有的服务器程序,包括我们熟知的Apache和wu-FTP,都用daemon进程的形式实现。很多Linux下常见的命令如inetd和ftpd,末尾的字母d就是指daemon。
为什么一定要使用daemon进程呢?Linux中每一个系统与用户进行交流的界面称为终端(terminal),每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端(Controlling terminal),当控制终端被关闭时,相应的进程都会被自动关闭。但是daemon进程却能够突破这种限制,即使对应的终端关闭,它也能在系统中长久地存在下去,如果我们想让某个进程长命百岁,不因为用户或终端或其他的变化而受到影响,就必须把这个进程变成一个daemon进程。
Daemon进程的编程规则
如果想把自己的进程变成daemon进程,我们必须严格按照以下步骤进行:
1. 调用fork产生一个子进程,同时父进程退出。我们所有后续工作都在子进程中完成。这样做我们可以:
- 如果我们是从命令行执行的该程序,这可以造成程序执行完毕的假象,shell会回去等待下一条命令; 刚刚通过fork产生的新进程一定不会是一个进程组的组长,这为第2步的执行提供了前提保障。
- 这样做还会出现一种很有趣的现象:由于父进程已经先于子进程退出,会造成子进程没有父进程,变成一个孤儿进程(orphan)。每当系统发现一个孤儿进程,就会自动由1号进程(init)收养它,这样,原先的子进程就会变成1号进程的子进程。
2. 调用setsid系统调用。这是整个过程中最重要的一步。它的作用是创建一个新的会话(session),并自任该会话的组长(session leader)。如果调用进程是一个进程组的组长,调用就会失败,但这已经在第1步得到了保证。调用setsid有3个作用:
- 让进程摆脱原会话的控制;
- 让进程摆脱原进程组的控制;
- 让进程摆脱原控制终端的控制;
总之,就是让调用进程完全独立出来,脱离所有其他进程的控制。
3. 禁止进程重新打开控制终端
现在,第一子进程已经成为五中断的会话组长,但他可以重新申请打开一个控制终端。为了避免第一子进程重新申请控制终端,使进程不再是会话组长:fork一个第二子进程,结束第一子进程,此时的第二子进程不是会话组长。
4. 把当前工作目录切换到根目录。
如果我们是在一个临时加载的文件系统上执行这个进程的,比如:/mnt/floppy/,该进程的当前工作目录就会是/mnt/floppy/。在整个进程运行期间该文件系统都无法被卸下(umount),而无论我们是否在使用这个文件系统,这会给我们带来很多不便。解决的方法是使用chdir系统调用把当前工作目录变为根目录,应该不会有人想把根目录卸下吧。
当然,在这一步里,如果有特殊的需要,我们也可以把当前工作目录换成其他的路径,比如/tmp。
5. 将文件权限掩码设为0。
这需要调用系统调用umask。每个进程都会从父进程那里继承一个文件权限掩码,当创建新文件时,这个掩码被用于设定文件的默认访问权限,屏蔽掉某些权限,如一般用户的写权限。当另一个进程用exec调用我们编写的daemon程序时,由于我们不知道那个进程的文件权限掩码是什么,这样在我们创建新文件时,就会带来一些麻烦。所以,我们应该重新设置文件权限掩码,我们可以设成任何我们想要的值,但一般情况下,大家都把它设为0,这样,它就不会屏蔽用户的任何操作。
如果你的应用程序根本就不涉及创建新文件或是文件访问权限的设定,你也完全可以把文件权限掩码一脚踢开,跳过这一步。
6. 关闭所有不需要的文件。
同文件权限掩码一样,我们的新进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不被我们的daemon进程读或写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。需要指出的是,文件描述符为0、1和2的三个文件(文件描述符的概念将在下一章介绍),也就是我们常说的输入、输出和报错这三个文件也需要被关闭。很可能不少读者会对此感到奇怪,难道我们不需要输入输出吗?但事实是,在上面的第2步后,我们的daemon进程已经与所属的控制终端失去了联系,我们从终端输入的字符不可能达到daemon进程,daemon进程用常规的方法(如printf)输出的字符也不可能在我们的终端上显示出来。所以这三个文件已经失去了存在的价值,也应该被关闭。
7. 处理SIGCHLD信号
处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影 响服务器进程的并发性能。在Linux下可以简单地将SIGCHLD信号的操作设为SIG_IGN。 signal(SIGCHLD,SIG_IGN);
代码如下:
class DaemonBase{ private $pid_file=""; private $pid_dir="/home/daisyfan/"; private $work_count=8; public function __construct($is_sington=false,$user='nobody',$output="/home/daisyfan/") { $this->is_sington = $is_sington; //是否单例运行,单例运行会在tmp目录下建立一个唯一的PID $this->user = $user;//设置运行的用户 默认情况下nobody $this->output = $output; //设置输出的地方 $this->checkPcntl(); } /** * 启动守护进程 */ public function daemonize(){ set_time_limit(0); //调用fork产生一个子进程,同时父进程退出 $pid=pcntl_fork(); if($pid==-1){ exit("fork子进程出错"); }else if($pid>0){ exit(0); //父进程退出 } posix_setsid();//将第一子进程设为会话首进程 $pid=pcntl_fork(); if($pid==-1){ exit("fork第二子进程出错"); }elseif($pid>0){ exit(0);//第一子进程退出 } chdir("/home/daisyfan/");//改变工作目录 umask(0);//把文件掩码清零 $this->setUser($this->user) or die("cannot change owner"); //关闭打开的文件描述符 fclose(STDIN); fclose(STDOUT); fclose(STDERR); $stdin = fopen($this->output, 'r'); $stdout = fopen($this->output, 'a'); $stderr = fopen($this->output, 'a'); if ($this->is_sington==true){ $this->createPidfile(); } } /** * 处理信号 */ public function checkPcntl(){ // PHP < 5.3 uses ticks to handle signals instead of pcntl_signal_dispatch // call sighandler only every 10 ticks if(!function_exists('pcntl_signal_dispatch')){ declare(ticks=10); } if(!function_exists('pcntl_signal')){ $message = 'PHP does not appear to be compiled with the PCNTL extension. This is neccesary for daemonization'; $this->_log($message); throw new Exception($message); } pcntl_signal(SIGTEM,array($this,'signalHandler'),false);//安装信号处理器 pcntl_signal(SIGINT,array($this,'signalHandler'),false); pcntl_signal(SIGQUIT,array($this,'signalHandler'),false); } //信号处理函数 public function signalHandler($signal){ switch($signal){ //用户自定义信号 case SIGUSR1: //busy if ($this->workers_count < $this->workers_max){ $pid = pcntl_fork(); if ($pid > 0){ $this->workers_count ++; } } break; //子进程结束信号 case SIGCHLD: while(($pid=pcntl_waitpid(-1, $status, WNOHANG)) > 0){ $this->workers_count --; } break; //中断进程 case SIGTERM: case SIGHUP: case SIGQUIT: break; default: return false; } } //检测pid file是否存在 public function checkPID(){ if(!file_exists($this->pid_file)){ return true; } $pid=file_get_contents($this->pid_file); $pid=intval($pid); if($pid>0 && posix_kill($pid,0) ){//posix_kill — Send a signal to a process $this->_log("the daemon process is already started"); }else{ $this->_log("the daemon process end abnormally, please check the pid file".$this->pid_file); } exit(1); } //创建pid file public function createPidfile(){ if(! is_dir($this->pid_dir)){ mkdir($this->pid_dir); } $fp=fopen($this->pid_file,'w') or exit('can not create pid file.'); fwrite($fp,posix_getpid()); fclose($fp); $this->_log("create pid file:".$this->pid_file); } public function setUser($name){ $result=false; if(empty($name)){ return true; } $user=posix_getpwnam($name);// Return info about a user by username if($user){ $uid=$user['uid']; $gid=$user['gid']; $result=posix_setuid($uid); posix_setgid($gid); } return $result; } public function start($count=1){ $this->_log("Daemon process is running now"); while(true){ if(function_exists('pcntl_signal_dispatch')){ pcntl_signal_dispatch(); } $pid=-1; if($this->$work_count<$count){ $pid=pcntl_fork(); } if($pid>0){ $this->work_count++; }elseif($pid==0){ // 这个符号表示恢复系统对信号的默认处理 pcntl_signal(SIGTERM, SIG_DFL); pcntl_signal(SIGCHLD, SIG_DFL); } } } }
相关推荐
守护进程在计算机系统中扮演着重要的角色,它们通常在后台运行,不与用户直接交互,负责执行特定的任务,如服务管理、日志记录、定时任务等。在本话题中,我们将深入探讨守护进程的原理、C/C++实现以及如何设计具有...
"Windows守护进程小工具"是一种实用程序,设计用于确保特定应用程序始终在Windows操作系统上运行。这个工具的核心功能是监控指定的程序,如果发现该程序未运行,它将自动启动该程序。这为用户提供了保障,确保关键...
《Delphi编写的守护进程详解》 在IT领域,守护进程(Daemon)是一种在后台运行且没有用户界面的程序,通常用于执行特定的任务,如监控系统状态或提供网络服务。在Windows操作系统中,我们可以利用Delphi这样的编程...
在IT行业中,守护进程(Daemon)通常是指在操作系统后台运行且独立于用户会话的长期服务程序。在Unix/Linux系统中,我们常看到各种守护进程,而在Windows系统中,我们可以用相似的概念来理解,比如服务(Service)。...
在IT领域,守护进程(Daemon)是一种在后台运行的特殊类型的服务程序,它们不与任何终端关联,通常在操作系统启动时启动,持续运行直到系统关闭。守护进程在各种服务、监控任务以及系统管理中扮演着重要角色。在...
本篇文章提供一个软件实现守护进程的办法,原理就是udp通信,单独写个守护进程程序,专门负责检测主程序是否存在,不存在则启动。主程序只需要启动live类监听端口,收到hello就回复ok就行。 为了使得兼容任意程序,...
在C#编程环境中,开发一个控制台应用作为守护进程(Daemon)是常见的需求,尤其在服务器端软件中,为了确保核心服务的持续运行,我们往往需要一个后台程序来监控并管理这些服务。在这个名为"C#控制台实现的一个简单...
《编写Linux守护进程》这篇文献主要探讨了如何在Linux操作系统中创建和管理守护进程,这是一种在后台运行且独立于控制终端的进程,常用于提供系统服务,如HTTP服务器、打印服务等。守护进程的主要特点包括: 1. **...
在Java编程环境中,实现守护进程(Daemon)以及创建多个进程和JVM是非常常见的需求,特别是在构建分布式系统或者需要持续后台运行的服务时。本篇将详细阐述如何使用Java来实现这些功能。 守护进程通常是一个在后台...
在Python编程中,守护进程(Daemon)是一种特殊类型的后台进程,它不依附于终端,且能在系统启动时启动,并在系统关闭时终止。守护进程常用于提供持续的服务,例如Web服务器、数据库服务等。本篇文章将深入探讨如何...
在Linux环境中,为了确保应用程序如Tomcat能够随系统自动启动并作为守护进程运行,需要进行一系列的配置步骤。本文将详细介绍如何将Tomcat添加到Linux的守护进程,并处理"Invalid user name 'tomcat' specified"的...
在计算机编程领域,特别是Windows操作系统环境下,守护进程(也称为后台服务)是一种长期运行、通常独立于用户交互之外的程序。它主要用于执行系统管理任务或提供某种持续性的功能支持。在VC++环境下创建守护进程,...
《使用Delphi构建守护进程(服务程序)》 在IT领域,守护进程和服务程序是操作系统中不可或缺的部分,它们常在后台运行,确保关键任务的稳定执行。本文将深入探讨如何利用Delphi编程语言来创建一个守护进程,该进程...
### Windows 下 Tomcat 的守护进程与心跳检测程序详解 在 IT 领域,特别是 Java Web 开发中,Apache Tomcat 是一个非常重要的轻量级应用服务器,它主要用于部署 Java Web 应用程序。在实际生产环境中,为了保证服务...
守护进程(daemon)是Linux和Unix-like操作系统中的一个重要概念,它们是后台运行的程序,不与任何终端直接关联,主要用于提供系统服务,如网络服务、日志记录、打印队列等。守护进程在系统启动时启动,持续运行直到...
在IT领域,守护进程(Daemon)是一种在后台运行且没有控制终端的进程,通常用于提供系统服务,如网络服务、日志记录等。守护进程在操作系统中扮演着重要的角色,因为它们可以持续运行,即使没有用户登录也能提供必要...
### 如何编写守护进程 守护进程(Daemon Process)是一种在后台持续运行的进程,它不依附于任何用户终端并且独立于控制台之外。守护进程主要用于执行特定的任务或提供服务,例如日志管理、定时任务处理等。本文将...
### 后台进程与守护进程的区别 在深入探讨后台进程与守护进程的区别之前,我们先来明确一下两者的基本概念。 #### 后台进程 后台进程(Background Process)是指那些在操作系统后台默默工作,无需用户直接干预的...
### Linux系统下守护进程编程方法 #### 一、守护进程概览 守护进程(Daemons)是Linux系统中一类特殊的服务程序,它们通常在后台运行,没有控制终端,也不依赖于任何登录Shell。守护进程的设计目标是高可靠性,...