存档

文章标签 ‘swoole_process源码解析’

swoole_process源码读解-构造析构函数

2018年5月16日 没有评论

php源码对大部分php程序员来说都是神秘的,因为php本身是一门神奇伟大的语音。它可以让没有什么编程基础的人可以编程,而且偏向于应用。而swoole是php中一个非常不错的扩展,它让php对异步编程更容易,对高并发做了很多的支持。而我想抽时间写一个针对swoole源码解读的文章,来让我们更深入的了解理解swoole,更好的在项目中使用它。

适合人群

那么这个系列文章比较适合哪些程序员哪?对php扩展开发有一定了解,有c/c++语言功底的。如果你对php扩展开发不是很了解,可以查看一下我以前写过php扩展开发的文章,或者看一下《PHP扩展开发及内核应用》这本书。

源码版本

php版本:7.1.11

swoole版本:2.1.2

下面将进入正题,我们将一起来查看swoole_process是如何实现的。

swoole_process构造函数

构造函数源码实现在swoole_process.c:239

static PHP_METHOD(swoole_process, __construct)
{
    zend_bool redirect_stdin_and_stdout = 0;
    long pipe_type = 2;
    zval *callback;
    
    //判断当前是不是命令行模式
    if (!SWOOLE_G(cli))
    {
        swoole_php_fatal_error(E_ERROR, "swoole_process only can be used in PHP CLI mode.");
        RETURN_FALSE;
    }

    //当前是否server中的master进程中
    if (SwooleG.serv && SwooleGS->start == 1 && swIsMaster())
    {
        swoole_php_fatal_error(E_ERROR, "swoole_process can't be used in master process.");
        RETURN_FALSE;
    }

    //解析参数,具体的请查看swoole官方手册中各参数含义
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|bl", &callback, &redirect_stdin_and_stdout, &pipe_type) == FAILURE)
    {
        RETURN_FALSE;
    }

    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);

    //分配process所需的内存
    swWorker *process = emalloc(sizeof(swWorker));
    bzero(process, sizeof(swWorker));

    int base = 1;
    //计算当前已启动process数量
    if (SwooleG.serv && SwooleGS->start)
    {
        base = SwooleG.serv->worker_num + SwooleG.task_worker_num + SwooleG.serv->user_worker_num;
    }
    if (php_swoole_worker_round_id == 0)
    {
        php_swoole_worker_round_id = base;
    }
    //设置当前process id,注意这里不是进程pid
    process->id = php_swoole_worker_round_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(swWorker));
        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);
        process->pipe_worker = _pipe->getFd(_pipe, SW_PIPE_WORKER);
        process->pipe = process->pipe_master;
        //设置process对象的pipe属性
        zend_update_property_long(swoole_process_class_entry_ptr, getThis(), ZEND_STRL("pipe"), process->pipe_master TSRMLS_CC);
    }
    
    //将当前对象加入到swoole全局的对象管理当中
    swoole_set_object(getThis(), process);
    //设置process对象的callback属性
    zend_update_property(swoole_process_class_entry_ptr, getThis(), ZEND_STRL("callback"), callback TSRMLS_CC);
}

 

这里面有调用swoole_set_object函数,getThis函数获得当前对象指针,可以理解为php中常用的$this关键字,下面我们看一下swoole_set_object是如何实现的。

swoole_set_object在swoole.c:699

void swoole_set_object(zval *object, void *ptr)
{
    SWOOLE_GET_TSRMLS;
    int handle = sw_get_object_handle(object);
    assert(handle < SWOOLE_OBJECT_MAX);
    
    //是否需要为swoole_objects重新分配内存
    if (handle >= swoole_objects.size)
    {
        uint32_t old_size = swoole_objects.size;
        //新swoole_objects大小
        uint32_t new_size = swoole_get_new_size(old_size, handle TSRMLS_CC);

        void *old_ptr = swoole_objects.array;
        void *new_ptr = NULL;
        
        //重新分配内存
        new_ptr = realloc(old_ptr, sizeof(void*) * new_size);
        if (!new_ptr)
        {
            swoole_php_fatal_error(E_ERROR, "malloc(%d) failed.", (int )(new_size * sizeof(void *)));
            return;
        }
        bzero(new_ptr + (old_size * sizeof(void*)), (new_size - old_size) * sizeof(void*));
        swoole_objects.array = new_ptr;
        swoole_objects.size = new_size;
    }
    swoole_objects.array[handle] = ptr;
}

sw_get_object_handle是1个宏函数,最终展开结果是((*(object)).value.obj)->handle。其作用是获得当前对象的handle,也就是它的索引。

typedef struct _zval_struct     zval; //zend_types.h:84
typedef struct _zend_object     zend_object; //zend_types.h:89

struct _zend_object { //zend_types.h:277
	zend_refcounted_h gc;
	uint32_t          handle; // TODO: may be removed ???
	zend_class_entry *ce;
	const zend_object_handlers *handlers;
	HashTable        *properties;
	zval              properties_table[1];
};

typedef union _zend_value { //zend_types.h:101
	zend_long         lval;				/* long value */
	double            dval;				/* double value */
	zend_refcounted  *counted;
	zend_string      *str;
	zend_array       *arr;
	zend_object      *obj;
	zend_resource    *res;
	zend_reference   *ref;
	zend_ast_ref     *ast;
	zval             *zv;
	void             *ptr;
	zend_class_entry *ce;
	zend_function    *func;
	struct {
		uint32_t w1;
		uint32_t w2;
	} ww;
} zend_value;

struct _zval_struct {   //zend_types.h:121
	zend_value        value;			/* value */
	union {
		struct {
			ZEND_ENDIAN_LOHI_4(
				zend_uchar    type,			/* active type */
				zend_uchar    type_flags,
				zend_uchar    const_flags,
				zend_uchar    reserved)	    /* call info for EX(This) */
		} v;
		uint32_t type_info;
	} u1;
	union {
		uint32_t     next;                 /* hash collision chain */
		uint32_t     cache_slot;           /* literal cache slot */
		uint32_t     lineno;               /* line number (for ast nodes) */
		uint32_t     num_args;             /* arguments number for EX(This) */
		uint32_t     fe_pos;               /* foreach position */
		uint32_t     fe_iter_idx;          /* foreach iterator index */
		uint32_t     access_flags;         /* class constant access flags */
		uint32_t     property_guard;       /* single property guard */
		uint32_t     extra;                /* not further specified */
	} u2;
};

#define sw_get_object_handle(object)        Z_OBJ_HANDLE_P(object)
#define Z_OBJ_HANDLE_P(zval_p)      Z_OBJ_HANDLE(*(zval_p))
#define Z_OBJ_HANDLE(zval)          (Z_OBJ((zval)))->handle
#define Z_OBJ(zval)					(zval).value.obj

 析构函数

析构函数实现在swoole_process.c:318

static PHP_METHOD(swoole_process, __destruct)
{
    swWorker *process = swoole_get_object(getThis());
    swPipe *_pipe = process->pipe_object;
    if (_pipe)
    {
        _pipe->close(_pipe);
        efree(_pipe);
    }
    if (process->queue)
    {
        efree(process->queue);
    }
    efree(process);
}

析构函数主要判断是否使用管道和队列,然后关闭管道和释放管道队列占用的内存,然后释放进程占用内存。