首页 >> 大全

操作系统之网络IO

2023-08-10 大全 31 作者:考证青年

拷贝 传统的文件传输

在传统的文件传输中,由于涉及到两次系统调用read和write,所以会产生4次上下文切换。并且存在4次数据拷贝(2次DMA拷贝和2次CPU拷贝),①第一次拷贝,把磁盘上的数据拷贝到内存的操作系统内核的缓冲区里,这个拷贝的过程是通过 DMA完成的;②第二次拷贝,把内核缓冲区的数据拷贝到用户的缓冲区里,这个拷贝到过程是由 CPU 完成的;③第三次拷贝,把刚才拷贝到用户的缓冲区里的数据,再拷贝到内核的 的缓冲区里,这个过程依然还是由 CPU 搬运的;④第四次拷贝,把内核的 缓冲区里的数据,拷贝到网卡的缓冲区里,这个过程又是由 DMA 搬运的。

mmap+write实现零拷贝

在传统的文件传输中,读文件需要先将文件拷贝到内核缓冲区中,然后再拷贝到用户空间中。

mmap()函数会在进程的虚拟地址空间中,寻找一段空闲的满足要求的连续虚拟地址,然后建立页表项,将该段虚拟地址映射到该文件的文件地址上,并且在第一次访问的时候会触发缺页中断,然后将文件内容从磁盘调入内存中,这样的话就可以像访问普通内存一样来访问该文件。可以发现通过mmap()系统调用,只使用一次数据拷贝,就从磁盘中将数据传入内存的用户空间中,供进程使用。同时,用户空间和内核空间中的一方对映射区域的修改,都可以立即被另一方捕捉到。

通过使用 mmap() 来代替 read(), 可以减少一次数据拷贝的过程。但是还是存在2次系统调用,4次上下文切换和3次拷贝(2次DMA拷贝和1次CPU拷贝)。

实现零拷贝

通过()函数可以实现read和write两个函数的功能,将数据从源文件描述符端口拷贝到目的文件描述符端口,减少了一次系统调用。并且直接将数据从内核缓冲区拷贝到网卡,不再经过缓冲区了,所以只有2次拷贝,①通过 DMA 将磁盘上的数据拷贝到内核缓冲区里;②将内核缓存中的数据拷贝到网卡的缓冲区里。所以只需要2次上下文切换和2次数据拷贝即可,而且2次的数据拷贝过程都是由 DMA 来完成的,不需要通过CPU。

IO模型

对于一次IO访问(以read为例),数据(位于磁盘或网卡等地方)会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的空间。所以会经历两个阶段:①等待内核将数据准备好;②将数据从内核拷贝到进程的用户空间中。

①阻塞IO模型:默认情况下所有的文件操作都是阻塞的,在用户空间调用(以套接字接口为例)读取数据时,其系统调用直到数据包到达且被复制到应用缓冲区中或者发送错误时才返回,在此期间一直会等待,进程从调用到返回这段时间内都是被阻塞;

②非阻塞IO:应用程序调用读取数据时,如果该内核缓冲区没有数据的话,就会直接返回一个错误,不会让应用一直等待中;如果数据已经准备好了,则会将数据从内核缓冲区拷贝到用户空间中。我们在应用程序中可以循环调用函数并判断是否出现该错误标识,直到读取到它数据要的数据为止。

③IO多路复用:进程通过将一个或多个fd传递给/poll系统调用,阻塞在操作上,/poll帮我们侦测多个fd是否准备就绪,当有fd准备就绪时,返回数据可读状态,应用程序再调用读取数据。/poll顺序扫描fd是否就绪,每次执行的时间复杂度和fd的数量成正比,且支持的fd数量受到限制,因此Linux提供了epoll系统调用,它使用事件驱动的方式代替了顺序扫描,当有fd就绪时,立即执行其回调函数,性能更高。IO多路复用的好处就是通过或poll、epoll 来监控多个fd ,从而不必为每个fd创建一个对应的监控线程,从而创建多个线程的开销并减少了资源占用。

④信号驱动IO模型:首先开启套接口信号驱动IO功能,并通过系统调用为SIGIO信号绑定一个信号处理函数,此时请求即刻返回,当内核数据准备就绪时,就给对应进程发送SIGIO信号,进程接收到该信号后,会通过信号处理程序来通知应用程序调用来读取数据。

⑤异步IO:应用程序告知内核启动某个操作,并让内核在整个操作完成之后通知应用程序,比如说内核已经将数据准备好并已经将其拷贝到用户空间中了。这种模型与信号驱动模型的主要区别在于,信号驱动IO只是由内核通知我们何时可以开始IO操作,而异步IO模型是由内核通知我们操作何时已经完成。

IO多路复用 /poll

实现多路复用的方式是,将多个已连接的 都放到一个文件描述符集合,然后调用 函数将文件描述符集合拷贝到内核里,内核通过顺序遍历文件描述符集合的方式来检查是否有网络事件产生,当检查到有事件产生后,将此 标记为可读或可写, 接着再把整个文件描述符集合拷贝回用户态里,然后用户态还需要再通过遍历的方法找到可读或可写的 ,然后再对其处理。

缺点:

单个进程能够监视的文件描述符的数量存在最大限制,通常是1024,当然可以更改数量,但由于采用轮询的方式扫描文件描述符,文件描述符数量越多,性能越差;需要通过遍历的方式来找到可读或可写的文件描述符,时间复杂度为O(n);每次调用都需要会产生两次文件描述符集合的拷贝;

poll以链表形式来组织文件描述符集合,不再有单个进程文件描述符个数限制,当然还会受到系统文件描述符限制。除此之外它的实现方式和并没有区别,所以还是会存在上述的缺点2和3。

epoll

epoll在内核里使用红黑树来管理所有待检测的文件描述符,用户程序只需要通过 () 函数将需要监控的 加入内核中的红黑树里,这样就不需要像 /poll 每次操作时都要拷贝整个文件描述符集合到内核,而只需要传入一个待检测的 ,减少了内核和用户空间大量的数据拷贝。

epoll 使用事件驱动的机制,内核里维护了一个双向链表来记录就绪事件,当某个 有事件发生时,通过回调函数内核会将其加入到这个就绪事件链表中,当用户调用 () 函数时,只会返回有事件发生的文件描述符的个数,不需要像 /poll 那样轮询扫描整个文件描述符集合,因此与执行效率与文件描述符的个数无关,也对文件描述符的个数没有限制。

触发方式

**水平触发(LT):**当检测到某描述符事件就绪并通知应用程序时,应用程序可以不立即处理该事件;下次调用时,会再次通知此事件

边缘触发(ET): 当检测到某描述符事件就绪并通知应用程序时,应用程序必须立即处理该事件。如果不处理,下次调用时,不会再次通知此事件。

/poll只支持水平触发,epoll支持水平触发和边缘触发,但是默认为水平触发。

模型 单单线程模型

使用一个线程来完成客户端的所有请求,通过IO多路复用接口监听事件,收到事件后通过进行分发,根据事件类型将其分发给 来建立连接或者当前连接对应的来完成业务请求。

缺点:

所以该模型不适用于计算密集型场景,只适用于业务处理非常快的场景(如Redis)。

单多线程模型

该模型专门有一个线程(线程)用于处理客户端的TCP连接请求,然后其它的业务请求交由一个线程池来负责。

在一些场景下,一个线程负责监听和处理所有的客户端连接可能会存在性能问题。

主从多线程模型

用于接收客户端连接的不再是一个单独的线程,而是一个独立的线程池。接收到客户端的TCP连接请求并处理完成后,将新建的注册到IO线程池的某个线程上,此后由它来负责该连接后续的读写和编解码工作。

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了