class SignalDemo { public $time_start; public $pid_childs = []; public $pid_childs_kill = []; public $config; public $master_pid; public $master_status = 1; public $str = '無'; public function __construct($config) { $this->config = $config; $this->time_start = date('Y-m-d H:i:s'); } public function run() { declare(ticks=1); //pcntl_signal_dispatch(); $this->master_pid = posix_getpid();//主id $this->clear();//清屏 $this->command();//指令 $this->installSignal();//安裝信號 //獲取多進程任務 $task_info = $this->config['task_info']; foreach ($task_info as $info) { $this->forkOneTask($info);//開啓子進程 } while ($this->master_status) { $this->printStr();//輸出信息 //守護子進程,如下是模擬子進程結束 foreach ($this->pid_childs as $k => $pid) { posix_kill($pid, SIGUSR1); pcntl_waitpid($pid, $status); unset($this->pid_childs[$k]); $this->str = '主進程把子進程:' . $pid . '殺掉了'; $this->printStr(); sleep(20); } $this->str = '無'; sleep(1); } echo '您按了 ctrl+c,主進程結束~~~'; } //開啓一個進程 public function forkOneTask($info) { $pid = pcntl_fork(); if ($pid === -1) { echo 'error'; exit; } if ($pid) { $this->pid_childs[] = $pid; } else { //子進程 while (true) { //作你想作的事。。。。。 sleep(2); if (is_array($this->pid_childs_kill) && in_array(posix_getpid(), $this->pid_childs_kill)) { exit(); } } } } //安裝信號 public function installSignal($handler = 'signalHandler') { pcntl_signal(SIGINT, array($this, $handler)); pcntl_signal(SIGHUP, array($this, $handler)); pcntl_signal(SIGUSR1, array($this, $handler)); } //收到信號回調 public function signalHandler($signal) { switch ($signal) { case SIGINT: if (posix_getpid() == $this->master_pid) { $this->master_status = 0; //設置主進程完畢 } else { //子進程邏輯 $this->pid_childs_kill[] = posix_getpid(); } break; case SIGUSR1: $this->pid_childs_kill[] = posix_getpid(); break; } } //運行指令 public function command() { // 檢查運行命令的參數 global $argv; $start_file = $argv[0]; // 命令 $command = isset($argv[1]) ? trim($argv[1]) : 'start'; // 進程號 $pid = isset($argv[2]) ? $argv[2] : ''; // 根據命令作相應處理 switch ($command) { case 'start': break; case 'stop': exec("ps aux | grep $start_file | grep -v grep | awk '{print $2}'", $info); if (count($info) <= 1) { echo " [$start_file] not run\n"; } else { echo "[$start_file] stop success"; exec("ps aux | grep $start_file | grep -v grep | awk '{print $2}' |xargs kill -SIGINT", $info); } exit; break; case 'stop-pid': echo "[$start_file] stop pid {$pid}"; exec("kill {$pid} -SIGINT"); exit; break; case 'kill': exec("ps aux | grep $start_file | grep -v grep | awk '{print $2}' |xargs kill -SIGKILL"); break; case 'kill-pid': exec("kill {$pid} -SIGKILL"); exit; break; case 'status': exit(0); // 未知命令 default : exit("Usage: php yourfile.php {start|stop|kill}\n"); } } //清屏 public function clear() { $arr = array(27, 91, 72, 27, 91, 50, 74); foreach ($arr as $a) { echo chr($a); } //array_map(create_function('$a', 'print chr($a);'), array(27, 91, 72, 27, 91, 50, 74)); } //系統負載 public function getSysLoad() { $loadavg = sys_getloadavg(); foreach ($loadavg as $k => $v) { $loadavg[$k] = round($v, 2); } return implode(", ", $loadavg); } //打印到屏幕 public function printStr() { $display_str = ''; $display_str .= "-----------------------<white> PHP多進程與信號模擬操做 </white>-------------------" . PHP_EOL; $display_str .= '開始時間:' . $this->time_start . PHP_EOL; $display_str .= "如今時間:" . date('Y-m-d H:i:s') . PHP_EOL; $display_str .= 'Load average: ' . $this->getSysLoad() . PHP_EOL; $display_str .= "PHP version:<purple>" . PHP_VERSION . "</purple>" . PHP_EOL; $display_str .= "當前子進程數: <red>" . count($this->pid_childs) . "個,PID:(" . implode(',', $this->pid_childs) . ")</red>" . PHP_EOL; $display_str .= "當前主進程PID: <red>" . posix_getpid() . "</red>" . PHP_EOL; $display_str .= "通知: <red>" . $this->str . "</red>" . PHP_EOL; $display_str .= "-----------------------<green> By:DuZhenxun </green>--------------------------" . PHP_EOL; $display_str .= "<yellow>Press Ctrl+C to quit.</yellow>" . PHP_EOL; $display_str = $this->clearLine($this->replaceStr($display_str));//替換文字,清屏 echo $display_str; } //文字替換 public function replaceStr($str) { $line = "\033[1A\n\033[K"; $white = "\033[47;30m"; $green = "\033[32;40m"; $yellow = "\033[33;40m"; $red = "\033[31;40m"; $purple = "\033[35;40m"; $end = "\033[0m"; $str = str_replace(array('<n>', '<white>', '<green>', '<yellow>', '<red>', '<purple>'), array($line, $white, $green, $yellow, $red, $purple), $str); $str = str_replace(array('</n>', '</white>', '</green>', '</yellow>', '</red>', '</purple>'), $end, $str); return $str; } //shell 替換顯示 function clearLine($message, $force_clear_lines = NULL) { static $last_lines = 0; if (!is_null($force_clear_lines)) { $last_lines = $force_clear_lines; } // 獲取終端寬度 $toss = $status = null; $term_width = exec('tput cols', $toss, $status); if ($status || empty($term_width)) { $term_width = 64; // Arbitrary fall-back term width. } $line_count = 0; foreach (explode("\n", $message) as $line) { $line_count += count(str_split($line, $term_width)); } // Erasure MAGIC: Clear as many lines as the last output had. for ($i = 0; $i < $last_lines; $i++) { echo "\r\033[K\033[1A\r\033[K\r"; } $last_lines = $line_count; return $message . "\n"; } } $config = []; $config['task_info'] = [ ['task_id' => 'a_1', 'info' => 'abcd'], ['task_id' => 'a_2', 'info' => '222'], ['task_id' => 'a_3', 'info' => '3333'], ['task_id' => 'a_4', 'info' => '3333'], ]; $obj = new SignalDemo($config); $obj->run();