颤抖开篇,从php角度谈谈IO模型(BIO)

文章详细介绍了IO的基本概念,特别是阻塞IO(BIO)的工作原理,包括数据准备和数据拷贝两个阶段。在BIO模型中,阻塞主要发生在数据准备阶段,当用户线程尝试读取内核Socket缓冲区无数据时会进入阻塞状态。文章还讨论了BIO在处理多客户端连接时的限制,并给出了通过fork多进程来改善的问题,但指出这可能导致大量进程切换的开销。最后,提出了预创建固定数量子进程的资源池化解决方案,以适应并发较低的业务场景。

IO 是什么?

在计算机系统中I/O就是输入(input)和输出(Output)的意思。针对不同的操作对象,可以划分为磁盘I/O模型,网络I/O模型,内存映射I/O,Direct I/O、数据库I/O等,只要具有输入输出类型的交互系统都可以认为是I/O系统,也可以说是整个操作系统数据交换与人机交互的通道,这个概念与选用的开发语言没有关系,是一个通用概念。

谈谈 (阻塞)

在学习 IO 中必须要搞懂的几个概念:(阻塞,非阻塞)与(同步,异步)

本篇文章只介绍阻塞,其余几个概念将在后面篇章中挨个介绍学习。

在了解阻塞IO前,我们先看看网络数据包接收流程,在这里我们可以将整个流程总结为两个阶段:

图片
数据接收阶段.png

  • 数据准备阶段:
    在这个阶段,网络数据包到达网卡,通过DMA的方式将数据包拷贝到内存中,然后经过硬中断,软中断,接着通过内核线程ksoftirqd经过内核协议栈的处理,最终将数据发送到内核Socket的接收缓冲区中。

  • 数据拷贝阶段: 当数据到达内核Socket的接收缓冲区中时,此时数据存在于内核空间中,需要将数据拷贝到用户空间中,才能够被应用程序读取。

阻塞

阻塞主要发生在第一阶段:数据准备阶段。

当应用程序发起系统调用 read 读操作时,线程从用户态转为内核态,读取内核Socket的接收缓冲区中的网络数据。

如果这时内核Socket的接收缓冲区没有数据,那么线程就会一直等待,直到Socket接收缓冲区有数据为止。随后将数据从内核空间拷贝到用户空间,系统调用 read 返回。

在这里插入图片描述

从图中我们可以看出:阻塞的特点是在第一阶段和第二阶段都会等待。

阻塞IO(BIO)

在这里插入图片描述

经过前一小节对阻塞这个概念的介绍,相信大家可以很容易理解阻塞IO的概念和过程。

既然这小节我们谈的是IO,那么下边我们来看下在阻塞IO模型下,网络数据的读写过程。

阻塞读

当用户线程发起read系统调用,用户线程从用户态切换到内核态,在内核中去查看Socket接收缓冲区是否有数据到来。

Socket接收缓冲区中有数据,则用户线程在内核态将内核空间中的数据拷贝到用户空间,系统IO调用返回。

Socket接收缓冲区中无数据,则用户线程让出CPU,进入阻塞状态。当数据到达Socket接收缓冲区后,内核唤醒阻塞状态中的用户线程进入就绪状态,随后经过CPU的调度获取到CPU quota进入运行状态,将内核空间的数据拷贝到用户空间,随后系统调用返回。

阻塞写

当用户线程发起send系统调用时,用户线程从用户态切换到内核态,将发送数据从用户空间拷贝到内核空间中的Socket发送缓冲区中。

当Socket发送缓冲区能够容纳下发送数据时,用户线程会将全部的发送数据写入Socket缓冲区,然后执行在网络包发送流程,然后返回。

当Socket发送缓冲区空间不够,无法容纳下全部发送数据时,用户线程让出CPU,进入阻塞状态,直到Socket发送缓冲区能够容纳下全部发送数据时,内核唤醒用户线程,执行后续发送流程。

阻塞IO模型下的写操作做事风格比较硬刚,非得要把全部的发送数据写入发送缓冲区才肯善罢甘休。

由于BIO 的阻塞特性,想让BIO 同时为多个客户端服务,每个请求都需要被一个独立的线程处理。一个线程在同一时刻只能与一个连接绑定。来一个请求,服务端就需要创建一个线程用来处理请求。

阻塞IO模型

在这里插入图片描述

在早期 Java 中 BIO 的实现就是一个客户端连接创建一个线程来处理请求,由于php 对多线程支持不是特别好,在php 中如何实现BIO呢? 咱们先来个例子

// 创建 ipv4 tcp socket
$sockfd = socket_create(AF_INET, SOCK_STREAM, 0);
if (!is_resource($sockfd)) {
   
   
   fprintf(STDOUT, "socket create fail:%s\n", socket_strerror(socket_last_error($this->_sockfd)));

}
// 绑定地址 端口
socket_bind($sockfd,'0.0.0.0',6379);
// 监听 socket
socket_listen($sockfd,10);

while (1){
   
   
   // 第一个阻塞函数   获取客户端连接,没有客户端连接会一直阻塞,阻塞状态的进程是不会占据CPU的
   $connfd = socket_accept($sockfd);
   if
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值