进程、线程、协程、异步、非堵塞IO,多路复用详解

未完待续…
##进程(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

####进程组、会话
–进程组:

  1. 是一组进程的集合
  2. 一般是父进程fork了一组子进程,父进程其实就是组长
  3. 子进程可以没有组长而存在,也有权限将自己设置到其他组

–会话:

  1. 会话是多个进程组的集合
  2. 开始于用户登录,终止与用户退出,此期间所有进程都属于这个会话

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

  • 协程的概念
协程:即是用户态完成程序的调度,像系统调度进程和线程一样,只不过是每次切换的时候又用户完成堆栈和寄存器中的数值的保存,并记录可以恢复到这个上下文执行。自己这样实现就是协程了。

-实现协程的几种方式

1. 使用setjmp和longjmp跳转来实现上下文的恢复来实现协程。
2. 使用ucontext库函数来实现协程。
3. 云风的协程库,腾讯开源的libco协程库
  • php中如何使用协程
  • 协程适合的使用场景

##常见的进程模型

  1. 单进程单线程
  2. 多进程
  3. 单进程多线程
  4. 单进程单线程协程

##多路复用

有一篇文章一看就懂
地址:
多路复用图解
概念

多路复用通常表示在一个信道上传输多路信号或数据流的过程和技术

在这里插入图片描述

在这里插入图片描述
##同步、异步,阻塞,非阻塞

  • php中实现同步堵塞
  • php中实现异步非堵塞(IO多路复用 select/poll/epoll, libevent)
在做网络编程的时候,我们会经常碰到阻塞(Blocking)与非阻塞(Non-Blocking)、同步(Synchronous)与异步(Asynchronous)的概念。这两组概念很相近,容易混淆。

阻塞和非阻塞是用来描述I/O操作的:在执行 I/O操作时,如果数据没有准备好,阻塞模式下会一直等待直到准备好或者超时,非阻塞模式下会立即返回。

同步与异步是用来描述消息传递方式:同步模式下,接收方需要不断地主动向发送方查询消息;异步模式下,发送方会主动将消息送给接收方。
体现在程序上就是线程卡主,一个线程不卡主

本质上两组概念是没有关联的,不过我们会经常看到异步非阻塞的词语,这是因为在实际I/O处理时都有应用场景。处理I/O操作时,需要及时知道I/O状态及操作结果等消息。在Server端,通常会用异步的消息传递方式及非阻塞I/O以提高并发吞吐效率

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

##apache的进程模型

##nginx的进程模型

##tomcat的进程模型

##mysql的进程模型

##memcache和redis的进程模型
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值