阅读 81

操作系统IO进化史(操作系统io控制方式)

简介

我们都知道,BIO和NIO的特点,像Java程序提供的方法,底层都是需要操作系统来进行支持,我们想要更加了解其特点的话,就需要和原理进行结合,下面是我自己对操作系统提供的IO变迁的理解;在IO变迁之前,我先让大家看一下老钱(Redis深度历险核心原理与应用实践的作者)的一篇博客,上面的那个动图真的是绝了 博客地址;

epoll进化史

  1. 首先,对应一个文件描述符(socket 有一个文件描述符),刚刚开始的时候,只有一个read指令,socket在这个时期是阻塞的,一个socket需要一个线程去读取;BIO模型

  2. 第二个时期,系统内核支持了NIO,就是可以去调用read; 如果,没有相关的数据返回,也不会阻塞住。所以,我们可以通过一个线程去轮询所有的文件描述符,看看该文件描述符是否有数据可以读;但是,这还是有问题,就是假设有1000个文件描述符,需要调用1000次read 由于,查询一个文件描述符,就需要调用一次内核函数,但是,会涉及用户态和内核态的转换,涉及CPU的上下文切换,消耗还是很大;

  3. 这个时候,内核又向前发展了,内核增加了一个系统调用select,假设有1000个文件描述符,你把这个1000个文件描述符传给他,它会进行监控,当发现状态为ready,再进行返回。现在还是同步非阻塞的。举个例子,假设,我有1000个文件描述符,然后,把这1000个文件描述符传给内核函数select(调用一次),假设,这里面有50个文件描述符可读,所以,只需要调用一次select就可以知道50个可读了,然后,再调用read去读就可以了。现在,解决了1000次调用read 变成了 1次调用select + 50次调用read;  这也是多路复用, 但是还有有一个问题: 内核态由于不信任用户态,所以,需要进行传参拷贝:内核不能信任任何空间的指针,必须对用户空间的指针指向的数据进行验证,如果,只做验证不做拷贝的话,那么,在随后的运行中要随时受到其他进/线程对该空间数据修改的威胁。所以,必须做拷贝。 所以,这1000个文件描述符要先复制到内核态中;效率比较低;

  4. 为了解决这个问题,提出了一个共享空间的思路,在这个空间中,即可以内核态去访问,也可以用户态去访问,所以出现了epoll概念,有一个文件描述符产生,先放到共享空间中,这个新的文件描述符,就会给内部的红黑树进行注册,所以,增删改是内核来进行完成的,查询的话是用户态和内核态都可以(用来保证安全,因为用户态无法对其进行更改) 当发现read事件时,会给他放到链表中,这时候,就可以通过read(文件描述符)去读取数据。所以,epoll也不是AIO ,因为,当它知道read事件时,也是需要调用read(文件描述符)去进行读取的。 AIO 异步:就是调用过一次read() 就可以不管了,当消息来了,就直接回调一个回调函数即可,不需要再进行调用read();

备注:poll: poll的实现和select非常相似,只是文件描述符的集合不同,它底层使用的是链表,所以,和select相比,它没有1024这个缺点;但是,也有如下缺点:

1 每次调用poll,都需要把文件描述符集合从用户态拷贝到内核态,这个开销很大

2 每次调用poll,都需要在内核遍历传递进来的文件描述符,这个开销也很大

epoll 多路复用器
注意: IO操作是分为两步:
第一步是从内核中知道那些文件描述符可以读
第二步是把内核中文件读取到程序中
epoll 多用复用器,不负责数据的读写,只管理读写的事件;


作者:功夫熊猫阿宝
链接:https://juejin.cn/post/6956460693415526430


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐