taoCMS是基于php+sqlite/mysql的国内最小(100Kb左右)的功能完善、开源免费的CMS管理系统

PHP扩展pcntl实现”多线程”(进程)

2012-12-07

pcntl与ticks

ticks是通过declare(ticks = n) {statement}语法定义的, declare语法目前只能接受ticks, 他定义的ticks = n的意义是当declare指定的语句块中执行了N条低级语句去发生一个事件, 这个事件可以通过register_tick_function($function_name)来注册.

pcntl的信号机制是基于ticks机制实现的. 因此, 我们使用pcntl族函数中信号相关的函数时, 需要在前面增加declare(ticks = n)语法结构.

int pcntl_alarm(int $seconds): 

$seconds秒后向进程发送一个SIGALRM信号, 每次调用pcntl_alarm方法都会取消之前设置的时钟.

void pcntl_exec(string $path[, array $args[, array $env]]):

在当前进程空间执行一个程序. 

$path: 必须是二进制可执行文件, 或具有有效脚本头信息(#!/usr/local/bin/php)的脚本文件路径.

$args: 将要传递给该程序的字符串参数列表(数组形式)

$envs: 环境变量. 以数组(key => value形式)方式传递给要执行程序的环境变量.

int pcntl_fork(void):

创建一个子进程, 该子进程与父进程仅仅是PID(进程号)和PPID(父进程号)不同.

在父线程执行时返回创建的子进程pid, 在子线程执行时返回0, 创建子进程失败时会在父进程上下文返回-1, 并引发php错误.

理解这里的fork需要知道: pcntl_fork创建的是一个分支节点, 相当于一个标记, 父进程完成后, 子进程会从标记处继续执行, 也就是说pcntl_fork后面的代码分别被父进程和子进程执行了两遍, 而两个进程在执行过程中得到的返回值是不同的. 因此, 才可以分离父子进程执行不同的代码.

int pcntl_getpriority([int $pid = getmypid()[, int $process_identifier = PRIO_PROCESS]]): 

获取给定$pid对应的进程的优先级, 默认是通过getmypid()获取到的值也就是当前进程.

$pid: 如果没有指定, 默认是当前进程.

$process_identifier: PRIO_PGRP, PRIO_USER, PRIO_PROCESS三者之一, 默认PRIO_PROCESS. 其中PRIO_PGRP指获取进程组的优先级, PRIO_USER指获取用户进程的优先级, PRIO_PROCESS指获取特定进程优先级.

返回进程的优先级, 或者在发生错误时返回false, 值越小说明越优先

bool pcntl_setpriority(int $priority[, int $pid = getmypid()[, int $process_identifier = PRIO_PROCESS]]: 

设置进程的优先级.

$priority: 优先级值, -20到20的范围内, 默认优先级为0.  值越小说明越优先.

$pid: 如果没有指定, 指当前进程

$process_identifier: 意义同pcntl_getpriority的$process_identifier.

设置成功返回TRUE, 失败返回FALSE.

bool pcntl_signal_dispatch(void):

调用通过pcntl_signal()安装的即将发生的信号的处理器.

调用成功返回TRUE, 失败返回false.

php 5.3.3加入

bool pcntl_signal(int $signo, callback $handler[, bool $restart_syscalls = true]):

为指定的信号$signo安装一个新的信号处理器$handler.

最后一个参数不明白意义.

bool pcntl_sigprocmask(int $how, array $set[, array &$oldset]):

增加, 删除或设置锁信号, 具体的行为依赖于$how参数

$how: SIG_BLOCK用于把信号增加到当前锁信号中, SIG_UNBLOCK用于把信号从当前锁信号中移除, SIG_SETMASK用于用给定的信号列表替换当前锁信号.

$set: 要增加, 移除或设置的信号列表.

$oldset: 用于向调用者返回旧的锁定信号.

成功返回TRUE, 失败返回FALSE.

int pcntl_sigtimedwait(array $set[, array &$siginfo[, int $seconds = 0[, int $nanoseconds = 0]]]):

pcntl_sigtimedwait实际上和pcntl_sigwaitinfo()所做的是同样的事情, 不过pcntl_sigtimedwait多了两个增强的参数$seconds和$nanoseconds, 这样就允许脚本的停留时间有一个上限而不是无限制等待.

$set: 需要等待的信号列表

$siginfo: 用来向调用者返回等待得到的信号的信息, 信息内容见pcntl_sigwaitinfo

$seconds: 超时的秒数

$nanoseconds: 超时的纳秒数

成功后, pcntl_sigtimedwiat()返回信号数量

int pcntl_sigwaitinfo(array $set[, array &$siginfo]):

挂起当前脚本的执行, 直到接受到$set中的某个信号, 如果其中的一个信号将要到达(比如被pcntl_sigprocmask锁定)那么pcntl_sigwaitinfo将会立刻返回

$set: 等待的信号列表

$siginfo: 用来向调用者返回等待得到的信号的信息, 该信息包含以下内容:

1.所有信号都有以下三个信息:

a)signo: 信号数量

b)errno: 错误号

c)code: 信号代码

2.SIGCHLD信号特有的信息

a)status: 退出的值或信号

b)utime: 用户消耗时间

c)stime: 系统消耗时间

d)pid: 发送进程id

e)uid: 发送进程的真实用户id

3.SIGILL, SIGFPE, SIGSEGV, SIGBUS拥有的信息

a)addr: 产生故障的内存位置

4.SIGPOLL特有的信息:

a)band: band event, 意义未知

b)fd: 文件描述符

函数成功运行返回信号数量

int pcntl_wait(int &$status[, int *options = 0]):

挂起当前进程直到一个子进程退出或直到一个信号要求终止当前进程或调用一个信号处理函数. 如果子进程在调用时已经退出(俗称成为了僵尸进程), 此函数会马上返回, 所有的系统资源都将被释放.

$status用来保存子进程的状态信息, 该状态信息由以下函数产生: pcntl_wifexited, pcntl_wifstopped, pcntl_wifsignaled, pcntl_wexitstatus, pcntl_wtermsig, pcntl_wstopsig.

$options: 如果你的系统允许wait3(大多数的BSD类系统), 你可以提供一个可选的options参数, 如果不提供这个参数, wait将会使用系统调用, 如果系统不允许wait3, 提供这个参数不会有任何影响, $options的值可以是0或者WNOHANG和WUNTRACED两个常数.

函数返回退出的子进程的PID, 或在错误时返回-1, 或者如果提供WNOHANG作为option(wait3不可用的系统)并且无有效子进程返回0

僵尸进程: 由于父进程在fork之后, 无法预知子进程什么时候结束, 所以子进程为了要留给父进程一些信息, 会留下一个称作僵尸的数据结构, 等待由父进程发起wait的操作来为它收尸, 在子进程结束(逻辑结束)到父进程收尸前这一段时间子进程就被称为僵尸进程, 在父进程结束后, 所有的子进程会交由Init来负责, 因此, 如果父进程结束, 僵尸进程都会被回收, 但是, 如果父进程永远不结束, 这些僵尸进程就一直占用进程号, 如果系统进程号耗尽, 那么将导致无法启动新进程, 因此, 安全的做法是在父进程中为自己产生的子进程去收尸.

int pcntl_waitpid(int $pid, int &$status[, int $options = 0]):

挂起当前进程直到给定$pid的子进程退出, 或者当前进程接受到一个退出信号, 或者接受到一ige 信号去调用一个信号处理器.

如果给定$pid对应的子进程在调用此函数时已经退出(僵尸态), 函数立刻返回, 所有的系统资源被释放.

$pid: 进程号, 小于-1表明等待的是进程组中的任何子进程, 进程组号就是$pid的绝对值. 等于-1表明等待任意紫禁城, 与pcntl_wait函数行为一致. 等于0代表等待与调用进程在同一组的子进程, 大于0代表是特定的进程.

$status: 用来由函数返回子进程状态. 该状态信息由以下函数产生: pcntl_wifexited, pcntl_wifstopped, pcntl_wifsignaled, pcntl_wexitstatus, pcntl_wtermsig, pcntl_wstopsig.

$options: 与pcntl_wait的$options意义相同

int pcntl_wexitstatus(int $status):

返回一个已经中断的子进程返回代码, 此函数仅在pcntl_wifexited函数返回TRUE时有用.

$status参数是pcntl_waitpid产生的状态信息.

bool pcntl_wifexited(int $status): 

检查给定状态是否表明子进程是正常退出的.

bool pcntl_wifsignaled(int $status):

检查给定状态是否表明子进程是由于收到某个信号退出的.

bool pcntl_wifstopped(int $status):

检查$status是否能表明子进程当前已经停止, 这个函数只有在作用于pcntl_waitpid函数使用的WUNTRACED作为$options参数的值时产生的$status上才有效.

int pcntl_wstopsig(int $status): 

通过分析$status返回使得子进程停止的信号的数量, 这个函数只有在pcntl_wifsignaled返回TRUE时才有效.

int pcntl_wtermsig(int $status):

返回使进程中断的信号数量. 这个函数只有在pcntl_wifsignaled返回TRUE时才有效.

一个PHP产生子进程的例子

<?php

/**

 * 创建子进程入口

 * @author selfimpr

 * @blog http://blog.csdn.net/lgg201

 * @mail lgg860911@yahoo.com.cn

 * @param $func_name 代表子进程处理过程的函数名

 * @param other 接受不定参数, 提供给子进程的过程函数.

 */

function new_child($func_name) {

$args = func_get_args();

unset($args[0]);

$pid = pcntl_fork();

if($pid == 0) {

function_exists($func_name) and exit(call_user_func_array($func_name, $args)) or exit(-1);

} else if($pid == -1) {

echo “Couldn’t create child process.”;

}

}

//测试处理函数, 输出$prefix连接的数组

function test($prefix, $num) {

while($i ++ < $num) echo $prefix.$i.”n”;

}

//创建一个子进程

new_child(“test”, “child process “, 100);

//父进程也开启一个与子进程同样多的循环.

teest(“parent process”, 100);

//运行结果, 我这里运行父进程输出50个左右, 子进程开始运行.

?>

类别:技术文章 | 阅读:276524 | 评论:0 | 标签:php 多线程 fork

想收藏或者和大家分享这篇好文章→

“PHP扩展pcntl实现”多线程”(进程)”共有0条留言

发表评论

姓名:

邮箱:

网址:

验证码:

公告

taoCMS发布taoCMS 3.0.2(最后更新21年03月15日),请大家速速升级,欢迎大家试用和提出您宝贵的意见建议。

捐助与联系

☟请使用新浪微博联系我☟

☟在github上follow我☟

标签云