PHP
  Home arrow PHP arrow Page 3 - Managing Standalone Scripts in PHP
Administration  
AJAX  
Apache  
BrainDump  
DHTML  
Flash  
Java  
JavaScript  
Multimedia  
MySQL  
Oracle  
Perl  
PHP  
Practices  
Python  
Reviews  
Security  
Style-Sheets  
Web Services  
XML  
Zend  
Zope  
Forums Sitemap 
IBM® developerWorks 
Sun Developer Network 
E-Commerce Hosting 
Linux Web Hosting 
Managed Hosting 
Small Business Hosting 
Mobile Linux 
App Generation ROI 
VPS Hosting 
Weekly Newsletter

 
Developer Updates  
Free Website Content 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us Get Paid 
Request Media Kit
Contact Us 
Site Map 
Privacy Policy 
Support 
 USERNAME
 
 PASSWORD
 
 
  >>> SIGN UP!  
  Lost Password? 
PHP

Managing Standalone Scripts in PHP
By: Sams Publishing
  • Search For More Articles!
  • Disclaimer
  • Author Terms
  • Rating: 4 stars4 stars4 stars4 stars4 stars / 8
    2006-09-07

    Table of Contents:
  • Managing Standalone Scripts in PHP
  • Closing Shared Resources
  • Signals
  • Writing Daemons

  • Rate this Article: Poor Best 
      ADD THIS ARTICLE TO:
      Del.ici.ous Digg
      Blink Simpy
      Google Spurl
      Y! MyWeb Furl
    Email Me Similar Content When Posted
    Add Developer Shed Article Feed To Your Site
    Email Article To Friend
    Print Version Of Article
    PDF Version Of Article
     
     
    ADVERTISEMENT


    Managing Standalone Scripts in PHP - Signals


    (Page 3 of 4 )

    Signals send simple instructions to processes. When you use the shell command kill to terminate a process on your system, you are in fact simply sending an interrupt signal (SIGINT). Most signals have a default behavior (for example, the default behavior for SIGINT is to terminate the process), but except for a few exceptions, these signals can be caught and handled in custom ways inside a process.

    Some of the most common signals are listed next (the complete list is in the signal(3) man page):

    Signal Name

    Description

    Default Behavior

    SIGCHLD

    Child termination

    Ignore

    SIGINT

    Interrupt request

    Terminate process

    SIGKILL

    Kill program

    Terminate process

    SIGHUP

    Terminal hangup

    Terminate process

    SIGUSR1

    User defined

    Terminate process

    SIGUSR2

    User defined

    Terminate process

    SIGALRM

    Alarm timeout

    Terminate process


    To register your own signal handler, you simply define a function like this:

    function sig_usr1($signal)
    {
    print "SIGUSR1 Caught.\n";
    }

    and then register it with this:

    declare(ticks=1);
    pcntl_signal(SIGUSR1, "sig_usr1");

    Because signals occur at the process level and not inside the PHP virtual machine itself, the engine needs to be instructed to check for signals and run the pcntl callbacks. To allow this to happen, you need to set the execution directive ticks. ticks instructs the engine to run certain callbacks every N statements in the executor. The signal callback is essentially a no-op, so setting declare(ticks=1) instructs the engine to look for signals on every statement executed.

    The following sections describe the two most useful signal handlers for multiprocess scripts—SIGCHLD and SIGALRM—as well as other common signals.

    SIGCHLD

    SIGCHLD is a common signal handler that you set in applications where you fork a number of children. In the examples in the preceding section, the parent has to loop on pcntl_wait() or pcntl_waitpid() to ensure that all children are collected on. Signals provide a way for the child process termination event to notify the parent process that children need to be collected. That way, the parent process can execute its own logic instead of just spinning while waiting to collect children.

    To implement this sort of setup, you first need to define a callback to handle SIGCHLD events. Here is a simple example that removes the PID from the global $children array and prints some debugging information on what it is doing:

    function sig_child($signal) 
    {
    global $children;
    pcntl_signal(SIGCHLD, "sig_child");
    fputs(STDERR, "Caught SIGCHLD\n");
    while(($pid = pcntl_wait($status, WNOHANG)) > 0) {
    $children = array_diff($children, array($pid));
    fputs(STDERR, "Collected pid $pid\n");
    }
    }

    The SIGCHLD signal does not give any information on which child process has terminated, so you need to call pcntl_wait() internally to find the terminated processes. In fact, because multiple processes may terminate while the signal handler is being called, you must loop on pcntl_wait() until no terminated processes are remaining, to guarantee that they are all collected. Because the option WNOHANG is used, this call will not block in the parent process.

    Most modern signal facilities restore a signal handler after it is called, but for portability to older systems, you should always reinstate the signal handler manually inside the call.

    When you add a SIGCHLD handler to the earlier example, it looks like this:

    #!/usr/bin/env php
    <?php
    declare(ticks=1);
    pcntl_signal(SIGCHLD, "sig_child");
    define('PROCESS_COUNT', '5');
    $children = array();
    for($i = 0; $i < PROCESS_COUNT; $i++) {
    if(($pid = pcntl_fork()) == 0) {
    exit(child_main());
    }
    else {
    $children[] = $pid;
    } 
    }
    while($children) {
    sleep(10); // or perform parent logic
    }
    pcntl_alarm(0);
    function child_main() 
    {
    sleep(rand(0, 10)); // or perform child logic
    return 1;
    }
    function sig_child($signal) 
    {
    global $children;
    pcntl_signal(SIGCHLD, "sig_child");
    fputs(STDERR, "Caught SIGCHLD\n");
    while(($pid = pcntl_wait($status, WNOHANG)) > 0) {
    $children = array_diff($children, array($pid));
    if(!pcntl_wifexited($status)) {
    fputs(STDERR, "Collected killed pid $pid\n");
    }
    else {
    fputs(STDERR, "Collected exited pid $pid\n");
    }
    }
    }
    ?>

    Running this yields the following output:

    > ./8.php
    Caught SIGCHLD
    Collected exited pid 5000
    Caught SIGCHLD
    Collected exited pid 5003
    Caught SIGCHLD
    Collected exited pid 5001
    Caught SIGCHLD
    Collected exited pid 5002
    Caught SIGCHLD
    Collected exited pid 5004

    SIGALRM

    Another useful signal is SIGALRM, the alarm signal. Alarms allow you to bail out of tasks if they are taking too long to complete. To use an alarm, you define a signal handler, register it, and then call pcntl_alarm() to set the timeout. When the specified timeout is reached, a SIGALRM signal is sent to the process.

    Here is a signal handler that loops through all the PIDs remaining in $children and sends them a SIGINT signal (the same as the Unix shell command kill):

    function sig_alarm($signal)
    {
    global $children;
    fputs(STDERR, "Caught SIGALRM\n");
    foreach ($children as $pid) {
    posix_kill($pid, SIGINT);
    }
    }

    Note the use of posix_kill(). posix_kill() signals the specified process with the given signal.

    You also need to register the sig_alarm() SIGALRM handler (alongside the SIGCHLD handler) and change the main block as follows:

    declare(ticks=1);
    pcntl_signal(SIGCHLD, "sig_child");
    pcntl_signal(SIGALRM, "sig_alarm");
    define('PROCESS_COUNT', '5');
    $children = array();
    pcntl_alarm(5);
    for($i = 0; $i < PROCESS_COUNT; $i++) {
    if(($pid = pcntl_fork()) == 0) {
    exit(child_main());
    }
    else {
    $children[] = $pid;
    }
    }
    while($children) {
    sleep(10); // or perform parent logic
    }
    pcntl_alarm(0);

    It is important to remember to set the alarm timeout to 0 when it is no longer needed; otherwise, it will fire when you do not expect it. Running the script with these modifications yields the following output:

    > ./9.php
    Caught SIGCHLD
    Collected exited pid 5011
    Caught SIGCHLD
    Collected exited pid 5013
    Caught SIGALRM
    Caught SIGCHLD
    Collected killed pid 5014
    Collected killed pid 5012
    Collected killed pid 5010

    In this example, the parent process uses the alarm to clean up (via termination) any child processes that have taken too long to execute.

    Other Common Signals

    Other common signals you might want to install handlers for are SIGHUP, SIGUSR1, and SIGUSR2. The default behavior for a process when receiving any of these signals is to terminate. SIGHUP is the signal sent at terminal disconnection (when the shell exits). A typical process in the background in your shell terminates when you log out of your terminal session.

    If you simply want to ignore these signals, you can instruct a script to ignore them by using the following code:

    pcntl_signal(SIGHUP, SIGIGN);

    Rather than ignore these three signals, it is common practice to use them to send simple commands to processes—for instance, to reread a configuration file, reopen a logfile, or dump some status information.

    More PHP Articles
    More By Sams Publishing


       · This article is an excerpt from the book "Advanced PHP Programming," published by...
     

    Buy this book now. This book is excerpted from chapter five of the book Advanced PHP Programming, written by George Schlossnagle (Sams; ISBN: 0672325616). Check it out today at your favorite bookstore. Buy this book now.

       

    PHP ARTICLES

    - Working With Different Namespaces in PHP 5
    - User Management Explained: Overview
    - Using Namespaces in PHP 5
    - Database Security: Guarding Against SQL Inje...
    - Building a Modular Exception Class in PHP 5
    - Database and Password Security for Web Appli...
    - Handling MySQL Data Set Failures in PHP 5
    - Building Site Registration for Web Applicati...
    - Intercepting Customized Exceptions in PHP 5
    - Securing Your Web Application Against Attacks
    - Sub Classing Exceptions in PHP 5
    - Authentication for Web Application Security
    - Building a Content Management System with Co...
    - Filters and Login Systems for Web Applicatio...
    - Working with the Email Class in Code Igniter





    © 2003-2008 by Developer Shed. All rights reserved. DS Cluster 1 hosted by Hostway
    Stay green...Green IT