IO模型理解
- 阻塞I/O
- 非阻塞I/O
- I/O多路复用
- 信号驱动I/O
- 异步I/O
常用就3种( 阻塞I/O, 非阻塞I/O,I/O多路复用),NIO属于I/O的多路复用
IO类型
- BIO:同步阻塞IO
- NIO:同步非阻塞IO
- AIO:异步非阻塞IO
学习I/O模型需要的基础
- 文件描述符Linux 的内核将所有外部设备都看做一个文件来操作,对一个文件的读写操作会调用内核提供的系统命令(api),返回一个file descriptor(fd,文件描述符)。而对一个socket的读写也会有响应的描述符,称为socket fd(socket文件描述符),描述符就是一个数字,指向内核中的一个结构体(文件路径,数据区等一些属性)。所以说:在Linux下对文件的操作是利用文件描述符(file descriptor)来实现的。
- 用户空间和内核空间为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操心系统将虚拟空间划分为两部分;一部分为内核空间。一部分为用户空间。
- I/O运行过程我们来看看IO在系统中的运行是怎么样的(我们以read为例)
可以发现的是:当应用程序调用read方法时,是需要等待的—>从内核空间中找数据,再将内核空间的数据拷贝到用户空间的。这个等待是必要的过程!
下面只讲解用得最多的3个I/0模型:
- 阻塞I/O模型在进程(用户)空间中调用recvfrom,其系统调用直到数据包到达且被复制到应用进程的缓冲区中或者发生错误时才返回,在此期间一直等待。
- 非阻塞I/O模型recvfrom从应用层到内核的时候,如果没有数据就直接返回一个EWOULDBLOCK错误,一般都对非阻塞I/O模型进行轮询检查这个状态,看内核是不是有数据到来。
- I/O复用模型前面也已经说了:在Linux下对文件的操作是利用文件描述符(file
descriptor)来实现的。在Linux下它是这样子实现I/O复用模型的:调用select/poll/epoll/pselect其中一个函数,传入多个文件描述符,如果有一个文件描述符就绪,则返回,否则阻塞直到超时。比如poll()函数是这样子的:int
poll(struct pollfd fds,nfds_t nfds, int timeout);其中 pollfd
结构定义如下:struct pollfd {
int fd; / 文件描述符 /
short events; / 等待的事件 /
short revents; / 实际发生了的事件 */ };
(1)当用户进程调用了select,那么整个进程会被block;
(2)而同时,kernel会“监视”所有select负责的socket;
(3)当任何一个socket中的数据准备好了,select就会返回;
(4)这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程(空间)。
所以,I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符其中的任意一个进入读就绪状态,select()函数就可以返回。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。
I/O模型总结与理解:
买早点操作是请求:服务端是早点店
- 阻塞I/O:就是你买早点,首先你要排队,到你了,你点单,但是不是点了就马上到,等了1个小时,我买到了卷饼.(在门口干等了1小时)
- 非阻塞I/O:你去买早点,去排队,排队到你了,你去点了单,然后就去玩玩牌,喝喝水,时不时的问问店主,好了没有(玩玩牌,喝喝水,时不时的问问店主)
- I/O多路复用:你想买多个早点,这时有个小程序帮你(selector),你同时点了多个早点(玉米,粥,卷饼),哪个早点好了,小程序就提醒你去拿.(小程序帮你点,并在早点好了就立刻通知给你)
引用:
https://www.zhihu.com/question/29005375