共计 1184 个字符,预计需要花费 3 分钟才能阅读完成。
I/ O 模式
阻塞 I/O
个别设施有两种操作:读和写。其中随同读缓冲区和写缓冲区。
- 读缓冲区无数据:
当读数据的速率超过写数据的速率,可能造成缓冲区无数据可读的状况,此时用户程序如果是阻塞 I/O 模式,那么用户程序将会进入期待,在 linux 中的具体操作就是从零碎的 “Runable Queue”中删除该过程,将其退出到期待队列“Wait Queue”。只有当设施通过“中断”告知了内核缓冲区已有数据或者内核轮询发现了缓冲区有数据,内核会唤醒该期待队列上的过程,把他们退出可“Runable Queue”。 - 写缓冲区满数据:
相似,当写数据的速率超过读数据的速率,可能造成缓冲区无闲暇内存可写的状况,这时内核同样的会阻塞写线程直到缓冲区可写。
非阻塞 I/O
相似下面的状况,当无数据可读可写时,执行 read/write(个别 read/write 办法会返回此次调用读取或写了多少字节)后,内核可间接返回 0,并不阻塞以后线程,把无数据可读可写的结果交由用户程序本人解决。
那什么时候可读和可写?这种 I/O 框架个别存在一种叫 Selector 选择器的线程,由这个线程去轮询所有的 已注册 的文件描述符(这包含一般文件,套接字等)缓冲区,当呈现缓冲区可读或可写时,找出在其下面注册了相应的可读事件或可写事件的回调办法,进行调用。但这种形式在解决大量描述符时成果不是很好,因为为缓冲区的可读可写的判断,须要进入内核态。
用户态 切换到内核态的代价挺大的,尽管他们都是同一个线程,但却须要保留硬件上下文,这是因为用户态的数据段、代码段、栈基址、栈指针 和内核态都不一样,每次切换须要切换这些寄存器值。
Epoll
在服务器过程中,一个 server 过程可能须要治理成千盈百个 Socket。
他们的可读可写如果是阻塞模式,那将造成灾难性的结果,一个网络差的客户端连贯,足以迁延整个零碎。
如果可读可写是异步模式,大量的 Socket 治理将连累 Selector,让 Selector 线程跑满 CPU
Linux 应用了 Epoll 模式,改善了下面的状况。
- 通过应用一片用户态和内核态共享的内存空间,因为此片空间为共享的,所以用户程序向此内存写数据时不须要陷入内核态。
- 用户程序须要写入什么数据?epoll 在此空间保护了一颗红黑树用来保留 Socket,一个就绪列表来援用就绪的 Socket。用户程序能够将本人的 Socket 增加至红黑树,并向 epoll 注册相干事件(可读、可写、连贯 …)的监督函数。
- 当网卡承受到了数据或发送了数据后,向操作系统收回了一个硬中断告诉操作系统,操作系统得悉后,在 Epoll 红黑树中找到对应的 socket,退出到就绪队列,。
- Epoll 关注了就绪列表,通过就绪列表中的 Socket 向关怀相干事件的过程发送告诉,至此用户程序能够读取或写入数据了。
全程没有阻塞,所有操作都是异步的(异步扭转世界),也没有用户态和内核态的非必要切换。
正文完