IO模型种类
文章目录
- 同步阻塞 I/O(Blocking I/O,BIO)
- 同步非阻塞 I/O(Non-blocking I/O,NIO)
- I/O 多路复用(I/O Multiplexing)
- 信号驱动 I/O(Signal-driven I/O)
- 异步 I/O(Asynchronous I/O,AIO)
在计算机编程和操作系统领域,I/O(输入/输出)模型是处理输入输出操作的不同方式,主要用于解决应用程序如何与外部设备(如磁盘、网络等)进行数据交互的问题。
同步阻塞 I/O(Blocking I/O,BIO)
- 工作原理:在这种模型中,当应用程序发起一个 I/O 操作时,会一直阻塞等待,直到该操作完成并返回结果。在此期间,应用程序不能进行其他操作,CPU 资源被闲置。例如,在网络编程中,当调用
recv
函数接收数据时,程序会一直等待,直到有数据到达或者发生错误。 - 应用场景:适用于连接数比较少且固定的场景,因为它的实现简单,但处理效率较低。比如一些传统的单机应用程序,对并发处理要求不高的场景。
- 示例代码(Python):
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 8888))
server_socket.listen(1)
print('Waiting for a connection...')
conn, addr = server_socket.accept()
print(f'Connected by {addr}')
data = conn.recv(1024) # 阻塞等待数据
print(f'Received: {data.decode()}')
conn.sendall(b'Hello, client!')
conn.close()
同步非阻塞 I/O(Non-blocking I/O,NIO)
- 工作原理:应用程序发起 I/O 操作后,不会阻塞等待结果,而是立即返回。应用程序需要不断地轮询检查 I/O 操作的状态,直到操作完成。这种方式可以让应用程序在等待 I/O 操作的同时进行其他任务,但频繁的轮询会消耗大量的 CPU 资源。
- 应用场景:适用于连接数较多,但每个连接的 I/O 操作比较少的场景。例如,在一些简单的网络爬虫程序中,可以使用非阻塞 I/O 同时处理多个网络请求。
- 示例代码(Python):
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 8888))
server_socket.listen(1)
server_socket.setblocking(False) # 设置为非阻塞模式
while True:
try:
conn, addr = server_socket.accept()
print(f'Connected by {addr}')
conn.setblocking(False) # 设置连接为非阻塞模式
try:
data = conn.recv(1024)
if data:
print(f'Received: {data.decode()}')
conn.sendall(b'Hello, client!')
except BlockingIOError:
pass
except BlockingIOError:
pass
I/O 多路复用(I/O Multiplexing)
- 工作原理:通过一个机制(如
select
、poll
、epoll
等)同时监视多个 I/O 事件,当其中任何一个 I/O 事件就绪时,通知应用程序进行相应的处理。应用程序在等待期间可以进行其他操作,避免了同步阻塞 I/O 中 CPU 资源的闲置。 - 应用场景:适用于连接数较多且连接比较活跃的场景,如网络服务器。通过 I/O 多路复用,可以高效地处理大量的并发连接。
- 示例代码(Python 使用
select
):
import socket
import select
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 8888))
server_socket.listen(5)
inputs = [server_socket]
while True:
readable, _, _ = select.select(inputs, [], [])
for sock in readable:
if sock is server_socket:
conn, addr = server_socket.accept()
print(f'Connected by {addr}')
inputs.append(conn)
else:
data = sock.recv(1024)
if data:
print(f'Received: {data.decode()}')
sock.sendall(b'Hello, client!')
else:
inputs.remove(sock)
sock.close()
信号驱动 I/O(Signal-driven I/O)
- 工作原理:应用程序发起 I/O 操作后,会立即返回,当 I/O 操作完成时,操作系统会发送一个信号通知应用程序。应用程序在等待信号期间可以进行其他操作。
- 应用场景:相对较少使用,主要用于一些对实时性要求较高,但对数据传输量要求不高的场景。
- 示例代码(C 语言):由于信号驱动 I/O 涉及底层系统调用和信号处理,代码较为复杂,以下是一个简单的伪代码示例:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <unistd.h>
// 信号处理函数
void sigio_handler(int signo) {
// 处理 I/O 就绪事件
printf("I/O is ready!\n");
}
int main() {
int sockfd;
struct sockaddr_in server_addr;
// 创建套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
// 设置服务器地址
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(8888);
// 绑定套接字
if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 监听连接
if (listen(sockfd, 5) < 0) {
perror("listen failed");
exit(EXIT_FAILURE);
}
// 设置信号处理函数
signal(SIGIO, sigio_handler);
// 设置套接字为信号驱动 I/O 模式
fcntl(sockfd, F_SETOWN, getpid());
int flags = fcntl(sockfd, F_GETFL);
fcntl(sockfd, F_SETFL, flags | O_ASYNC);
while (1) {
// 可以进行其他操作
sleep(1);
}
return 0;
}
异步 I/O(Asynchronous I/O,AIO)
- 工作原理:应用程序发起 I/O 操作后,立即返回,继续执行其他任务。操作系统会在 I/O 操作完成后,将结果直接返回给应用程序,而不需要应用程序进行额外的查询或处理。这种方式实现了真正的并发,应用程序的效率最高。
- 应用场景:适用于对 I/O 性能要求极高的场景,如大型数据库服务器、高性能网络服务器等。
- 示例代码(Python 使用
asyncio
库模拟异步 I/O):
import asyncio
async def handle_connection(reader, writer):
data = await reader.read(1024)
message = data.decode()
print(f'Received: {message}')
writer.write(b'Hello, client!')
await writer.drain()
writer.close()
async def main():
server = await asyncio.start_server(
handle_connection, 'localhost', 8888)
addr = server.sockets[0].getsockname()
print(f'Serving on {addr}')
async with server:
await server.serve_forever()
asyncio.run(main())
这些 I/O 模型各有优缺点,在不同的应用场景中可以选择合适的模型来提高程序的性能和效率。