文章目录
五大 I/O 模型
对于一次 IO 操作,数据会先被拷贝进操作系统内核空间的缓冲区中,然后才会从内核缓冲区中拷贝到用户空间。所以说,当一个 IO 操作发生时,它会经历两个阶段:
- 等待数据准备。
- 将数据从内核拷贝到用户空间
根据这两个阶段中用户进程等待的不同方式,可以分为以下五种 I/O 模型:
- 阻塞 I/O
- 非阻塞 I/O
- I/O 多路复用
- 信号驱动 I/O
- 异步 I/O
阻塞 I/O
原理介绍
当用户进程调用 recvfrom 系统函数,内核就开始了 IO 的第一个阶段:数据准备(对于网络 IO 来说,很多时候在一开始还没有数据的存在,内核需要在磁盘中读取数据并存放在内核中的缓冲区中)。数据准备完成后,内核会把在内核缓冲区中准备好的数据复制到用户空间的内存中。而在数据准备到数据复制到用户空间的整个过程中,用户进程一直是阻塞的。直到将数据复制到用户空间完成,内核返回成功,用户进程解除阻塞状态从而继续运行。
代码实现
-
blocking_nonblocking_io_server.php
服务端代码<?php class Worker { public $onConnect; public $onMessage; public function __construct($addr) { $this->socket = stream_socket_server($addr); echo "{ $addr}服务已经启动\r\n"; } public function start() { $this->accept(); } public function accept() { while (true) { $client = stream_socket_accept($this->socket); if (is_callable($this->onConnect)) { ($this->onConnect)($this, $client); } $data = fread($client, 65535); if (is_callable($this->onMessage)) { ($this->onMessage)($this, $client, $data); } fclose($client); } } public function sentMessage($client, $data) { $response = "HTTP/1.1 200 OK\r\n"; $response .= "Content-Type: text/html;charset=UTF-8\r\n"; $response .= "Connection: keep-alive\r\n"; $response .= "Content-length: " . strlen($data) . "\r\n\r\n"; $response .= $data; fwrite($client, $response); } } // 调用 $address = 'tcp://0.0.0.0:9000'; $server = new Worker($address); $server->onConnect = function ($serv, $client) { // 模拟内核准备数据,延迟5秒钟 sleep(5); $serv->sentMessage($client, 'hello client' . "\n"); }; $server->onMessage = function ($serv, $client, $data) { echo "收到來自客戶端的消息\n"; }; $server->start();
-
blocking_client.php
阻塞模型的客户端代码<?php $address = 'tcp://127.0.0.1:9000'; // 创建客户端套接字,默认为阻塞模型 $client = stream_socket_client($address); $time = time(); fwrite($client, 'hello server'); echo fread($client, 65536); echo "其他业务\r\n"; $m = time() - $time; echo