惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
O
OpenAI News
Engineering at Meta
Engineering at Meta
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
WordPress大学
WordPress大学
宝玉的分享
宝玉的分享
GbyAI
GbyAI
T
The Blog of Author Tim Ferriss
Google DeepMind News
Google DeepMind News
B
Blog RSS Feed
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
云风的 BLOG
云风的 BLOG
罗磊的独立博客
S
SegmentFault 最新的问题
The Register - Security
The Register - Security
Hugging Face - Blog
Hugging Face - Blog
D
DataBreaches.Net
U
Unit 42
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
B
Blog
阮一峰的网络日志
阮一峰的网络日志
P
Proofpoint News Feed
雷峰网
雷峰网
V
Visual Studio Blog
小众软件
小众软件
aimingoo的专栏
aimingoo的专栏
N
Netflix TechBlog - Medium
酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Y
Y Combinator Blog
博客园 - 【当耐特】
G
Google Developers Blog
L
LangChain Blog
Stack Overflow Blog
Stack Overflow Blog
I
InfoQ
Martin Fowler
Martin Fowler
F
Fortinet All Blogs
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
The Cloudflare Blog
AI
AI
Google Online Security Blog
Google Online Security Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
博客园 - Franky
Blog — PlanetScale
Blog — PlanetScale
Webroot Blog
Webroot Blog
PCI Perspectives
PCI Perspectives
爱范儿
爱范儿
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org

文章列表 - 她和她的猫

使用小鹤双拼一个月 折腾 Hugo PaperMod 主题 去长鹿旅游休博园 1Password 自动化登录堡垒机 深入 Hyperf:Inject 注解是如何工作的? 译|理解容器镜像层 使用 Rust 实现高性能预写日志(Write Ahead Log) 译|使用 Linux 命名空间隔离系统 深入 Hyperf:HTTP 服务是如何处理请求的? Linux 文件权限学习笔记 译|了解 Bitcask:基于日志结构的 KV 存储引擎 还原 SM2 压缩公钥的几种方法 深入 Hyperf:HTTP 服务启动时发生了什么? 一次 Hyperf 注解失效问题分析 将博客迁移到又拍云 译|PHP 7 新的 Hashtable 实现 排查 ES 查询问题:深入了解 json_encode() 函数 浅析 Redlock 分布式锁实现原理 Yar 源码阅读笔记:RPC 服务端 Yar 源码阅读笔记:客户端的并行调用 Yar 源码阅读笔记:客户端的同步调用 Yar 源码阅读笔记:数据传输模块 Yar 源码阅读笔记:消息编码模块 Yar 源码阅读笔记:RPC 通信协议 Yar 源码阅读笔记:开篇 PHP 8 新特性介绍 基于 OpenSSL 实现国密 SM4 加解密 PHP 源码阅读笔记:编译与调试 PHP 什么是惊群问题 大端模式和小端模式 基于 GitHub Actions 定时推送网址到百度站长平台 Workerman 源码分析:文件上传 浅入浅出 HTTP 协议 PHP 网络编程:构建 MySQL 蜜罐获取攻击者微信 ID 使用 Workerman 接入 Bilibili 直播弹幕协议 使用 GitHub Actions 自动部署 Hexo 什么是二进制安全 【转载】PHP 程序员进阶之路 优化 Workerman 检查主进程是否存活的逻辑 基于 Redis 实现分布式锁 《PHP 实现 Base64 编码/解码》笔记 PHP 多进程下载必应壁纸 基于 Redis 实现延迟队列 【转载】PHP 实现 Base64 编码/解码 使用 MySQL FIELD() 函数自定义排序规则 Laravel 的 Facades 实现原理 C语言单链表实现约瑟夫环 数据结构学习笔记:顺序栈和链栈(C语言) 使用 QueryList + Redis 下载壁纸 在 Laravel 登录/注册中使用 mews/captcha 扩展包 使用 mews/captcha 扩展包为 Laravel 应用添加图片验证码 给 YOURLS 短网址系统编写插件《批量生成短网址》 给 YOURLS 短网址系统编写插件《Hello World!》 PHP 7.1 使用 json_encode 函数造成浮点类型数据出现精度问题 Laravel 授权策略(Policy)的基本使用 Ubuntu 系统下 WiFi 频繁掉线解决方法 Laravel 应用部署到 Nginx 服务器上的第一个坑 Ubuntu 16.04 LST 安装 Redis 和 Composer PHP 采集逐浪小说章节列表 Nginx 环境 SSL 强制 HTTP 301 跳转到 HTTPS 【工作&生活】第一周 ServiceStack.Redis 与多线程 Count the number of Duplicates C# Base64 加密及解密 ASP.NET MVC 使用动态类型传递数据 Django 使用 order_by() 对数据进行排序操作 ASP.NET + jQuery + AJAX 实现用户登录 ASP.NET 从客户端中检测到有潜在危险的 Request.Form 值 ASP.NET 使用 FormData 实现 AJAX 上传图片 Python 使用 BeautifulSoup 抓取网页 贪心算法之字符串的完美度 一言不和就造轮子之 CookieHelper ASP.NET 上传图片 DBHelper 类 C# 捕获屏幕源码 用 JavaScript 做一些有趣的事情 第一篇文章 好久不见。 如何防止非法调用 PHP 文件 清明小记。 PHP 生成随机字符串 Z-Blog PHP 程序做 301 跳转 PHP 处理数组常用的几个函数 Sublime Text 3 快捷键总结 PHP 使用 file_get_contents() 函数实现采集网页 使用 C# 编写学生信息管理系统 终于到了。 zblog报错: magic_quotes_gpc 和 safe_mode 世事无常。 如何使用 PHP 上传文件 安卓手机编写和运行 PHP 程序的软件 使用 AJAX 刷新、验证 PHP 图片验证码 PHP 生成图片验证码 PHP 之 session 的使用方法 PHP 之 cookie 的使用方法 PHP 实现分页原理详解 Thinkphp 框架中系统常量在 Js 文件中不解析的解决方法
聊聊五种 I/O 模型
她和她的猫 · 2021-05-12 · via 文章列表 - 她和她的猫

I/O 是 Input/Output 的简写,即输入/输出,是计算机与外部设备(键盘、鼠标、磁盘等)通信的统称,与具体实现无关。

与外部设备的通信其实就是对外部设备进行读取或写入数据的过程,比如对文件的读写操作可以称为文件 I/O、对套接字的读写操作称为网络 I/O。

同步 I/O 和异步 I/O

同步和异步是相对于获取数据的过程而言的。

  • 同步 I/O:包含阻塞 I/O、非阻塞 I/O、I/O 多路复用、信号驱动式I/O 四种 I/O 模型,它们的共同点就是在执行 I/O 操作时会阻塞进程,直到 I/O 操作完成。
  • 异步 I/O:执行 I/O 操作时不会阻塞进程。

在用户进程中执行 read 系统调用时,内核将数据从内核空间拷贝用户进程空间。如果没有读到数据,那么进程会一直处于阻塞状态,当读取到数据时才会恢复进程,继续执行后面的逻辑,所以我们称这个操作是同步的。

异步只需要执行 aio_read 系统调用告诉内核从哪儿读取数据就可以了,进程不用等待立即返回,内核会异步地将数据从内核空间拷贝到用户进程空间。

阻塞 I/O 模型

套接字在创建时默认就是阻塞的。

用户进程执行 read 系统调用,如果数据没有准备好,那么进程将会被挂起,直到数据准备好之后内核将数据拷贝给用户进程。

举一个烧开水的例子,水壶是套接字,水壶里的水是数据,水未烧开说明数据没有准备好。

现在有 A、B、C 三个水壶,进程查询 A 的状态,发现水还没有烧开就一直等着水烧开,哪怕 B、C 的水烧开了也不管,直到 A 的水烧开了才会去处理下一个水壶。

缺点:每次只能对一个套接字进行操作,就算其它套接字数据准备好了也没办法立即处理。

非阻塞 I/O 模型

在 PHP 中可以调用 socket_set_nonblock 函数将套接字设置为非阻塞的。

非阻塞 I/O 在数据未准备好的情况下,执行 read 系统调用将会立即返回,应用程序可以使用循环不停地轮训内核,直到数据准备好,内核将数据从拷贝到应用程序中。

伪代码:

while (true) {
    rbytes = read(fd);
    if (rbytes < 0 && errno == EWOULDBLOCK) {
        continue;
    }
    // 处理数据
}

用烧开水的例子来解释就是,进程不断地轮询每个水壶的状态,当某个水壶的水烧开了之后就执行下一步操作。

缺点:需要不停地轮询内核,浪费系统资源。

I/O 多路复用模型

多路复用就是多个网络请求复用同一个进程,让单进程的应用程序拥有了同时处理多个套接字的能力,避免不停地轮询内核,造成资源浪费。

将需要监听的套接字交给内核,然后进程被挂起陷入休眠状态,当套接字数据准备好时,内核将对应的套接字及事件返回给进程并唤醒进程,进程就可以执行 read 系统调用读取数据,这个时候数据肯定是准备好的。

目前常见的有三种多路复用机制,分别是 select、poll、epoll。

多路复用机制平台支持底层实现时间复杂度最大连接数fd 拷贝
selectLinux/Windows数组O(n)1024每次调用 select 都需要从用户进程拷贝到内核
pollLinux链表O(n)无上限每次调用 poll 都需要从用户进程拷贝到内核
epollLinux红黑树O(1)无上限调用 epoll_ctl 时需要从用户进程拷贝到内核,epoll_wait 不需要

从上表可以看出 epoll 性能最好,所以当程序运行在 Linux 系统上应该使用 epoll,如果是 Windows 可以使用 select,这样我们就可以实现跨平台的多路复用应用程序。

烧开水的例子:将 A、B、C 三个水壶交给内核,当有水烧开时内核就通知进程哪个水壶烧开了。

I/O 多路复用中的套接字必须设置为非阻塞的。

信号驱动式 I/O 模型

调用 sigaction 安装 SIGIO 信号处理器,为套接字设置宿主进程,当套接字的数据准备好时,操作系统会触发 SIGIO I/O 就绪信号,就会执行安装的信号处理器,在信号处理器中执行 I/O 操作。

进程会安装 SIGIO 信号的处理函数,让内核有数据准备好时就触发 SIGIO 信号,并执行该信号对应的处理函数。

用水壶的例子来解释就是给每个水壶都安装了一个蜂鸣器,当水烧开时就开始响,进程就知道哪个水壶烧开了。

异步 I/O 模型

前面这四种 I/O 模型都是同步 I/O,不管是进程是如何知道数据是否准备好的,最终执行 read 系统调用从内核拷贝数据到用户进程的过程是同步的,而异步 I/O 的区别就在于这里。

异步 I/O 的读写操作都是立即返回的,读写操作由内核异步地执行,数据拷贝的过程不会阻塞用户进程。

相当于告诉内核,水烧开了就倒在这个杯子里,我想喝水了就自己去喝。

总结

同步 I/O 和异步 I/O 的区别:内核将数据从内核空间拷贝到用户进程空间时,是否会阻塞用户进程。 阻塞 I/O 和非阻塞 I/O 的区别:数据没有准备好的时候是否会阻塞用户进程。