当前位置:  开发笔记 > 编程语言 > 正文

PHP脚本来写Daemon程序

这又是一个有趣的概念,daemon在英语中是"精灵"的意思,就像我们经常在迪斯尼动画里见到的那些,有些会飞,有些不会,经常围着动画片的主人公转来转去,啰里啰唆地提一些忠告,时不时倒霉地撞在柱子上,有时...">

  这又是一个有趣的概念,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进程。

  1. Daemon进程的编程规则

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

  1、调用fork产生一个子进程,同时父进程退出。我们所有后续工作都在子进程中完成。这样做我们可以:

  1.1 如果我们是从命令行执行的该程序,这可以造成程序执行完毕的假象,shell会回去等待下一条命令;

  1.2 刚刚通过fork产生的新进程一定不会是一个进程组的组长,这为第2步的执行提供了前提保障。

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

  2、调用setsid系统调用。

  这是整个过程中最重要的一步。setsid的介绍见附录2,它的作用是创建一个新的会话(session),并自任该会话的组长(session leader)。如果调用进程是一个进程组的组长,调用就会失败,但这已经在第1步得到了保证。调用setsid有3个作用:

  2.1 让进程摆脱原会话的控制;

  2.2 让进程摆脱原进程组的控制;

  2.3 让进程摆脱原控制终端的控制;

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

  3、把当前工作目录切换到根目录。

  如果我们是在一个临时加载的文件系统上执行这个进程的,比如:/mnt/floppy/,该进程的当前工作目录就会是/mnt/floppy/。在整个进程运行期间该文件系统都无法被卸下(umount),而无论我们是否在使用这个文件系统,这会给我们带来很多不便。解决的方法是使用chdir系统调用把当前工作目录变为根目录,应该不会有人想把根目录卸下吧。

  关于chdir的用法,参见附录1。

  当然,在这一步里,如果有特殊的需要,我们也可以把当前工作目录换成其他的路径,比如/tmp。

  4、将文件权限掩码设为0。

  这需要调用系统调用umask,参见附录3。每个进程都会从父进程那里继承一个文件权限掩码,当创建新文件时,这个掩码被用于设定文件的默认访问权限,屏蔽掉某些权限,如一般用户的写权限。当另一个进程用exec调用我们编写的daemon程序时,由于我们不知道那个进程的文件权限掩码是什么,这样在我们创建新文件时,就会带来一些麻烦。所以,我们应该重新设置文件权限掩码,我们可以设成任何我们想要的值,但一般情况下,大家都把它设为0,这样,它就不会屏蔽用户的任何操作。

  如果你的应用程序根本就不涉及创建新文件或是文件访问权限的设定,你也完全可以把文件权限掩码一脚踢开,跳过这一步。

  5、关闭所有不需要的文件。

  同文件权限掩码一样,我们的新进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不被我们的daemon进程读或写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。需要指出的是,文件描述符为0、1和2的三个文件(文件描述符的概念将在下一章介绍),也就是我们常说的输入、输出和报错这三个文件也需要被关闭。很可能不少读者会对此感到奇怪,难道我们不需要输入输出吗?但事实是,在上面的第2步后,我们的 daemon进程已经与所属的控制终端失去了联系,我们从终端输入的字符不可能达到daemon进程,daemon进程用常规的方法(如printf)输出的字符也不可能在我们的终端上显示出来。所以这三个文件已经失去了存在的价值,也应该被关闭。

  使用PHP编写Gearman的Worker守护进程

  在我之前的文章中,介绍过Gearman的使用。在我的项目中,我使用了PHP来编写一直运行的Worker。如果按照Gearman官方推荐的例子,只是简单的一个循环来等待任务,会有一些问题,包括:1、当代码进行过修改之后,如何让代码的修改生效;2、重启Worker的时候,如何保证当前的任务处理完成才重启。

  针对这个问题,我考虑了以下的解决方法:

  1、每次修改完代码后,Worker需要手工重启(先杀死然后启动)。这个只能解决重新加载配置文件的问题。

  2、在Worker中设置,单次任务循环完成后,就对Worker进行重启。这个方案的问题在于消耗比较大。

  3、在Worker中添加一个退出函数,如果需要Worker退出的时候,在Client端发送一个优先级比较高的退出调用。这个需要客户端配合,在使用后台类任务时,不太适合。

  4、在Worker中检查文件是否发生变化,如果发生了变化,退出并重启自身。

  5、为Worker编写信号控制,接受重启指令,类似于 http restart graceful 指令。

  最后,结合4和5两种方法,可以实现这样一个Daemon,如果配置文件发生了变化,他就会自动重启;如果接受到了用户的 kill -1 pid 信号,也会重新启动。

  代码如下:

  1.  
  2. declare( ticks = 1 );  
  3. // This case will check the config file regularly, if the config file changed, it will restart it self  
  4. // If you want to restart the daemon gracefully, give it a HUP signal  
  5. // by shiqiang<cocowool@gmail.com> at 2011-12-04  
  6.  
  7. $init_md5 = md5_file( &#39;config.php&#39;);  
  8.  
  9. // register signal handler  
  10. pcntl_signal( SIGALRM, "signal_handler", true );  
  11. pcntl_signal( SIGHUP, &#39;signal_handler&#39;, TRUE );  
  12.  
  13. $job_flag = FALSE;   
  14.  //Job status flag, to justify if the job has been finished  
  15. $signal_flag = FALSE;   
  16.  //Signal status flag, to justify whether we received the kill -1 signal  
  17.  
  18. while( 1 ){  
  19.     $job_flag = FALSE;   
  20.  //Job status flag  
  21.     print "Worker start running ... \n";  
  22.     sleep(5);  
  23.     print "Worker&#39;s task done ... \n";  
  24.     $flag = TRUE;  
  25.   //Job status flag  
  26.     AutoStart( $signal_flag );  
  27. }  
  28.  
  29. function signal_handler( $signal ) {  
  30.     global $job_flag;  
  31.     global $signal_flag;  
  32.  
  33.     switch( $signal ){  
  34.         case SIGQUIT:  
  35.             print date(&#39;y-m-d H:i:s&#39;, time() ) . " Caught Signal : SIGQUIT - No : $signal \n";  
  36.             exit(0);  
  37.             break;  
  38.         case SIGSTOP:  
  39.             print date(&#39;y-m-d H:i:s&#39;, time() ) . " Caught Signal : SIGSTOP - No : $signal \n";  
  40.             break;  
  41.         case SIGHUP:  
  42.             print date(&#39;y-m-d H:i:s&#39;, time() ) . " Caught Signal : SIGHUP - No : $signal \n";  
  43.             if( $flag === TRUE ){  
  44.                 AutoStart( TRUE );  
  45.             }else{  
  46.                 $signal_flag = TRUE;  
  47.             }  
  48.             break;  
  49.         case SIGALRM:  
  50.             print date(&#39;y-m-d H:i:s&#39;, time() ) . " Caught Signal : SIGALRM - No : $signal \n";  
  51.             //pcntl_exec( &#39;/bin/ls&#39; );  
  52.             pcntl_alarm( 5 );  
  53.             break;  
  54.         default:  
  55.             break;  
  56.     }  
  57. }  
  58.  
  59. function AutoStart( $signal = FALSE, $filename = &#39;config.php&#39; ){  
  60.     global $init_md5;  
  61.  
  62.     if( $signal || md5_file( $filename ) != $init_md5 ){  
  63.         print "The config file has been changed, we are going to restart. \n";  
  64.         $pid = pcntl_fork();  
  65.         if( $pid == -1 ){  
  66.             print "Fork error \n";  
  67.         }else if( $pid > 0 ){  
  68.             print "Parent exit \n";  
  69.             exit(0);  
  70.         }else{  
  71.             $init_md5 = md5_file( $filename );  
  72.             print "Child continue to run \n";  
  73.         }  
  74.     }  
  75. }

推荐阅读
  • 在Linux环境下,本文详细探讨了Apache服务器中CGI技术的应用与实现。首先,通过使用yum包管理器安装了必要的软件,如PHP。安装完成后,对Apache服务器进行了配置,确保CGI功能正常运行。此外,还介绍了如何编写和调试CGI脚本,以及如何在实际环境中部署这些脚本以提供动态网页内容。实验结果表明,通过合理的配置和优化,Apache服务器能够高效地支持CGI应用程序,为用户提供丰富的交互体验。 ... [详细]
  • 在《PHP应用性能优化实战指南:从理论到实践的全面解析》一文中,作者分享了一次实际的PHP应用优化经验。文章回顾了先前进行的一次优化项目,指出即使系统运行时间较长后出现的各种问题和性能瓶颈,通过采用一些通用的优化策略仍然能够有效解决。文中不仅详细阐述了优化的具体步骤和方法,还结合实例分析了优化前后的性能对比,为读者提供了宝贵的参考和借鉴。 ... [详细]
  • 本文首先对信息漏洞的基础知识进行了概述,重点介绍了几种常见的信息泄露途径。具体包括目录遍历、PHPINFO信息泄露以及备份文件的不当下载。其中,备份文件下载涉及网站源代码、`.bak`文件、Vim缓存文件和`DS_Store`文件等。目录遍历漏洞的详细分析为后续深入研究奠定了基础。 ... [详细]
  • 如何将PHP文件上传至服务器及正确配置服务器地址 ... [详细]
  • 本文深入探讨了IO复用技术的原理与实现,重点分析了其在解决C10K问题中的关键作用。IO复用技术允许单个进程同时管理多个IO对象,如文件、套接字和管道等,通过系统调用如`select`、`poll`和`epoll`,高效地处理大量并发连接。文章详细介绍了这些技术的工作机制,并结合实际案例,展示了它们在高并发场景下的应用效果。 ... [详细]
  • ActiveMQ是由Apache开发的一款广受欢迎且功能强大的开源消息中间件。作为完全符合JMS 1.1和J2EE 1.4规范的JMS Provider实现,尽管JMS规范已问世多年,但ActiveMQ依然保持了其在消息队列领域的领先地位。本文将带你初步了解ActiveMQ的核心概念及其应用场景,帮助你快速入门这一重要的消息传递技术。 ... [详细]
  • NoSQL数据库,即非关系型数据库,有时也被称作Not Only SQL,是一种区别于传统关系型数据库的管理系统。这类数据库设计用于处理大规模、高并发的数据存储与查询需求,特别适用于需要快速读写大量非结构化或半结构化数据的应用场景。NoSQL数据库通过牺牲部分一致性来换取更高的可扩展性和性能,支持分布式部署,能够有效应对互联网时代的海量数据挑战。 ... [详细]
  • 如何在Linux系统中利用crontab定时执行Shell脚本任务?
    在Linux系统中,如何实现定时执行任务脚本?在服务器日常运维过程中,经常需要定期执行某些任务,例如数据库备份、日志文件切割等。通过使用crontab工具,可以轻松配置这些周期性任务,确保它们按时自动运行,提高系统管理效率和可靠性。 ... [详细]
  • 本文深入解析了 Apache 配置文件 `httpd.conf` 和 `.htaccess` 的优化方法,探讨了如何通过合理配置提升服务器性能和安全性。文章详细介绍了这两个文件的关键参数及其作用,并提供了实际应用中的最佳实践,帮助读者更好地理解和运用 Apache 配置。 ... [详细]
  • 在Linux系统中设置网络路由转发的方法详解。本文介绍了如何配置服务器以实现外网访问特定端口时,将流量转发到其他外网端口。具体步骤包括配置服务器的内网IP地址(例如192.168.1.1),并调整相关的网络参数,确保路由转发功能正常工作。此外,还提供了详细的命令示例和配置文件修改方法,帮助用户顺利完成设置。 ... [详细]
  • Linux磁盘管理入门指南:MBR分区格式详解与安装步骤
    在 CentOS 7.x 环境下,本文详细介绍了 MBR 分区格式的基本概念及其安装步骤。实验中使用了 SAS 和 SATA 硬盘,其中 SAS 硬盘主要用于企业级应用和服务器,而 SATA 硬盘则广泛应用于个人计算机和低端服务器。文章通过具体操作示例,帮助读者更好地理解和掌握 Linux 磁盘管理的基本技能。 ... [详细]
  • 技术日志:深入探讨Spark Streaming与Spark SQL的融合应用
    技术日志:深入探讨Spark Streaming与Spark SQL的融合应用 ... [详细]
  • 在Linux系统中,原本已安装了多个版本的Python 2,并且还安装了Anaconda,其中包含了Python 3。本文详细介绍了如何通过配置环境变量,使系统默认使用指定版本的Python,以便在不同版本之间轻松切换。此外,文章还提供了具体的实践步骤和注意事项,帮助用户高效地管理和使用不同版本的Python环境。 ... [详细]
  • 如何利用Apache与Nginx高效实现动静态内容分离
    如何利用Apache与Nginx高效实现动静态内容分离 ... [详细]
  • 深入解析Wget CVE-2016-4971漏洞的利用方法与安全防范措施
    ### 摘要Wget 是一个广泛使用的命令行工具,用于从 Web 服务器下载文件。CVE-2016-4971 漏洞涉及 Wget 在处理特定 HTTP 响应头时的缺陷,可能导致远程代码执行。本文详细分析了该漏洞的成因、利用方法以及相应的安全防范措施,包括更新 Wget 版本、配置防火墙规则和使用安全的 HTTP 头。通过这些措施,可以有效防止潜在的安全威胁。 ... [详细]
author-avatar
sdfsadfwforever
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有