Skip to content

Commit 3216298

Browse files
committed
update
1 parent 3e39772 commit 3216298

File tree

7 files changed

+162
-138
lines changed

7 files changed

+162
-138
lines changed

book/ch05.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## 5 多路复用技术
1+
## 5. 多路复用技术
22

33
### 5.1 为什么需要 IO 多路复用技术?
44

@@ -61,14 +61,17 @@ IO 多路复用技术会涉及到以下几种 IO 模型的讨论,他们分别
6161

6262
![](imgs/io-multiplex-model.png)
6363

64-
![](imgs/signal-io-model.png)
65-
6664
#### 5.4.4 信号驱动 IO
6765

6866
同 IO 多路复用技术,内核通过信号通知我们的 IO 何时就绪。
6967

70-
![](imgs/asynchronous-io-model.png)
68+
![](imgs/signal-io-model.png)
7169

7270
#### 5.4.5 Asynchronous IO
7371

74-
同信号驱动 IO,内核通过信号通知我们的 IO 何时就绪,区别在于信号驱动 IO 通知发生在数据开始拷贝时,而异步 IO 通知在数据已经完成从内核到用户进程的拷贝。
72+
同信号驱动 IO,内核通过信号通知我们的 IO 何时就绪,区别在于信号驱动 IO 通知发生在数据开始拷贝时,而异步 IO 通知在数据已经完成从内核到用户进程的拷贝。
73+
74+
![](imgs/asynchronous-io-model.png)
75+
76+
在接下来的几个小节会对 linux 提供的几种 IO 多路复用技术一一进行介绍,他们分别是 select、poll、epoll。
77+

book/ch06.md

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
# IO 多路复用之 select
1+
## 6. IO 多路复用之 select
22

3-
> 上一节我们介绍了 IO 多路复用的技术,这一节我们说说其中的 select。
3+
这一节我们说说 O 多路复用的技术 select。
44

5-
## select 是什么
5+
### 6.1 select 是什么
66

77
select 是 linux 内核提供的函数,它允许同时监听多个文件描述符的读写事件,在这些事件中有一个或多个变得可用之后以及经历一段指定的时间之后通知用户进程。
88

9-
## 使用 select
9+
### 6.2 使用 select
1010

1111
Python 中提供了 select 模块来调用系统的 select 函数:
1212
```python
@@ -22,7 +22,7 @@ server_address = ('localhost', 8000)
2222
server.bind(server_address)
2323
server.listen(10)
2424
```
25-
注意在上面的代码中,我们设置了当前的 socket 为非阻塞的 **server.setblocking(0)**,在 web server 中我们始终有一个 socket 是来监听的新的 socket 连接,因而这个监听 socket 要加入读事件队列:
25+
注意在上面的代码中,我们设置了当前的 socket 为非阻塞的 **server.setblocking(0)**,在 Web Server 中我们始终有一个 socket 是来监听的新的 socket 连接,因而这个监听 socket 要加入读事件队列:
2626
```python
2727
inputs = [server]
2828
```
@@ -37,11 +37,16 @@ message_queues = {}
3737
while inputs:
3838
readable, writable, exceptional = select.select(inputs, outputs, inputs)
3939
```
40-
select 函数返回三个文件描述符列表,分别代表有**可读事件列表,有可写事件列表,有异常事件列表**返回这些事件列表包含我们作为 select 参数的那些文件描述符。
40+
select 函数返回三个文件描述符列表,分别代表有**可读事件列表,有可写事件列表,有异常事件列表**返回的这些事件列表中包括了我们作为 select 参数的那些文件描述符。
4141

42-
## 读事件列表
42+
### 6.3 读事件列表
43+
44+
读事件列表根据不同的情形有如下三种处理方式。
45+
46+
**情形一**
47+
48+
如果一个文件描述符可读,而且这个文件描述符恰巧就是我们刚才创建的 server,说明我们侦听的 socket **有新的连接请求**
4349

44-
**情形一**,如果一个文件描述符可读,而且这个文件描述符恰巧就是我们刚才创建的 server,说明我们侦听的 socket 有新的连接请求了:
4550
```python
4651
for s in readable:
4752
if s is server:
@@ -50,9 +55,12 @@ for s in readable:
5055
inputs.append(connection)
5156
message_queues[connection] = Queue.Queue()
5257
```
53-
在上面的代码中,我们通过 accept 函数获得新的连接,并且把该连接加入 inputs 列表,等待连接到该 connection 的客户端发送数据,在此我们也给这个新的 connection 一个写消息的队列。
58+
在上面的代码中,我们通过 accept 函数获得新的连接,并且把该连接加入 inputs 列表,等待连接到该 connection 的 client 发送数据,在此我们也给这个新的 connection 创建一个写消息的队列。
59+
60+
**情形二**
61+
62+
如果一个描述符不是侦听 socket 说明这个描述符**有数据可读**,此时我们可以使用 `recv` 来读取数据:
5463

55-
**情形二**,如果一个描述符不是侦听 socket 说明这个描述符有数据可读,此时我们可以使用 `recv` 来读取数据:
5664
```python
5765
else:
5866
data = s.recv(1024)
@@ -62,7 +70,10 @@ else:
6270
message_queues[s].put(data.capitalize())
6371
```
6472

65-
**情形三**,如果读取的数据是空的,说明 client 关闭了这个连接,我们需要把这个连接从我们的监控列表中移除,并且释放对应的资源:
73+
**情形三**
74+
75+
如果读取的数据是空的,说明 client 关闭了这个连接,我们需要把这个连接从我们的监控列表中移除,并且释放对应的资源:
76+
6677
```python
6778
else:
6879
if s in outputs:
@@ -71,7 +82,7 @@ else:
7182
s.close()
7283
```
7384

74-
## 写事件列表
85+
### 6.4 写事件列表
7586

7687
对于写事件列表来说,在这个例子中需要处理的事情比较有限,如果这个描述符有数据要发送,则通过 `send` 函数发送数据:
7788
```python
@@ -85,7 +96,7 @@ for s in writable:
8596
s.send(next_msg)
8697
```
8798

88-
## 异常列表
99+
### 6.5 异常列表
89100

90101
最后是异常列表,说明描述符有错误发生,关闭它们并且释放资源:
91102
```python
@@ -97,7 +108,7 @@ for s in exceptional:
97108
del message_queues[s]
98109
```
99110

100-
## select 的超时设置
111+
### 6.6 select 的超时设置
101112

102113
select 的超时设置有以下三种情形:
103114

@@ -113,9 +124,9 @@ select 的超时设置有以下三种情形:
113124

114125
不等待,检查完描述符之后立即返回,此时参数是 0
115126

116-
## select 的问题
127+
### 6.7 select 的问题
117128

118-
select 是早期 linux 内核提供的多路复用技术,并且伴随着 linux 设计的限制,自身也有这些不足
129+
select 是早期 linux 内核提供的多路复用技术,并且伴随着 linux 设计的限制,自身也有些不足
119130
1. select 有最大监听描述符限制
120131
2. select 是通过逐个扫描的方式获取有事件的描述符
121132
3. 在多线程环境下,select 的表现不尽如人意

book/ch07.md

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
# IO 多路复用之 poll
1+
## 7. IO 多路复用之 poll
22

3-
> 上一节我们介绍了 IO 多路复用的技术的 select,这一节我们说说 poll。
3+
这小节我们介绍 IO 多路复用技术的 poll。
44

5-
## poll 是什么
5+
### 7.1 poll 是什么
66

77
poll 是类似于 select 的又一个 linux 提供的内核函数,但是 poll 的底层实现和 select 相比更高效,更详细的差异后面的章节会讲到。
88

99

10-
## 使用 poll
10+
### 7.2 使用 poll
1111

1212
Python 中 poll 的实现同样也在 select module 中,但是不同于 select,poll 是通过主动注册文件描述符和事件实现对文件描述符的监听,而且 poll 提供了一些列的事件 flag 来进行对应的事件注册:
1313

@@ -26,9 +26,9 @@ Python 中 poll 的实现同样也在 select module 中,但是不同于 select
2626
<tr><td>POLLHUP</td> <td>Hung up</td></tr>
2727
<tr><td>POLLNVAL</td> <td>Invalid request: descriptor not open</td></tr>
2828
</tbody>
29-
3029
</table>
3130

31+
3232
同上文中 select 的例子一样,我们使用 poll 创建一个回显服务,区别是 poll 需要主动注册文件描述符和需要监听的事件:
3333
```python
3434

@@ -59,11 +59,14 @@ while True:
5959
pass
6060
```
6161

62-
## 读事件 POLLIN 和 POLLPRI
62+
### 7.3 读事件 POLLIN 和 POLLPRI
6363

6464
poll 的读事件对应的是 **POLLIN****POLLPRI** ,同 select 读事件一样也有以下的几种情形。
6565

66-
**情形一**,如果文件描述符是 server,说明有新的连接到来,在获取这个连接之后需要把新的连接通过 poll 来进行注册,这样我们就可以监听新连接的事件了:
66+
**情形一**
67+
68+
如果文件描述符是 server,说明有新的连接到来,在获取这个连接之后需要把新的连接通过 poll 来进行注册,这样我们就可以监听新连接的事件了:
69+
6770
```python
6871

6972
if s is server:
@@ -75,7 +78,10 @@ if s is server:
7578
message_queues[connection] = Queue.Queue()
7679
```
7780

78-
**情形二**,如果文件描述符不是 server,说明这个文件描述符有数据可读,此时我们可以使用 `recv` 来读取数据:
81+
**情形二**
82+
83+
如果文件描述符不是 server,说明这个文件描述符有数据可读,此时我们可以使用 `recv` 来读取数据:
84+
7985
```python
8086
else:
8187
data = s.recv(1024)
@@ -86,7 +92,10 @@ else:
8692
```
8793
与此同时,我们也需要修改这个文件描述符需要被监听的事件类型,在读完数据之后我们需要进行数据回显,因此修改事件类型为 `READ_WRITE`,这点与 select 不同。
8894

89-
**情形三**,如果读取的数据是空的,说明 client 关闭了这个连接,我们需要把这个连接从 poll 的注册列表中移除,并且释放对应的资源:
95+
**情形三**
96+
97+
如果读取的数据是空的,说明 client 关闭了这个连接,我们需要把这个连接从 poll 的注册列表中移除,并且释放对应的资源:
98+
9099
```python
91100
poller.unregister(s)
92101
s.close()
@@ -95,10 +104,10 @@ s.close()
95104
del message_queues[s]
96105
```
97106

98-
## 写事件 POLLOUT
107+
### 7.4 写事件 POLLOUT
99108

100109
对有写事件的文件描述符的处理和 select 类似,区别在于如果没有消息可写,我们修改描述符的监听事件为 `READ_ONLY`
101-
```
110+
```python
102111
elif flag & select.POLLOUT:
103112
try:
104113
next_msg = message_queues[s].get_nowait()
@@ -108,7 +117,7 @@ elif flag & select.POLLOUT:
108117
s.send(next_msg)
109118
```
110119

111-
## 异常处理 POLLHUP 和 POLLERR
120+
### 7.5 异常处理 POLLHUP 和 POLLERR
112121

113122
`POLLHUP` 说明 client 没有正确关闭连接,`POLLERR` 说明有错误发生,这两种情形我们选择取消对文件描述符的监听,并且释放资源:
114123
```python
@@ -120,7 +129,7 @@ s.close()
120129
del message_queues[s]
121130
```
122131

123-
## poll 和 select 的对比
132+
### 7.6 poll 和 select 的对比
124133

125134
select 和 poll 都支持同时监听多个文件描述符,但是他们有着显著的区别。
126135

0 commit comments

Comments
 (0)