Update Socket.md

重新绘制 I/O 模型的配图,更新非阻塞 I/O 和 select 示例的描述。
This commit is contained in:
CyC2018
2026-10-26 22:04:01 +08:00
parent b8200eda75
commit 3bbc964708
13 changed files with 388 additions and 20 deletions
+31 -20
View File
@@ -48,15 +48,15 @@ Unix 有五种 I/O 模型:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
```
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/1492928416812_4.png"/> </div><br>
<div align="center"> <img src="pics/io-blocking.png" width="760px"/> </div><br>
### 非阻塞式 I/O
应用进程执行系统调用之后,内核返回一个错误码。应用进程可以继续执行,但是需要不断的执行系统调用来获知 I/O 是否完成,这种方式称为轮询(polling)。
应用进程执行系统调用之后,如果数据还没有准备好,内核立即返回错误码 EAGAIN(也写作 EWOULDBLOCK)。应用进程可以继续执行,但是需要不断的执行系统调用来获知 I/O 是否完成,这种方式称为轮询(polling)。
由于 CPU 要处理更多的系统调用,因此这种模型的 CPU 利用率比较低。
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/1492929000361_5.png"/> </div><br>
<div align="center"> <img src="pics/io-nonblocking.png" width="760px"/> </div><br>
### I/O 复用
@@ -66,7 +66,7 @@ ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *
如果一个 Web 服务器没有 I/O 复用,那么每一个 Socket 连接都需要创建一个线程去处理。如果同时有几万个连接,那么就需要创建相同数量的线程。相比于多进程和多线程技术,I/O 复用不需要进程线程创建和切换的开销,系统开销更小。
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/1492929444818_6.png"/> </div><br>
<div align="center"> <img src="pics/io-multiplex.png" width="760px"/> </div><br>
### 信号驱动 I/O
@@ -74,7 +74,7 @@ ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *
相比于非阻塞式 I/O 的轮询方式,信号驱动 I/O 的 CPU 利用率更高。
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/1492929553651_7.png"/> </div><br>
<div align="center"> <img src="pics/io-signal.png" width="760px"/> </div><br>
### 异步 I/O
@@ -82,7 +82,7 @@ ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *
异步 I/O 与信号驱动 I/O 的区别在于,异步 I/O 的信号是通知应用进程 I/O 完成,而信号驱动 I/O 的信号是通知应用进程可以开始 I/O。
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/1492930243286_8.png"/> </div><br>
<div align="center"> <img src="pics/io-async.png" width="760px"/> </div><br>
### 五大 I/O 模型比较
@@ -93,7 +93,7 @@ ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *
非阻塞式 I/O 、信号驱动 I/O 和异步 I/O 在第一阶段不会阻塞。
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/1492928105791_3.png"/> </div><br>
<div align="center"> <img src="pics/io-compare.png" width="760px"/> </div><br>
## 二、I/O 复用
@@ -138,17 +138,21 @@ tv.tv_usec = 0;
int ret = select( largest_sock + 1, &fd_in, &fd_out, NULL, &tv );
// Check if select actually succeed
if ( ret == -1 )
if ( ret == -1 ) {
// report error and abort
else if ( ret == 0 )
}
else if ( ret == 0 ) {
// timeout; no event detected
}
else
{
if ( FD_ISSET( sock1, &fd_in ) )
if ( FD_ISSET( sock1, &fd_in ) ) {
// input event on sock1
}
if ( FD_ISSET( sock2, &fd_out ) )
if ( FD_ISSET( sock2, &fd_out ) ) {
// output event on sock2
}
}
```
@@ -184,12 +188,14 @@ fds[1].fd = sock2;
fds[1].events = POLLOUT;
// Wait 10 seconds
int ret = poll( &fds, 2, 10000 );
int ret = poll( fds, 2, 10000 );
// Check if poll actually succeed
if ( ret == -1 )
if ( ret == -1 ) {
// report error and abort
else if ( ret == 0 )
}
else if ( ret == 0 ) {
// timeout; no event detected
}
else
{
// If we detect the event, zero it out so we can reuse the structure
@@ -234,7 +240,7 @@ epoll_ctl() 用于向内核注册新的描述符或者是改变某个文件描
从上面的描述可以看出,epoll 只需要将描述符从进程缓冲区向内核缓冲区拷贝一次,并且进程不需要通过轮询来获得事件完成的描述符。
<div align="center"> <img src="pics/epoll.png" width="600px"/> </div><br>
<div align="center"> <img src="pics/epoll.png" width="640px"/> </div><br>
epoll 仅适用于 Linux OS。
@@ -247,8 +253,9 @@ epoll 对多线程编程更加友好,一个线程调用了 epoll_wait() 另一
// The function argument is ignored (it was not before, but now it is), so put your favorite number here
int pollingfd = epoll_create( 0xCAFE );
if ( pollingfd < 0 )
// report error
if ( pollingfd < 0 ) {
// report error
}
// Initialize the epoll structure in case more members are added in future
struct epoll_event ev = { 0 };
@@ -261,8 +268,9 @@ ev.data.ptr = pConnection1;
ev.events = EPOLLIN | EPOLLONESHOT;
// Add the descriptor into the monitoring list. We can do it even if another thread is
// waiting in epoll_wait - the descriptor will be properly added
if ( epoll_ctl( epollfd, EPOLL_CTL_ADD, pConnection1->getSocket(), &ev ) != 0 )
if ( epoll_ctl( pollingfd, EPOLL_CTL_ADD, pConnection1->getSocket(), &ev ) != 0 ) {
// report error
}
// Wait for up to 20 events (assuming we have added maybe 200 sockets before that it may happen)
struct epoll_event pevents[ 20 ];
@@ -270,10 +278,12 @@ struct epoll_event pevents[ 20 ];
// Wait for 10 seconds, and retrieve less than 20 epoll_event and store them into epoll_event array
int ready = epoll_wait( pollingfd, pevents, 20, 10000 );
// Check if epoll actually succeed
if ( ret == -1 )
if ( ready == -1 ) {
// report error and abort
else if ( ret == 0 )
}
else if ( ready == 0 ) {
// timeout; no event detected
}
else
{
// Check if any events detected
@@ -337,3 +347,4 @@ poll 没有最大描述符数量的限制,如果平台支持并且对实时性
- [poll vs select vs event-based](https://daniel.haxx.se/docs/poll-vs-select.html)
- [select / poll / epoll: practical difference for system architects](http://www.ulduzsoft.com/2014/01/select-poll-epoll-practical-difference-for-system-architects/)
- [Browse the source code of userspace/glibc/sysdeps/unix/sysv/linux/ online](https://code.woboq.org/userspace/glibc/sysdeps/unix/sysv/linux/)
- [epoll(7) — Linux manual page](https://man7.org/linux/man-pages/man7/epoll.7.html)
Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

+55
View File
@@ -0,0 +1,55 @@
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="380" viewBox="0 0 760 380">
<style>
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
.b { font-weight: bold; }
.c { text-anchor: middle; }
.e { text-anchor: end; }
.sub { font-size: 11px; fill: #6E6E6E; }
.note { fill: #C0605A; }
.ok { fill: #6A9F68; }
.mono { font-family: Menlo, Consolas, monospace; }
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
.red { stroke: #C0605A; marker-end: url(#r); }
.green { stroke: #6A9F68; marker-end: url(#g); }
.dash { stroke-dasharray: 5 4; }
.line { stroke: #555; stroke-width: 1.1; fill: none; }
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
.shadow { fill: #DADADA; }
</style>
<defs>
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
</marker>
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
</marker>
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
</marker>
</defs>
<rect width="100%" height="100%" fill="#FFFFFF"/>
<text x="380" y="28" class="title">异步 I/O</text>
<rect x="120" y="46" width="160" height="30" rx="6" fill="#C6ECE9" stroke="#7DBCB7"/>
<text x="200" y="65.5" class="c sub b">应用进程</text>
<rect x="460" y="46" width="160" height="30" rx="6" fill="#C6E4FB" stroke="#7FB2D6"/>
<text x="540" y="65.5" class="c sub b">内核</text>
<line x1="200" y1="78" x2="200" y2="330" stroke="#BBB" stroke-dasharray="4 4"/>
<line x1="540" y1="78" x2="540" y2="330" stroke="#BBB" stroke-dasharray="4 4"/>
<rect x="554" y="96" width="26" height="94" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
<text x="590" y="139" class="sub b" style="fill:#444">① 等待数据</text>
<text x="590" y="155" class="sub">例如等对方发来 hello</text>
<rect x="554" y="244" width="26" height="46" rx="3" fill="#E6E3FA" stroke="#B8B0DE" stroke-width="1"/>
<text x="590" y="270" class="sub b" style="fill:#444">② 复制到用户缓冲区</text>
<text x="554" y="214" class="sub">数据到达</text>
<line x1="200" y1="100" x2="540" y2="100" class="arrow "/>
<text x="370" y="95" class="c sub mono" style="fill:#444">aio_read(fd, buf, 1024)</text>
<line x1="540" y1="114" x2="200" y2="114" class="arrow dash"/>
<text x="370" y="109" class="c sub" style="fill:#444">立即返回</text>
<rect x="193" y="118" width="14" height="172" rx="3" fill="#C6ECE9" stroke="#7DBCB7"/>
<text x="186" y="208" class="sub ok e">继续执行其它代码</text>
<line x1="540" y1="290" x2="200" y2="290" class="arrow dash"/>
<text x="370" y="285" class="c sub" style="fill:#444">完成通知:buf 中已经是 hello</text>
<text x="30" y="356" class="sub">两个阶段都由内核完成,进程收到通知时数据已经在自己的缓冲区中。Linux 上实际的异步 I/O 多用 io_uring。</text>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

+53
View File
@@ -0,0 +1,53 @@
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="380" viewBox="0 0 760 380">
<style>
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
.b { font-weight: bold; }
.c { text-anchor: middle; }
.e { text-anchor: end; }
.sub { font-size: 11px; fill: #6E6E6E; }
.note { fill: #C0605A; }
.ok { fill: #6A9F68; }
.mono { font-family: Menlo, Consolas, monospace; }
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
.red { stroke: #C0605A; marker-end: url(#r); }
.green { stroke: #6A9F68; marker-end: url(#g); }
.dash { stroke-dasharray: 5 4; }
.line { stroke: #555; stroke-width: 1.1; fill: none; }
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
.shadow { fill: #DADADA; }
</style>
<defs>
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
</marker>
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
</marker>
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
</marker>
</defs>
<rect width="100%" height="100%" fill="#FFFFFF"/>
<text x="380" y="28" class="title">阻塞式 I/O</text>
<rect x="120" y="46" width="160" height="30" rx="6" fill="#C6ECE9" stroke="#7DBCB7"/>
<text x="200" y="65.5" class="c sub b">应用进程</text>
<rect x="460" y="46" width="160" height="30" rx="6" fill="#C6E4FB" stroke="#7FB2D6"/>
<text x="540" y="65.5" class="c sub b">内核</text>
<line x1="200" y1="78" x2="200" y2="330" stroke="#BBB" stroke-dasharray="4 4"/>
<line x1="540" y1="78" x2="540" y2="330" stroke="#BBB" stroke-dasharray="4 4"/>
<rect x="554" y="96" width="26" height="94" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
<text x="590" y="139" class="sub b" style="fill:#444">① 等待数据</text>
<text x="590" y="155" class="sub">例如等对方发来 hello</text>
<rect x="554" y="244" width="26" height="46" rx="3" fill="#E6E3FA" stroke="#B8B0DE" stroke-width="1"/>
<text x="590" y="270" class="sub b" style="fill:#444">② 复制到用户缓冲区</text>
<text x="554" y="214" class="sub">数据到达</text>
<line x1="200" y1="100" x2="540" y2="100" class="arrow "/>
<text x="370" y="95" class="c sub mono" style="fill:#444">recvfrom(fd, buf, 1024)</text>
<rect x="193" y="102" width="14" height="188" rx="3" fill="#F3C6C2" stroke="#D99A94"/>
<text x="186" y="200" class="sub note e">两个阶段都阻塞</text>
<line x1="540" y1="290" x2="200" y2="290" class="arrow dash"/>
<text x="370" y="285" class="c sub" style="fill:#444">返回 5(读到 hello)</text>
<text x="30" y="356" class="sub">本机测试:对方 300 ms 后才发送,recv 阻塞 0.31 秒后返回。等待期间线程不占用 CPU,但也不能做别的事。</text>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

+66
View File
@@ -0,0 +1,66 @@
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="330" viewBox="0 0 760 330">
<style>
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
.b { font-weight: bold; }
.c { text-anchor: middle; }
.e { text-anchor: end; }
.sub { font-size: 11px; fill: #6E6E6E; }
.note { fill: #C0605A; }
.ok { fill: #6A9F68; }
.mono { font-family: Menlo, Consolas, monospace; }
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
.red { stroke: #C0605A; marker-end: url(#r); }
.green { stroke: #6A9F68; marker-end: url(#g); }
.dash { stroke-dasharray: 5 4; }
.line { stroke: #555; stroke-width: 1.1; fill: none; }
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
.shadow { fill: #DADADA; }
</style>
<defs>
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
</marker>
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
</marker>
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
</marker>
</defs>
<rect width="100%" height="100%" fill="#FFFFFF"/>
<text x="380" y="28" class="title">五种 I/O 模型的比较</text>
<text x="108" y="129" class="sub b e" style="fill:#444">① 等待数据</text>
<text x="108" y="224" class="sub b e" style="fill:#444">② 复制数据</text>
<line x1="110" y1="180" x2="720" y2="180" stroke="#DDD" stroke-dasharray="4 4"/>
<text x="180" y="58" class="c b" style="fill:#444">阻塞式</text>
<rect x="172.0" y="76" width="16" height="98" rx="3" fill="#F3C6C2" stroke="#D99A94"/>
<text x="194" y="129" class="sub">阻塞</text>
<rect x="172.0" y="186" width="16" height="68" rx="3" fill="#F3C6C2" stroke="#D99A94"/>
<text x="194" y="224" class="sub">阻塞</text>
<text x="300" y="58" class="c b" style="fill:#444">非阻塞式</text>
<rect x="292.0" y="76" width="16" height="98" rx="3" fill="#FFFFFF" stroke="#D99A94" stroke-dasharray="4 3"/>
<text x="314" y="129" class="sub">反复轮询</text>
<rect x="292.0" y="186" width="16" height="68" rx="3" fill="#F3C6C2" stroke="#D99A94"/>
<text x="314" y="224" class="sub">阻塞</text>
<text x="420" y="58" class="c b" style="fill:#444">I/O 复用</text>
<rect x="412.0" y="76" width="16" height="98" rx="3" fill="#F3C6C2" stroke="#D99A94"/>
<text x="434" y="129" class="sub">阻塞在 select</text>
<rect x="412.0" y="186" width="16" height="68" rx="3" fill="#F3C6C2" stroke="#D99A94"/>
<text x="434" y="224" class="sub">阻塞</text>
<text x="540" y="58" class="c b" style="fill:#444">信号驱动</text>
<rect x="532.0" y="76" width="16" height="98" rx="3" fill="#C6ECE9" stroke="#7DBCB7"/>
<text x="554" y="129" class="sub">不阻塞</text>
<rect x="532.0" y="186" width="16" height="68" rx="3" fill="#F3C6C2" stroke="#D99A94"/>
<text x="554" y="224" class="sub">阻塞</text>
<text x="660" y="58" class="c b" style="fill:#444">异步</text>
<rect x="652.0" y="76" width="16" height="98" rx="3" fill="#C6ECE9" stroke="#7DBCB7"/>
<text x="674" y="129" class="sub">不阻塞</text>
<rect x="652.0" y="186" width="16" height="68" rx="3" fill="#C6ECE9" stroke="#7DBCB7"/>
<text x="674" y="224" class="sub">不阻塞</text>
<path d="M126,272 q0,8 8,8 H592 q8,0 8,-8" class="line"/>
<text x="360" y="296" class="c sub">同步 I/O:第二阶段由进程自己 recvfrom,都会阻塞</text>
<text x="660" y="296" class="c sub">异步 I/O</text>
<text x="30" y="320" class="sub">红色表示进程阻塞,浅青色表示进程可以做别的事。前四种的区别只在第一阶段。</text>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

+59
View File
@@ -0,0 +1,59 @@
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="380" viewBox="0 0 760 380">
<style>
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
.b { font-weight: bold; }
.c { text-anchor: middle; }
.e { text-anchor: end; }
.sub { font-size: 11px; fill: #6E6E6E; }
.note { fill: #C0605A; }
.ok { fill: #6A9F68; }
.mono { font-family: Menlo, Consolas, monospace; }
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
.red { stroke: #C0605A; marker-end: url(#r); }
.green { stroke: #6A9F68; marker-end: url(#g); }
.dash { stroke-dasharray: 5 4; }
.line { stroke: #555; stroke-width: 1.1; fill: none; }
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
.shadow { fill: #DADADA; }
</style>
<defs>
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
</marker>
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
</marker>
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
</marker>
</defs>
<rect width="100%" height="100%" fill="#FFFFFF"/>
<text x="380" y="28" class="title">I/O 复用</text>
<rect x="120" y="46" width="160" height="30" rx="6" fill="#C6ECE9" stroke="#7DBCB7"/>
<text x="200" y="65.5" class="c sub b">应用进程</text>
<rect x="460" y="46" width="160" height="30" rx="6" fill="#C6E4FB" stroke="#7FB2D6"/>
<text x="540" y="65.5" class="c sub b">内核</text>
<line x1="200" y1="78" x2="200" y2="330" stroke="#BBB" stroke-dasharray="4 4"/>
<line x1="540" y1="78" x2="540" y2="330" stroke="#BBB" stroke-dasharray="4 4"/>
<rect x="554" y="96" width="26" height="94" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
<text x="590" y="139" class="sub b" style="fill:#444">① 等待数据</text>
<text x="590" y="155" class="sub">例如等对方发来 hello</text>
<rect x="554" y="244" width="26" height="46" rx="3" fill="#E6E3FA" stroke="#B8B0DE" stroke-width="1"/>
<text x="590" y="270" class="sub b" style="fill:#444">② 复制到用户缓冲区</text>
<text x="554" y="214" class="sub">数据到达</text>
<line x1="200" y1="100" x2="540" y2="100" class="arrow "/>
<text x="370" y="95" class="c sub mono" style="fill:#444">select([fd1, fd2, …])</text>
<rect x="193" y="102" width="14" height="88" rx="3" fill="#F3C6C2" stroke="#D99A94"/>
<text x="186" y="150" class="sub note e">阻塞在 select</text>
<line x1="540" y1="190" x2="200" y2="190" class="arrow dash"/>
<text x="370" y="185" class="c sub" style="fill:#444">fd1 可读</text>
<line x1="200" y1="244" x2="540" y2="244" class="arrow "/>
<text x="370" y="239" class="c sub mono" style="fill:#444">recvfrom(fd1)</text>
<rect x="193" y="246" width="14" height="44" rx="3" fill="#F3C6C2" stroke="#D99A94"/>
<text x="186" y="272" class="sub note e">复制时阻塞</text>
<line x1="540" y1="290" x2="200" y2="290" class="arrow dash"/>
<text x="370" y="285" class="c sub" style="fill:#444">返回 5</text>
<text x="30" y="356" class="sub">和阻塞式 I/O 一样要等,但一次可以等很多个连接,任何一个就绪就返回,一个线程就能处理大量连接。</text>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

+63
View File
@@ -0,0 +1,63 @@
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="380" viewBox="0 0 760 380">
<style>
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
.b { font-weight: bold; }
.c { text-anchor: middle; }
.e { text-anchor: end; }
.sub { font-size: 11px; fill: #6E6E6E; }
.note { fill: #C0605A; }
.ok { fill: #6A9F68; }
.mono { font-family: Menlo, Consolas, monospace; }
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
.red { stroke: #C0605A; marker-end: url(#r); }
.green { stroke: #6A9F68; marker-end: url(#g); }
.dash { stroke-dasharray: 5 4; }
.line { stroke: #555; stroke-width: 1.1; fill: none; }
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
.shadow { fill: #DADADA; }
</style>
<defs>
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
</marker>
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
</marker>
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
</marker>
</defs>
<rect width="100%" height="100%" fill="#FFFFFF"/>
<text x="380" y="28" class="title">非阻塞式 I/O</text>
<rect x="120" y="46" width="160" height="30" rx="6" fill="#C6ECE9" stroke="#7DBCB7"/>
<text x="200" y="65.5" class="c sub b">应用进程</text>
<rect x="460" y="46" width="160" height="30" rx="6" fill="#C6E4FB" stroke="#7FB2D6"/>
<text x="540" y="65.5" class="c sub b">内核</text>
<line x1="200" y1="78" x2="200" y2="330" stroke="#BBB" stroke-dasharray="4 4"/>
<line x1="540" y1="78" x2="540" y2="330" stroke="#BBB" stroke-dasharray="4 4"/>
<rect x="554" y="96" width="26" height="94" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
<text x="590" y="139" class="sub b" style="fill:#444">① 等待数据</text>
<text x="590" y="155" class="sub">例如等对方发来 hello</text>
<rect x="554" y="244" width="26" height="46" rx="3" fill="#E6E3FA" stroke="#B8B0DE" stroke-width="1"/>
<text x="590" y="270" class="sub b" style="fill:#444">② 复制到用户缓冲区</text>
<text x="554" y="214" class="sub">数据到达</text>
<line x1="200" y1="96" x2="540" y2="96" class="arrow "/>
<text x="370" y="91" class="c sub mono" style="fill:#444">recvfrom</text>
<line x1="540" y1="110" x2="200" y2="110" class="arrow dash"/>
<text x="370" y="124" class="c sub" style="fill:#444">EAGAIN(没有数据)</text>
<line x1="200" y1="150" x2="540" y2="150" class="arrow "/>
<text x="370" y="145" class="c sub mono" style="fill:#444">recvfrom</text>
<line x1="540" y1="164" x2="200" y2="164" class="arrow dash"/>
<text x="370" y="178" class="c sub" style="fill:#444">EAGAIN ……共 6 次</text>
<line x1="200" y1="206" x2="540" y2="206" class="arrow "/>
<text x="370" y="201" class="c sub mono" style="fill:#444">recvfrom</text>
<rect x="193" y="100" width="14" height="100" rx="3" fill="#C6ECE9" stroke="#7DBCB7"/>
<text x="186" y="154" class="sub ok e">轮询</text>
<rect x="193" y="208" width="14" height="82" rx="3" fill="#F3C6C2" stroke="#D99A94"/>
<text x="186" y="253" class="sub note e">复制时阻塞</text>
<line x1="540" y1="290" x2="200" y2="290" class="arrow dash"/>
<text x="370" y="285" class="c sub" style="fill:#444">返回 5</text>
<text x="30" y="356" class="sub">本机测试:每 50 ms 调用一次 recv,前 6 次返回 EAGAIN(没有数据),第 7 次读到 hello。轮询本身要消耗 CPU。</text>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

+61
View File
@@ -0,0 +1,61 @@
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="380" viewBox="0 0 760 380">
<style>
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
.b { font-weight: bold; }
.c { text-anchor: middle; }
.e { text-anchor: end; }
.sub { font-size: 11px; fill: #6E6E6E; }
.note { fill: #C0605A; }
.ok { fill: #6A9F68; }
.mono { font-family: Menlo, Consolas, monospace; }
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
.red { stroke: #C0605A; marker-end: url(#r); }
.green { stroke: #6A9F68; marker-end: url(#g); }
.dash { stroke-dasharray: 5 4; }
.line { stroke: #555; stroke-width: 1.1; fill: none; }
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
.shadow { fill: #DADADA; }
</style>
<defs>
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
</marker>
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
</marker>
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
</marker>
</defs>
<rect width="100%" height="100%" fill="#FFFFFF"/>
<text x="380" y="28" class="title">信号驱动 I/O</text>
<rect x="120" y="46" width="160" height="30" rx="6" fill="#C6ECE9" stroke="#7DBCB7"/>
<text x="200" y="65.5" class="c sub b">应用进程</text>
<rect x="460" y="46" width="160" height="30" rx="6" fill="#C6E4FB" stroke="#7FB2D6"/>
<text x="540" y="65.5" class="c sub b">内核</text>
<line x1="200" y1="78" x2="200" y2="330" stroke="#BBB" stroke-dasharray="4 4"/>
<line x1="540" y1="78" x2="540" y2="330" stroke="#BBB" stroke-dasharray="4 4"/>
<rect x="554" y="96" width="26" height="94" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
<text x="590" y="139" class="sub b" style="fill:#444">① 等待数据</text>
<text x="590" y="155" class="sub">例如等对方发来 hello</text>
<rect x="554" y="244" width="26" height="46" rx="3" fill="#E6E3FA" stroke="#B8B0DE" stroke-width="1"/>
<text x="590" y="270" class="sub b" style="fill:#444">② 复制到用户缓冲区</text>
<text x="554" y="214" class="sub">数据到达</text>
<line x1="200" y1="100" x2="540" y2="100" class="arrow "/>
<text x="370" y="95" class="c sub mono" style="fill:#444">sigaction(SIGIO, handler)</text>
<line x1="540" y1="114" x2="200" y2="114" class="arrow dash"/>
<text x="370" y="109" class="c sub" style="fill:#444">立即返回</text>
<rect x="193" y="118" width="14" height="72" rx="3" fill="#C6ECE9" stroke="#7DBCB7"/>
<text x="186" y="158" class="sub ok e">继续执行其它代码</text>
<line x1="540" y1="190" x2="200" y2="190" class="arrow dash"/>
<text x="370" y="185" class="c sub mono" style="fill:#444">SIGIO 信号</text>
<line x1="200" y1="244" x2="540" y2="244" class="arrow "/>
<text x="370" y="239" class="c sub mono" style="fill:#444">在处理函数中 recvfrom</text>
<rect x="193" y="246" width="14" height="44" rx="3" fill="#F3C6C2" stroke="#D99A94"/>
<text x="186" y="272" class="sub note e">复制时阻塞</text>
<line x1="540" y1="290" x2="200" y2="290" class="arrow dash"/>
<text x="370" y="285" class="c sub" style="fill:#444">返回 5</text>
<text x="30" y="356" class="sub">等待阶段不阻塞,数据到达时内核发信号通知“可以读了”,读取仍由进程自己完成。</text>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB