swoole_process源码分析之process创建过程

swoole提供了创建进程的方式,据swoole官方文档介绍,比PHP原生的pcntl更高效,下面是官方文档给出的pcntl的缺点和swoole的优点。

PHP自带的pcntl,存在很多不足,如

  • pcntl没有提供进程间通信的功能
  • pcntl不支持重定向标准输入和输出
  • pcntl只提供了fork这样原始的接口,容易使用错误
  • swoole_process提供了比pcntl更强大的功能,更易用的API,使PHP在多进程编程方面更加轻松。

swoole_process提供了如下特性:

  • swoole_process提供了基于unixsock的进程间通信,使用很简单只需调用write/read或者push/pop即可
  • swoole_process支持重定向标准输入和输出,在子进程内echo不会打印屏幕,而是写入管道,读键盘输入可以重定向为管道读取数据
  • 配合swoole_event模块,创建的PHP子进程可以异步的事件驱动模式
  • swoole_process提供了exec接口,创建的进程可以执行其他程序,与原PHP父进程之间可以方便的通信。

从这篇文章开始,我们开始分析下swoole的进程控制模块,其代码位于swoole_process.c中,现在我们就从一个PHP的使用demo开始。

start();

function callback_function(swoole_process $worker)
{
    $worker->exec('/usr/local/bin/php', array(__DIR__.'/swoole_server.php'));
}

swoole_process::wait();

代码我们就从swoole_process的构造函数开始。

//swoole_process的构造函数,从PHP代码可以看出,其构造函数可以传入进程执行函数、标准输出和错误的重定向信息、是否使用管道以及管道类型
static PHP_METHOD(swoole_process, __construct)
{
    zend_bool redirect_stdin_and_stdout = 0;
    long pipe_type = 2;
    zval *callback;

    //这种模式仅仅适用于cli模式
    if (!SWOOLE_G(cli))
    {
        swoole_php_fatal_error(E_ERROR, "swoole_process only can be used in PHP CLI mode.");
        RETURN_FALSE;
    }
    //swoole_process不适用于master进程,这里主要是由于master进程牵涉多线程的操作,这里的master进程是指swoole_server里面的master进程
    if (SwooleG.serv && SwooleG.serv->gs->start == 1 && swIsMaster())
    {
        swoole_php_fatal_error(E_ERROR, "swoole_process can't be used in master process.");
        RETURN_FALSE;
    }

    //不支持异步IO
    if (SwooleAIO.init)
    {
        swoole_php_fatal_error(E_ERROR, "unable to create process with async-io threads.");
        RETURN_FALSE;
    }

    //解析输入参数信息,输入参数如上面所说的三个
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|bl", &callback, &redirect_stdin_and_stdout, &pipe_type) == FAILURE)
    {
        RETURN_FALSE;
    }

    //判断callback信息是否是可以执行的
    char *func_name = NULL;
    if (!sw_zend_is_callable(callback, 0, &func_name TSRMLS_CC))
    {
        swoole_php_fatal_error(E_ERROR, "function '%s' is not callable", func_name);
        efree(func_name);
        RETURN_FALSE;
    }
    efree(func_name);

    //申请swWorker空间及初始化
    swWorker *process = emalloc(sizeof(swWorker));
    bzero(process, sizeof(swWorker));

    int base = 1;
    if (SwooleG.serv && SwooleG.serv->gs->start) //确定worker的编号信息,这里和swoole_server结合一起确定其ID信息
    {
        base = SwooleG.serv->worker_num + SwooleG.serv->task_worker_num + SwooleG.serv->user_worker_num;
    }
    if (php_swoole_worker_round_id == 0)//如果没有ID,则该ID设置为1
    {
        php_swoole_worker_round_id = base;
    }
    process->id = php_swoole_worker_round_id++;//ID赋值信息

    if (redirect_stdin_and_stdout)//需要重定向,这里所谓的重定向是指标准输出和错误输出不再显示到屏幕上,而是写到管道中。
    {
        process->redirect_stdin = 1;
        process->redirect_stdout = 1;
        process->redirect_stderr = 1;
        /**
         * Forced to use stream pipe
         */
        pipe_type = 1;//管道信息设置
    }

    if (pipe_type > 0)//使用管道
    {
        swPipe *_pipe = emalloc(sizeof(swPipe));
        int socket_type = pipe_type == 1 ? SOCK_STREAM : SOCK_DGRAM;
        if (swPipeUnsock_create(_pipe, 1, socket_type) < 0) //创建管道信息
        {
            RETURN_FALSE;
        }

        process->pipe_object = _pipe;//设置进程的管道对象信息
        process->pipe_master = _pipe->getFd(_pipe, SW_PIPE_MASTER);//创建管道的master描述符信息
        process->pipe_worker = _pipe->getFd(_pipe, SW_PIPE_WORKER);//创建管道的worker描述符信息
        process->pipe = process->pipe_master;

        //设置swoole_process的属性
        zend_update_property_long(swoole_process_class_entry_ptr, getThis(), ZEND_STRL("pipe"), process->pipe_master TSRMLS_CC);
    }

    swoole_set_object(getThis(), process);//创建PHP侧对象swoole_process和内部对象的对应关系
    zend_update_property(swoole_process_class_entry_ptr, getThis(), ZEND_STRL("callback"), callback TSRMLS_CC);//设置swoole_process的属性
}

 

你可能感兴趣的:(swoole,Swoole源码分析)