多数大学生出来选择的工作和专业无关
首页 > 专业知识

使用PHP脚本来写Daemon程序

时间:2018-02-03 16:33:29 [来源]:郑州PHP培训学校

   使用PHP脚本来写Daemon程序

  为什么一定要使用daemon进程呢?Linux中每一个系统与用户进行交流的界面称为终端(terminal),每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端(Controlling terminal),当控制终端被关闭时,相应的进程都会被自动关闭。
 
  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 让进程摆脱原控制终端的控制;
  总之,就是让调用进程完全独立出来,脱离所有其他进程的控制。
  使用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 信号,也会重新启动。
  代码如下:
  <?php
  declare( ticks = 1 );
  // This case will check the config file regularly, if the config file changed, it will restart it self// If you want to restart the daemon gracefully, give it a HUP signal// by shiqiang<cocowool@gmail.com> at 2011-12-04$init_md5 = md5_file( 'config.php');
  // register signal handler
  pcntl_signal( SIGALRM, "signal_handler", true );pcntl_signal( SIGHUP, 'signal_handler', TRUE );$job_flag = FALSE;    //Job status flag, to justify if the job has been finished$signal_flag = FALSE;    //Signal status flag, to justify whether we received the kill -1 signalwhile( 1 ){
  $job_flag = FALSE;    //Job status flag
  print "Worker start running ... \n";
  sleep(5);
  print "Worker's task done ... \n";
  $flag = TRUE;    //Job status flag
  AutoStart( $signal_flag );
  }
  function signal_handler( $signal ) {
  global $job_flag;
  global $signal_flag;
  switch( $signal ){
  case SIGQUIT:
  print date('y-m-d H:i:s', time() ) . " Caught Signal : SIGQUIT - No : $signal \n";exit(0);
  break;
  case SIGSTOP:
  print date('y-m-d H:i:s', time() ) . " Caught Signal : SIGSTOP - No : $signal \n";break;
  case SIGHUP:
  print date('y-m-d H:i:s', time() ) . " Caught Signal : SIGHUP - No : $signal \n";if( $flag === TRUE ){
  AutoStart( TRUE );
  }else{
  $signal_flag = TRUE;
  }
  break;
  case SIGALRM:
  print date('y-m-d H:i:s', time() ) . " Caught Signal : SIGALRM - No : $signal \n";//pcntl_exec( '/bin/ls' );
  pcntl_alarm( 5 );
  break;
  default:
  break;
  }
  }
 
  使用qpm创建daemon程序
  Daemon程序又称为守护进程,特点是在后台持续运行,不与控制台、GUI交互。PHP中,Daemon 程序是以CLI模式运行的,与我们通常做网页开发的CGI模式有所不同。PHP CGI 程序的生命周期通常是一个HTTP请求,守护进程则是长期运行的。
  例如 foo.php
  <?php
  while(true) {
  file_put_contents('foo.log', date('Y-m-d H:i:s')."\n", FILE_APPEND);sleep(10);
  };
  ?>
  以上程序就是一个长期运行的例子,每隔10秒,程序会向foo.log文件追加一行时间信息。在CGI模式下,进程会收到PHP最大运行时间的限制,超时后自动退出,在CLI模式下,程序则持续运行,直到接收到终止信号。
  作为Daemon程序,如果从控制台启动,如果不做特殊处理,控制台关闭会导致程序推出,因此,通常的做法是:
  1、启动脚本; 2、复制进程(fork); 3、子进程转入后台运行; 4、父进程退出,子进程继续在后台运行。
  QPM 是一个PHP 进程管理框架,可用于简化Daemon开发,项目地址在:https://github.com/Comos/qpm使用QPM实现如下(例程见:qpm_simple_daemon.php)use qpm\process\Process as Process;
  //实际的工作内容
  function work() {
  while(true) {
  file_put_contents(__FILE__'.log', date('Y-m-d H:i:s')."\n", FILE_APPEND);sleep(10);
  };
  }
  //通过回调启动子进程
  Process::current()->forkByCallable(
  function() {
  //子进程将自己转入后台
  Process::current()->toBackground();
  work();
  }
  );
 

上一篇:国外5大最流行PHP框架排名

下一篇:PHP为什么优于Node.js