public function fork(Process $children, $user_name = null, $group_name = null) { if ($this->allocateSHMPerChildren()) { $children->setSHMSegment(new SHMCache(uniqid('process_manager;shm_per_children' . $children->getInternalId()), $this->allocateSHMPerChildren)); } pcntl_sigprocmask(SIG_BLOCK, array(SIGCHLD)); $pid = pcntl_fork(); // Error if ($pid == -1) { throw new RuntimeException('pcntl_fork() returned -1, are you sure you are running the script from CLI?'); } else { if (!$pid) { if (!is_null($group_name)) { if (!$this->setGroup($group_name)) { throw new RuntimeException('set group_name failed. are you sure you have the privileges?'); } } if (!is_null($user_name)) { if (!$this->setUser($user_name)) { throw new RuntimeException('set user_name failed. are you sure you have the privileges?'); } } $children->run(); exit; // redundant, added only for clarity } else { $children->setStarted(true); $this->children[] = $children; // Store the children's PID $children->setPid($pid); pcntl_sigprocmask(SIG_UNBLOCK, array(SIGCHLD)); } } }
/** * Create a ParallelParentCollector that will collect * issues via a message queue. You'll want to do the * real collection via * \Phan\Output\Collector\ParallelChildCollector. * * @param IssueCollectorInterface $base_collector * A collector must be given to which collected issues * will be passed */ public function __construct(IssueCollectorInterface $base_collector) { assert(extension_loaded('sysvsem'), 'PHP must be compiled with --enable-sysvsem in order to use -j(>=2).'); assert(extension_loaded('sysvmsg'), 'PHP must be compiled with --enable-sysvmsg in order to use -j(>=2).'); $this->base_collector = $base_collector; // Create a message queue for this process group $message_queue_key = posix_getpgid(posix_getpid()); $this->message_queue_resource = msg_get_queue($message_queue_key); // Listen for ALARMS that indicate we should flush // the queue pcntl_sigprocmask(SIG_UNBLOCK, array(SIGUSR1), $old); pcntl_signal(SIGUSR1, function () { $this->readQueuedIssues(); }); }
private function configureSignals() { if (extension_loaded('pcntl')) { if (!defined('AMQP_WITHOUT_SIGNALS')) { define('AMQP_WITHOUT_SIGNALS', false); } pcntl_signal(SIGUSR1, [&$this, 'signalHandler']); pcntl_signal(SIGTERM, [&$this, 'signalHandler']); pcntl_signal(SIGHUP, [&$this, 'signalHandler']); pcntl_signal(SIGINT, [&$this, 'signalHandler']); pcntl_signal(SIGQUIT, [&$this, 'signalHandler']); pcntl_signal(SIGUSR2, [&$this, 'signalHandler']); pcntl_signal(SIGALRM, [&$this, 'alarmHandler']); pcntl_sigprocmask(SIG_BLOCK, array(SIGHUP)); } }
/** * ワーカーの後処理 */ public function fin() { if ($this->_sigset) { // シグナルマスクを解除 pcntl_sigprocmask(SIG_UNBLOCK, $this->_sigset); $this->_sigset = null; } if ($this->_pidlock) { // PIDファイルのアンロック $this->_pidlock->release(); $this->_pidlock = null; // ログ $pid = posix_getpid(); $this->_log("exit worker [{$pid}]"); } }
function watchdog() { $childpid = 0; $signals = array(SIGINT, SIGCHLD); pcntl_sigprocmask(SIG_BLOCK, $signals); do { printf("%s watchdog launching daemon...\n", @date('r')); if (0 === ($childpid = pcntl_fork())) { unreliable_daemon(); exit(1); } $signal = pcntl_sigwaitinfo($signals, $info); } while ($signal != SIGINT); echo "watchdog got SIGINT. killing child...\n"; posix_kill($childpid, SIGKILL); echo "watchdog exiting...\n"; }
/** * Creates a new fork of the process and sets all related properties * * @throws UnableToForkException * @return int|false pid of the new process for the parent, false for the child */ public function fork() { pcntl_sigprocmask(SIG_BLOCK, [SIGCHLD]); $pid = pcntl_fork(); if ($pid === -1) { throw new UnableToForkException(); } else { if ($pid) { $this->isParent = true; $this->children[$pid] = $pid; $this->childrenCount++; pcntl_sigprocmask(SIG_UNBLOCK, [SIGCHLD]); return $pid; } else { $this->isParent = false; $this->children = []; $this->childrenCount = 0; return false; } } }
/** * 运行守护进程,监控有变化的日志文件。 * * @see ZtChart_Model_Monitor_Abstract::daemon() */ public function run() { if (0 == ($pid = pcntl_fork())) { // 子进程负责处理当前日志 try { $this->tail($this->_console->getLogPaths()); } catch (ZtChart_Model_Monitor_Exception $e) { $this->_logger->err($e->getMessage()); exit(1); } } else { if (0 < $pid) { pcntl_setpriority(-1); // 父进程负责把子进产生的数据写入数据库 if (pcntl_sigprocmask(SIG_BLOCK, array(SIGCHLD, SIGALRM, SIGINT, SIGTERM))) { $interval = 10; // 10秒写一次 pcntl_alarm($interval); while ($signo = pcntl_sigwaitinfo(array(SIGCHLD, SIGALRM, SIGINT, SIGTERM))) { if (SIGALRM == $signo) { pcntl_alarm($interval); } try { $this->extract($interval); } catch (ZtChart_Model_Monitor_Exception $e) { posix_kill($pid, 9); exit(1); } if (SIGCHLD == $signo || SIGINT == $signo || SIGTERM == $signo) { break; } } } } } exit(0); }
public function fork(Process $children) { if ($this->allocateSHMPerChildren()) { $children->setSHMSegment(new \Zebra\Ipcs\SHMCache(\uniqid('process_manager;shm_per_children' . $children->getInternalId()), $this->allocateSHMPerChildren)); } \pcntl_sigprocmask(SIG_BLOCK, array(SIGCHLD)); $pid = \pcntl_fork(); // Error if ($pid == -1) { throw new RuntimeException('pcntl_fork() returned -1, are you sure you are running the script from CLI?'); } else { if (!$pid) { $children->run(); exit; // redundant, added only for clarity } else { $children->setStarted(true); $this->children[] = $children; // Store the children's PID $children->setPid($pid); \pcntl_sigprocmask(SIG_UNBLOCK, array(SIGCHLD)); } } }
function server_loop() { $SERVER =& $GLOBALS['SERVER']; $CONF =& $GLOBALS['CONF']; $CHILDRENS =& $GLOBALS['CHILDRENS']; // create perfork children loop $trymask = 0; while ($GLOBALS['ALLOW_RUN'] == true) { // does allow fork children? if ($GLOBALS['ALLOW_FORK'] == false) { continue; } //create $idlecount = utils_mem_idle_get(); for ($ice = $idlecount; $ice < $CONF['daemon']['max_idle_servers']; $ice++) { // max of connections $children_list_count = count($CHILDRENS); if ($children_list_count > $CONF['daemon']['max_connections']) { utils_message('[' . __FUNCTION__ . ']: Connection max of limits!', 0, $SERVER['runmode'], $SERVER['output_level']); break; } //block all sig before fork children pcntl_sigprocmask(SIG_BLOCK, array(SIGTERM, SIGINT, SIGCHLD, SIGHUP)); // current $pid = pcntl_fork(); if ($pid == -1) { utils_message('[' . __FUNCTION__ . ']: Children fork failure!', 0, $SERVER['runmode'], $SERVER['output_level']); die; } if ($pid == 0) { //in child //in child signal must default and unlock in children pcntl_signal(SIGTERM, SIG_DFL); pcntl_signal(SIGINT, SIG_DFL); pcntl_signal(SIGCHLD, SIG_DFL); pcntl_signal(SIGHUP, SIG_DFL); pcntl_sigprocmask(SIG_UNBLOCK, array(SIGTERM, SIGINT, SIGCHLD, SIGHUP)); //run children work server_children_work(); exit; } else { //in parent utils_message('[' . __FUNCTION__ . ']: children ' . $pid . ' created!', 4, $SERVER['runmode'], $SERVER['output_level']); //save memory utils_mem_idle_inc(); //recored childrens $CHILDRENS[$pid] = null; // unblock pcntl_sigprocmask(SIG_UNBLOCK, array(SIGTERM, SIGINT, SIGCHLD, SIGHUP)); } } usleep(10000); } }
/** * Get system-specific signal handler * * @return callable */ protected function getSignalHandler() { return function (SignalHandler $handler, $signal) { if (SIGKILL != $signal) { //do not redefine SIGKILL, for consistency pcntl_sigprocmask(SIG_UNBLOCK, [$signal]); if (!pcntl_signal($signal, [$handler, 'handle'])) { $this->logger->error('Failed to register system signal ' . $signal . ': ' . pcntl_strerror(pcntl_get_last_error())); } else { $this->logger->info('Registered system signal: ' . $signal); } } }; }
private function become_worker() { $this->log_method_call(__FUNCTION__); $pid = $this->fork_process(); if ($pid === 0) { $this->worker_pid = posix_getpid(); // The child process ignores SIGINT (small race here) pcntl_sigprocmask(SIG_BLOCK, array(SIGINT)); // and lets go of the parent's libevent base event_base_reinit($this->event_base); // and breaks out of the service event loop event_base_loopbreak($this->event_base); // and dismantles the whole event structure foreach ($this->events as $i => $event) { event_del($event); event_free($event); unset($this->events[$i]); } $bufferevents = array_merge($this->offers_accepted, $this->offers_waiting, $this->offers_written, $this->requests_accepted, $this->requests_buffered, $this->requests_written, $this->requests_working, $this->responses_accepted, $this->responses_buffering); foreach ($bufferevents as $event) { event_buffer_disable($event, EV_READ | EV_WRITE); event_buffer_free($event); } event_base_free($this->event_base); unset($this->event_base); $this->worker = new Prefork_Worker($this); return true; } $start_time = microtime(true); $this->workers_alive[$pid] = $start_time; $this->workers_starting[$pid] = $start_time; return false; }
public function testUnblockingSignals() { pcntl_sigprocmask(SIG_BLOCK, array(SIGQUIT), $oldset); $pid = pcntl_fork(); if (0 === $pid) { Signal::unblock(array(SIGQUIT)); while (true) { sleep(1); } exit(0); } // Put mask back on this process. pcntl_sigprocmask(SIG_SETMASK, $oldset); $this->assertProcessUp($pid); posix_kill($pid, SIGQUIT); // Clear off pending process. pcntl_waitpid($pid, $status); exec("ps {$pid}", $lines); $this->assertTrue(count($lines) < 2); // Check status exit was SIGQUIT $this->assertEquals(SIGQUIT, $status); }
/** * Unblock common used signals */ public static function stopSignalHandler() { pcntl_sigprocmask(SIG_UNBLOCK, array(SIGTERM, SIGINT, SIGQUIT)); }
/** * Registers a callback function for the signal dispatcher or for special signals used by PowerProcess * * Special signals are: * - 'shutdown' : Triggered on completion of the Shutdown() method * - 'threadotl' : Triggered on killing a thread due to exceeding time limit * * @param int|string $signal The signal to register a callback for * @param callback $callback The callback function */ public function RegisterCallback($signal, $callback = false) { if ($callback !== false) { $this->callbacks[$signal][] = $callback; } // Register with PCNTL if (is_int($signal)) { $this->Log("Registering signal {$signal} with dispatcher", true); pcntl_signal($signal, array(&$this, 'SignalDispatch')); // Unblock the Signal pcntl_sigprocmask(SIG_UNBLOCK, array($signal)); } }
/** * Starts the thread. * @return bool On successful execution returns true otherwise returns false. */ public final function start() { // {{{ if (!$this->amIParent()) { return false; } if ($this->amIStarted) { return false; } $this->childPid = pcntl_fork(); if ($this->childPid == -1) { return false; } $this->_childPid = getmypid(); $this->amIStarted = true; $csInitializationResult = null; if ($this->criticalSection !== null) { $csInitializationResult = $this->criticalSection->initialize($this->childPid, $this->uniqueId); } if (!$this->amIParent()) { // child // no dispatchers needed in the children; this means that no threads withing threads creation is possible unregister_tick_function('GPhpThreadCriticalSection::dispatch'); // flag any subscribed variables indicating that the current // instance is located in a GPhpThread foreach (self::$originDynamicDataArr as &$o) { if ($o instanceof \GPhpThreadNotCloneableContainer) { $o->import(true); } } if ($csInitializationResult === false) { $this->stop(); } // don't execute the thread body if critical section is required, but missing pcntl_sigprocmask(SIG_UNBLOCK, array(SIGCHLD)); $this->run(); if ($this->criticalSection !== null) { $this->notifyParentThatChildIsTerminated(); } $this->stop(); } else { // parent if ($this->childPid != -1 && $this->criticalSection !== null) { if ($csInitializationResult === false) { // don't add the thread to the dispatch queue if missing but required critical section is the case (actually this is done in the initialize method above) $this->childPid = -1; $this->_childPid = null; $this->amIStarted = false; return false; } if (!GPhpThread::$isCriticalSectionDispatcherRegistered) { GPhpThread::$isCriticalSectionDispatcherRegistered = register_tick_function('GPhpThreadCriticalSection::dispatch'); } pcntl_sigprocmask(SIG_BLOCK, array(SIGCHLD)); // SIGCHLD will wait in the queue until it's processed } return true; } return true; }
/** * @param \EvWatcher $watcher * @param BeanieWorker $worker * @param JobOath $jobOath */ protected function handleIncomingJob(\EvWatcher $watcher, BeanieWorker $worker, JobOath $jobOath) { pcntl_sigprocmask(SIG_BLOCK, $this->terminationSignals); try { $job = $jobOath->invoke(); call_user_func($this->jobReceivedCallback, $job); } catch (SocketException $socketException) { $this->logger->error($socketException->getCode() . ': ' . $socketException->getMessage()); $this->removeWatcher($watcher); $this->removeJobListener($worker); return; } catch (\Exception $exception) { $this->logger->error($exception->getCode() . ': ' . $exception->getMessage()); $this->removeWatcher($watcher); return; } finally { pcntl_sigprocmask(SIG_UNBLOCK, $this->terminationSignals); } $worker->reserveOath(); }
/** * Time the execution loop and sleep an appropriate amount of time. * @param boolean $start * @return mixed */ private function timer($start = false) { static $start_time = null; // Start the Stop Watch and Return if ($start) { return $start_time = microtime(true); } // End the Stop Watch // Determine if we should run the ON_IDLE tasks. // In timer based applications, determine if we have remaining time. // Otherwise apply the $idle_probability factor $end_time = $probability = null; if ($this->loop_interval) { $end_time = $start_time + $this->loop_interval() - 0.01; } if ($this->idle_probability) { $probability = 1 / $this->idle_probability; } $is_idle = function () use($end_time, $probability) { if ($end_time) { return microtime(true) < $end_time; } if ($probability) { return mt_rand(1, $probability) == 1; } return false; }; // If we have idle time, do any housekeeping tasks if ($is_idle()) { $this->dispatch(array(Core_Daemon::ON_IDLE), array($is_idle)); } $stats = array(); $stats['duration'] = microtime(true) - $start_time; $stats['idle'] = $this->loop_interval - $stats['duration']; // Suppress child signals during sleep to stop exiting forks/workers from interrupting the timer. // Note: SIGCONT (-18) signals are not suppressed and can be used to "wake up" the daemon. if ($stats['idle'] > 0) { pcntl_sigprocmask(SIG_BLOCK, array(SIGCHLD)); usleep($stats['idle'] * 1000000); pcntl_sigprocmask(SIG_UNBLOCK, array(SIGCHLD)); } else { // There is no time to sleep between intervals -- but we still need to give the CPU a break // Sleep for 1/100 a second. usleep(10000); if ($this->loop_interval > 0) { $this->error('Run Loop Taking Too Long. Duration: ' . number_format($stats['duration'], 3) . ' Interval: ' . $this->loop_interval); } } $this->stats[] = $stats; return $stats; }
/** * Constructor. * * @param array $options options. * * @return void */ protected function __construct(array $options) { // Setup logger. $this->_logger = \Logger::getLogger(get_class($this)); $this->_logDebugEnabled = $this->_logger->isDebugEnabled(); $soullessArray = array(); $this->_beanAliases = $soullessArray; $this->_beanDefs = $soullessArray; $this->_beans = $soullessArray; $this->_shutdowners = $soullessArray; $this->_resources = $soullessArray; $this->_eventListeners = $soullessArray; $this->_properties = $soullessArray; // Merge options with our defaults. self::$_options = array_replace_recursive(self::$_options, $options); $this->registerProperties(self::$_options['properties']); $sapi = php_sapi_name(); if (function_exists('pcntl_signal')) { if ($sapi == 'cgi' || $sapi == 'cli') { $signals = array(SIGQUIT, SIGHUP, SIGINT, SIGCHLD, SIGTERM, SIGUSR1, SIGUSR2); $handler = array($this, 'signalHandler'); foreach ($signals as $signal) { pcntl_signal($signal, $handler); } pcntl_sigprocmask(SIG_UNBLOCK, $signals); } } set_error_handler(array($this, 'errorHandler')); register_shutdown_function(array($this, 'shutdownHandler')); $this->_lifecycleManager = new BeanLifecycleManager(); $this->_dispatcherTemplate = new DispatcherImpl(); $this->_aspectManager = new AspectManager(); $this->_aspectManager->setCache(DummyCacheImpl::getInstance()); $this->_beanDefCache = DummyCacheImpl::getInstance(); $this->_beanCache = DummyCacheImpl::getInstance(); $this->registerBeanDefinitionProvider(new Core(self::$_options)); $this->_reflectionFactory = $this; $this->_reflectionFactory = $this->getBean('dingReflectionFactory'); $this->_proxyFactory = $this->getBean('dingProxyFactory'); $this->_beanDefCache = $this->getBean('dingDefinitionsCache'); $this->_beanCache = $this->getBean('dingBeanCache'); $this->_lifecycleManager = $this->getBean('dingLifecycleManager'); $this->_aspectManager = $this->getBean('dingAspectManager'); $this->_dispatcherTemplate = $this->getBean('dingAspectCallDispatcher'); // Set drivers if (isset(self::$_options['bdef']['xml'])) { $xmlDriver = $this->getBean('dingXmlBeanDefinitionProvider'); } if (isset(self::$_options['bdef']['yaml'])) { $yamlDriver = $this->getBean('dingYamlBeanDefinitionProvider'); } $this->getBean('dingPropertiesDriver'); $this->getBean('dingMessageSourceDriver'); $this->getBean('dingMethodInjectionDriver'); // All set, continue. if (isset(self::$_options['bdef']['annotation'])) { $this->getBean('dingAnnotationDiscovererDriver'); $this->getBean('dingAnnotationBeanDefinitionProvider'); $this->getBean('dingAnnotationValueDriver'); $this->getBean('dingAnnotationResourceDriver'); $this->getBean('dingAnnotationInjectDriver'); $this->getBean('dingAnnotationInitDestroyMethodDriver'); $this->getBean('dingAnnotationRequiredDriver'); $this->getBean('dingMvcAnnotationDriver'); } $this->_lifecycleManager->afterConfig(); }
<?php // This is more or less a copy of zend/bad/ext/pcntl/tests/003.php. // 003.php.skipif is overly-cautious, so it would skip that test. This test // also makes less assumptions about what the blocked signal set looks like // coming into the test. pcntl_sigprocmask(SIG_BLOCK, array(SIGCHLD, SIGTERM), $first_set); pcntl_sigprocmask(SIG_BLOCK, array(SIGINT), $old); var_dump(count($old) - count($first_set)); pcntl_sigprocmask(SIG_UNBLOCK, array(SIGINT), $old); var_dump(count($old) - count($first_set)); pcntl_sigprocmask(SIG_SETMASK, array(SIGINT), $old); var_dump(count($old) - count($first_set)); pcntl_sigprocmask(SIG_SETMASK, array(), $old); var_dump(count($old)); pcntl_sigprocmask(SIG_SETMASK, array(), $old); var_dump(count($old));
<?php declare (ticks=1); $parentPid = posix_getpid(); $child1 = pcntl_fork(); if ($child1 != 0) { $child2 = pcntl_fork(); if ($child2 != 0) { pcntl_setpriority(10); echo "Parent\n"; $firstChildIsFree = true; $secondChildIsFree = true; pcntl_sigprocmask(SIG_BLOCK, array(SIGUSR1, SIGUSR2)); while (true) { $changes = file_get_contents('db.txt'); if (!empty($changes)) { $got2execution = false; if ($firstChildIsFree) { posix_kill($child1, SIGHUP); $firstChildIsFree = false; $got2execution = true; } elseif ($secondChildIsFree) { posix_kill($child2, SIGHUP); $secondChildIsFree = false; $got2execution = true; } if ($got2execution) { file_put_contents('db.txt', ''); } else { echo "No free workers\n"; }
echo "signo === SIGCHLD\n"; var_dump($siginfo['signo'] === SIGCHLD); echo "signo === uid\n"; var_dump($siginfo['uid'] === posix_getuid()); echo "signo === pid\n"; var_dump($siginfo['pid'] === $pid); pcntl_waitpid($pid, $status); set_error_handler(function ($errno, $errstr) { echo "Error triggered\n"; }, E_WARNING); echo "sigprocmask with invalid arguments\n"; /* Valgrind expectedly complains about this: * "sigprocmask: unknown 'how' field 2147483647" * Skip */ if (getenv("USE_ZEND_ALLOC") !== '0') { var_dump(pcntl_sigprocmask(PHP_INT_MAX, array(SIGTERM))); } else { echo "Error triggered\n"; echo "bool(false)\n"; } var_dump(pcntl_sigprocmask(SIG_SETMASK, array(0))); echo "sigwaitinfo with invalid arguments\n"; var_dump(pcntl_sigwaitinfo(array(0))); echo "sigtimedwait with invalid arguments\n"; var_dump(pcntl_sigtimedwait(array(SIGTERM), $signo, PHP_INT_MAX, PHP_INT_MAX)); } else { $siginfo = NULL; pcntl_sigtimedwait(array(SIGINT), $siginfo, 3600, 0); exit; } }
/** * Unblock an array of signals so that * the process thread will receive them. * @param array $signals */ public static function unblock(array $signals) { $signals = array_map(array(__CLASS__, 'interpretSignal'), $signals); pcntl_sigprocmask(SIG_UNBLOCK, $signals, $old); }
<?php // For simple cases, see php5_pcntl_003.php print "Correct usage, two args\n"; var_dump(pcntl_sigprocmask(SIG_BLOCK, [SIGHUP])); print "Invalid \$how\n"; var_dump(pcntl_sigprocmask(PHP_INT_MAX, [])); print "Invalid signal\n"; var_dump(pcntl_sigprocmask(SIG_SETMASK, [1337])); print "Invalid byref arg\n"; $oldset = new stdClass(); var_dump(pcntl_sigprocmask(SIG_SETMASK, [SIGHUP], $oldset)); var_dump($oldset);
/** * Sig Proc Mask * * @param int $how Sets the behavior of pcntl_sigprocmask(). Possible values: SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK * @param array $set List of signals. * @param array $oldSet The oldset parameter is set to an array containing the list of the previously blocked signals. * * @return bool Returns TRUE on success or FALSE on failure. */ public function sigProcMask($how, array $set, array $oldSet = array()) { return pcntl_sigprocmask($how, $set, $oldSet); }
/** * Fork a process, execute the callback and store process information * * @param string $name * @param callable $callback * @return bool True in parent process and False in child process if no callback was supplied */ protected function forkChild($name, callable $callback = null) { $pid = \pcntl_fork(); if ($pid === -1) { throw new \RuntimeException('Failed to fork process "' . $name . '"".'); } elseif ($pid === 0) { if (function_exists('\\cli_set_process_title')) { \cli_set_process_title('php ' . $_SERVER['argv'][0] . ' (' . $name . ': ' . ((isset($this->processStartCounter[$name]) ? $this->processStartCounter[$name] : 0) + 1) . ')'); } \pcntl_sigprocmask(SIG_SETMASK, []); unset($this->processCallbacks); unset($this->processFinished); unset($this->processPIDs); unset($this->processRestart); unset($this->processStartCounter); unset($this->processStarted); unset($this->processTimeouts); if ($callback !== null) { call_user_func($callback); exit(0); } else { return false; } } $this->processPIDs[$pid] = $name; $this->processStarted[$name] = microtime(true); $this->processStartCounter[$name] = (isset($this->processStartCounter[$name]) ? $this->processStartCounter[$name] : 0) + 1; unset($this->processRestart[$name]); return true; }