未完待续…
进程(process)
- 进程的概念
- php如何创建子进程,修改进程名称
- php查看进程
- 进程组、会话
- daemon进程
- 信号
- 进程间通信(ipc),信号,队列,共享内存
进程的概念
进程:是系统进行资源分配和调度的基本单位
php如何创建子进程
<?php
//从这一行代码开始,下面的每一行代码都是父子进程都执行,执行顺序依赖cpu的调度
$pid = pcntl_fork();
if ($pid < 0) {
die('fork child process fail');
}
if ($pid > 0) {
//parent process
} else {
//child process
//这里一般会退出,不然子进程不退出,会执行到下面主程序的代码
exit(0);
}
//这一行代码是父子都会执行的,所以上面在子程序的else里面用了exit
echo 'hello world';
修改进程名称
function setProcessName($name)
{
// >=php 5.5
if (function_exists('cli_set_process_title')) {
@cli_set_process_title($name);
} elseif (extension_loaded('proctitle') && function_exists('setproctitle')) {
@setproctitle($name);
}
}
查看进程
ps -ef |grep pname/pid
进程组、会话
–进程组:
- 是一组进程的集合
- 一般是父进程fork了一组子进程,父进程其实就是组长
- 子进程可以没有组长而存在,也有权限将自己设置到其他组
–会话:
- 会话是多个进程组的集合
- 开始于用户登录,终止与用户退出,此期间所有进程都属于这个会话
php中启动daemon进程
<?php
//启动一个daemon进程,fork两次,一次创建子进程,一次脱离终端
function daemonize() {
//设置文件权限掩码
umask(0);
//fork child process
$pid = pcntl_fork();
if (-1 === $pid) {
throw new Exception('fork fail');
} elseif ($pid > 0) {
// parent child
exit(0);
}
//set child process to session leader
if (-1 === posix_setsid()) {
throw new Exception('setsid fail');
}
//fork again out terminal
$pid = pcntl_fork();
if (-1 === $pid) {
throw new Exception("fork fail");
} elseif (0 !== $pid) {
exit(0);
}
}
daemonize();
//下面的代码都是在daemon进程中执行了
echo 'hello world';
信号
<?php
//时钟间隔,每执行一代码都监听信号
declare(ticks = 1);
function sig_handler($signo) {
switch($signo) {
case SIGTERM:
echo "sigterm signal";
break;
case SIGHUP:
echo "sighup signal";
break;
case SIGUSR1:
echo "sigusr1 signal";
break;
}
}
//安装信号
echo "install signal";
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGHUP, "sig_handler");
pcntl_signal(SIGUSR1, "sig_handler");
//给自己发送一个杀死信号
posix_kill(posix_getpid(), SIGUSR1);
pcntl_signal_dispatch()和ticks完成了一样的功能
<?php
pcntl_signal(SIGINT,function($signo) {
echo "recv signal $signo";
});
while(1) {
sleep(1);
//调用等待信号处理器,和ticks完成一样的功能
pcntl_signal_dispatch();
}
<?php
$pid_arr = [];
for ($i = 0; $i < 10; $i++) {
$pid = pcntl_fork();
if ($pid == -1) {
die('fork fail');
} elseif ($pid > 0) {
echo 'parent pid=' . posix_getpid();
$pid_arr[] = $pid;
} elseif ($pid == 0) {
echo 'child pid=' . posix_getpid();
exit(0); //子进程退出
}
}
//如果存在子进程
if (count($pid_arr) > 0) {
foreach ($pid_arr as $_pid) {
//发送杀死信号到子进程
posix_kill($_pid, SIGTERM);
//等待子进程退出
$return_pid = pcntl_waitpid($_pid, $status);
if ($return_pid == $_pid) {
echo "process pid=$_pid stoped";
}
}
}
IPC(信号量,消息,共享内存)
–信号量
信号量是一个计数器,用户多进程对共享数据对象的存取。
信号量值为正,进程使用该资源,信号量减1,表明该进程已经使用了一个资源单位
若此信号量为0,则进程进入休眠状态,等信号量 +1后进程被唤醒
–消息
<?php
$msgkey = 123456;
//获取消息key
$msg_id = msg_get_queue($msgkey, 0600);
$content = "hello world";
//将消息发送到队列
if (!msg_send($msg_id, 1, $content, true, true, $msg_err)) {
echo "Msg not sent because $msg_err";
}
//消息key存在,则读取
if (msg_queue_exists($msgkey)) {
//读取
msg_receive($msg_id, 1, $msg_type, 16384, $message);
echo $message;
//读取后删除这条消息
msg_remove_queue($msg_id);
}
两个进程相互发送消息,并且接受消息的例子
class TestMsg {
public static $MSGKEY = 12345;
public function run() {
for ($i = 0; $i < 2; $i++) {
$pid = pcntl_fork();
if ($pid == -1) {
die('fork fail');
} elseif ($pid > 0) { //parent
//
} elseif ($pid == 0) {//child
$this->send_msg($i);
$tmpkey = ($i == 0) ? 1 : 0;
$message = $this->recv_msg($tmpkey);
echo "i=$i, message=$message || ";
exit(0);
}
}
}
public function send_msg($i) {
$msgkey = self::$MSGKEY+ $i;
$msg_id = msg_get_queue($msgkey);
$content = self::$MSGKEY . '_' . $i;
if (!msg_send ($msg_id, 1, $content, true, true, $msg_err)) {
echo "because $msg_err";
}
}
public function recv_msg($i) {
$msgkey = self::$MSGKEY+ $i;
$msg_id = msg_get_queue($msgkey);
while(1) {
if (msg_queue_exists($msgkey)) {
//读取
msg_receive($msg_id, 1, $msg_type, 16384, $message);
//读取后删除这条消息
msg_remove_queue($msg_id);
return $message;
}
sleep(2);
continue;
}
}
}
$msg = new TestMsg();
$msg->run();
查看ipc的一些命令
ipcs用法
ipcs -a 是默认的输出信息 打印出当前系统中所有的进程间通信方式的信息
ipcs -m 打印出使用共享内存进行进程间通信的信息
ipcs -q 打印出使用消息队列进行进程间通信的信息
ipcs -s 打印出使用信号进行进程间通信的信息
输出格式的控制
ipcs -t 输出信息的详细变化时间
ipcs -p 输出ipc方式的进程ID
ipcs -c 输出ipc方式的创建者/拥有者
ipcs -c 输出ipc各种方式的在该系统下的限制条件信息
ipcs -u 输出当前系统下ipc各种方式的状态信息(共享内存,消息队列,信号)
ipcrm 命令
移除一个消息对象。或者共享内存段,或者一个信号集,同时会将与ipc对象相关链的数据也一起移除。当然,只有超级管理员,或者ipc对象的创建者才有这项权利啦
ipcrm用法
ipcrm -M shmkey 移除用shmkey创建的共享内存段
ipcrm -m shmid 移除用shmid标识的共享内存段
ipcrm -Q msgkey 移除用msqkey创建的消息队列
ipcrm -q msqid 移除用msqid标识的消息队列
ipcrm -S semkey 移除用semkey创建的信号
ipcrm -s semid 移除用semid标识的信号
线程(pthread)
- 线程的概念
- php中创建多个线程,主线程概念
- 线程的查看
- 线程池设计
- 线程间通信、锁操作
- 线程如何申明本线程私有的数据
协程(coroutine)
- 协程的概念
协程:即是用户态完成程序的调度,像系统调度进程和线程一样,只不过是每次切换的时候又用户完成堆栈和寄存器中的数值的保存,并记录可以恢复到这个上下文执行。自己这样实现就是协程了。
-实现协程的几种方式
<ol><li>使用setjmp和longjmp跳转来实现上下文的恢复来实现协程。</li>
<li>使用ucontext库函数来实现协程。</li>
<li>云风的协程库,腾讯开源的libco协程库 - php中如何使用协程
- 协程适合的使用场景
常见的进程模型
- 单进程单线程
- 多进程
- 单进程多线程
- 单进程单线程协程
同步堵塞、异步非堵塞
- php中实现同步堵塞
-
php中实现异步非堵塞(IO多路复用 select/poll/epoll, libevent)
php中实现异步非堵塞
<?php
set_time_limit(0);
class EpollSocketServer
{
private static $socket;
private static $connections;
private static $buffers;
function EpollSocketServer ()
{
global $errno, $errstr;
if (!extension_loaded('libevent')) {
die("extension libevent no exists");
}
$socket_server = stream_socket_server("tcp://0.0.0.0:1234", $errno, $errstr);
if (!$socket_server) die("$errstr ($errno)");
stream_set_blocking($socket_server, 0); // 非阻塞
$base = event_base_new();
$event = event_new();
event_set($event, $socket_server, EV_READ | EV_PERSIST, array(__CLASS__, 'ev_accept'));
event_base_set($event, $base);
event_add($event);
event_base_loop($base);
}
function ev_accept($socket, $flag)
{
$conn = stream_socket_accept($socket, 60);
stream_set_blocking($conn, 0);
// Compatible with hhvm
if (function_exists('stream_set_read_buffer')) {
stream_set_read_buffer($conn, 0);
}
//第二次使用事件,我的坑爹的啊
$base = event_base_new();
$event = event_new();
event_set($event, $conn, EV_READ | EV_PERSIST, array(__CLASS__, 'ev_accept2', $base));
event_base_set($event, $base);
event_add($event);
event_base_loop($base);
}
public function ev_accept2($socket, $flag, $event) {
$buffer = fread($socket, 1024);
if ($buffer === '' || $buffer === false) {
event_del($event);
return;
}
fwrite($socket, $buffer);
}
}
new EpollSocketServer();