前一篇文章我们分析了进程的构造过程,这篇我们开始分析其start过程,start通过执行底层fork系统调用,启动进程,在PHP侧的执行原型如下:
$pid = $process->start();
函数执行成功,则返回子进程的PID
,执行失败返回false
。可以通过$process->pid和$process->pipe分别获取子进程的PID和管道的文件描述符,而在start过程中子进程会继承父进程的内存和文件句柄,同时会清除从父进程继承的EventLoop
、Signal
、Timer。
下面我们开始分析代码。
static PHP_METHOD(swoole_process, start)
{
swWorker *process = swoole_get_object(getThis());//获取内部对象swWorker信息
if (process->pid > 0 && kill(process->pid, 0) == 0)//进程已经在运行中
{
swoole_php_fatal_error(E_WARNING, "process has already been started.");
RETURN_FALSE;
}
pid_t pid = fork();//调用fork系统调用创建进程
if (pid < 0)//fork失败,fork一般在内存不足等情况会出现失败的情况
{
swoole_php_fatal_error(E_WARNING, "fork() failed. Error: %s[%d]", strerror(errno), errno);
RETURN_FALSE;
}
else if (pid > 0) //父进程逻辑
{
process->pid = pid;//赋值进程号信息
process->child_process = 0;//标记为不是子进程
zend_update_property_long(swoole_server_class_entry_ptr, getThis(), ZEND_STRL("pid"), process->pid TSRMLS_CC);//设置swoole_process的pid属性信息
RETURN_LONG(pid);//父进程返回子进程的进程ID信息
}
else //pid=0的情况,也就是子进程的逻辑
{
process->child_process = 1;//标记为子进程
//执行子进程的逻辑
SW_CHECK_RETURN(php_swoole_process_start(process, getThis() TSRMLS_CC));
}
RETURN_TRUE;
}
int php_swoole_process_start(swWorker *process, zval *object TSRMLS_DC)
{
process->pipe = process->pipe_worker;//进程管道信息
process->pid = getpid();//进程号信息
if (process->redirect_stdin)//如果PHP端设置了标准输入的重定向属性
{
if (dup2(process->pipe, STDIN_FILENO) < 0)//将进程的标准输入重定向到管道中
{
swoole_php_fatal_error(E_WARNING, "dup2() failed. Error: %s[%d]", strerror(errno), errno);
}
}
if (process->redirect_stdout)//如果PHP端设置了标准输出的重定向属性
{
if (dup2(process->pipe, STDOUT_FILENO) < 0)//将进程的标准输出重定向到管道中
{
swoole_php_fatal_error(E_WARNING, "dup2() failed. Error: %s[%d]", strerror(errno), errno);
}
}
if (process->redirect_stderr)//如果PHP端设置了标准错误的重定向属性
{
if (dup2(process->pipe, STDERR_FILENO) < 0)//将进程的标准错误重定向到管道中
{
swoole_php_fatal_error(E_WARNING, "dup2() failed. Error: %s[%d]", strerror(errno), errno);
}
}
//注销进程的主事件管理器
if (SwooleG.main_reactor)
{
SwooleG.main_reactor->free(SwooleG.main_reactor);
SwooleG.main_reactor = NULL;
swTraceLog(SW_TRACE_PHP, "destroy reactor");
}
#ifdef SW_COROUTINE
swLinkedList *coro_timeout_list = SwooleWG.coro_timeout_list;
#endif
bzero(&SwooleWG, sizeof(SwooleWG));
SwooleG.pid = process->pid;//设置SwooleG的pid信息,SwooleG抽象的是主进程信息
if (SwooleG.process_type != SW_PROCESS_USERWORKER)
{
SwooleG.process_type = 0;//进程类型信息
}
SwooleWG.id = process->id;//SwooleWG抽象的是用户端worker的信息,这里设置SwooleWG的id属性
#ifdef SW_COROUTINE
SwooleWG.coro_timeout_list = coro_timeout_list;
#endif
if (SwooleG.timer.fd)//清理所有定时器信息
{
swTimer_free(&SwooleG.timer);
bzero(&SwooleG.timer, sizeof(SwooleG.timer));
}
swSignal_clear();//清理所有的信号处理器
zend_update_property_long(swoole_process_class_entry_ptr, object, ZEND_STRL("pid"), process->pid TSRMLS_CC);//设置swoole_process对象的pid属性,设置为当前进程ID,也就是子进程的进程ID
zend_update_property_long(swoole_process_class_entry_ptr, object, ZEND_STRL("pipe"), process->pipe_worker TSRMLS_CC);//设置swoole_process对象的pipe属性
zval *zcallback = sw_zend_read_property(swoole_process_class_entry_ptr, object, ZEND_STRL("callback"), 0 TSRMLS_CC);//读取PHP业务端设置的callback函数信息
zval **args[1];
if (zcallback == NULL || ZVAL_IS_NULL(zcallback))//读取异常
{
swoole_php_fatal_error(E_ERROR, "no callback.");
return SW_ERR;
}
zval *retval = NULL;
args[0] = &object;
sw_zval_add_ref(&object);
if (sw_call_user_function_ex(EG(function_table), NULL, zcallback, &retval, 1, args, 0, NULL TSRMLS_CC) == FAILURE)//执行业务端的callback函数
{
swoole_php_fatal_error(E_ERROR, "callback function error");
return SW_ERR;
}
if (EG(exception))//出现异常信息
{
zend_exception_error(EG(exception), E_ERROR TSRMLS_CC);//调用PHP底层函数处理异常信息
}
if (retval)
{
sw_zval_ptr_dtor(&retval);
}
if (SwooleG.main_reactor)//这一步显得多余,上面已经把SwooleG.main_reactor设置为null了。
{
php_swoole_event_wait();
}
SwooleG.running = 0;//标记SwooleG为未运行状态
zend_bailout();
return SW_OK;
}