当前位置: 首页 > 科技观察

浅谈操作系统的IO模式

时间:2023-03-17 21:21:52 科技观察

IOIO(Input/Output,输入/输出)指的是读(接收)或写(发送)数据的操作。通常,一个完整的IO在用户进程中分为两个阶段:用户进程空间<-->内核空间、内核空间<-->设备空间(磁盘、网络等)。IO分为三种,内存IO,网络IO,磁盘IO。通常我们所说的IO就是指后两者。LINUX中进程不能直接操作I/O设备,必须通过系统调用请求内核协助完成I/O动作;内核将为每个I/O设备维护一个缓冲区。对于一个输入操作,在进程IO系统调用之后,内核会先检查buffer中是否有对应的缓存数据,然后再从设备中读取,因为设备IO一般比较慢,需要等待;内核缓冲区如果有数据,直接复制到进程空间。因此,一次网络输入操作通常包括两个不同的阶段:(1)等待网络数据到达网卡→读入内核缓冲区,数据准备就绪;(2)从内核缓冲区拷贝数据到进程空间。关键概念理解同步:发起一个调用,返回前得到结果。异步:调用发起后,调用直接返回;调用者主动要求被调用者获取结果,或者被调用者传递回调函数。阻塞式:调用是指在调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果后才会返回。非阻塞:调用是指调用不会阻塞当前线程,直到不能立即得到结果。同步只能分为阻塞和非阻塞;阻塞和非阻塞关系到如何对待事物的结果(阻塞:不得到想要的结果我就不走)clearprocessstate就绪状态被调度,它获得CPU资源(分配CPU时间片),因此进程从就绪状态变为运行状态。运行态->就绪态:处于运行态的进程在时间片用完后不得不让出CPU,使进程从运行态变为就绪态。此外,在可抢占式操作系统中,当更高优先级的进程就绪时,调度级别将正在执行的进程转换为就绪状态,允许更高优先级的进程执行。运行状态->阻塞状态:当进程请求使用和分配资源(如外设)或等待事件发生(如I/O操作完成)时,从运行状态转换到阻塞状态。进程以系统调用的形式请求操作系统提供服务,系统调用是操作系统内核进程被运行的用户态程序调用的一种特殊形式。阻塞态->就绪态:当进程等待的事件到来时,如I/O操作结束或中断结束,中断处理程序必须将相应进程的状态从阻塞态转换到就绪状态。从操作系统层面执行应用理解IO模型BlockingIO模型:简介:进程会一直阻塞,直到数据拷贝完成,应用调用一个IO函数,导致应用阻塞等待数据准备好。如果数据没有准备好,继续等待……数据准备好后,从内核拷贝到用户空间,IO函数返回成功指示。我们最初接触的网络编程是从listen()、send()、recv()等接口开始的。使用这些接口可以轻松构建服务器/客户端模型。阻塞I/O模型图:当调用recv()/recvfrom()函数时,内核中发生了等待数据和复制数据的过程。调用recv()函数时,系统首先检查是否有就绪数据。如果数据没有准备好,那么系统就处于等待状态。当数据准备好后,将数据从系统缓冲区复制到用户空间,函数返回。在socket应用中,调用recv()函数时,用户空间中可能还没有数据,此时recv()函数会处于等待状态。阻塞模式给网络编程带来了很大的问题。例如,在调用send()时,线程将被阻塞。在此期间,线程将无法执行任何操作或响应任何网络请求。这给多客户端、多服务逻辑的网络编程带来了挑战。这时候,我们可能会选择多线程的方式来解决这个问题。应对多客户端网络应用,最简单的解决方案是在服务器端使用多线程(或多处理)。多线程(或多处理)的目的是让每个连接都有一个独立的线程(或进程),这样任何一个连接的阻塞都不会影响其他连接。使用多进程还是多线程,没有特定的模式。传统上,进程的开销远大于线程。因此,如果需要同时为更多的客户端提供服务,不建议使用多进程;如果单个服务执行需要消耗更多的CPU资源,例如,你需要进行大规模或长期的数据计算或文件访问,这个过程更安全。非阻塞IO模型介绍:非阻塞IO通过进程重复调用IO函数(多次系统调用,立即返回);数据复制过程中,进程被阻塞;我们将一个SOCKET接口设置为非阻塞来告诉内核,当请求的I/O操作无法完成时,不要让进程休眠,而是返回一个错误。这样我们的I/O操作函数就会不断的检测数据是否就绪,如果没有就继续检测,直到数据就绪。在这个不断测试的过程中,会占用大量的CPU时间。IO多路复用模型:简介:IO多路复用就是我们所说的select、poll、epoll。在某些地方,这种IO方式也被称为事件驱动IO。select/epoll的优点是单个进程可以同时处理多个网络连接的IO。它的基本原理是select、poll、epoll函数会不断轮询它负责的所有socket,当某个socket有数据到达时通知用户进程。当用户进程调用select时,整个进程会被阻塞。同时,内核会“监控”所有负责select的socket。当任何套接字中的数据准备就绪时,select将返回。这时用户进程调用read操作将数据从内核拷贝到用户进程。因此,I/O多路复用的特点是一个进程可以通过一种机制同时等待多个文件描述符,其中任意一个文件描述符(套接字描述符)进入read-ready状态,select()函数可以返回。异步IO模型简介:用户进程发起读操作后,可以马上开始做其他事情。另一方面,从内核的角度来看,当它接收到一个异步读取时,它会先立即返回,因此它不会为用户进程产生任何块。然后,内核会等待数据准备完成,然后将数据拷贝到用户内存中。当这一切完成后,内核会向用户进程发送一个信号,告诉它读操作已经完成。区分IO多路复用中的selectpollepollselectintselect(intn,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,structtimeval*timeout);select函数监听的文件描述符分为三类,分别是writefds和readfds,exceptfds。调用后select函数会一直阻塞,直到一个描述符就绪(数据可读、可写或except),或者超时(timeout指定等待时间,如果立即返回则置为null),函数回报。当select函数返回时,遍历fdset可以找到就绪描述符pollintpoll(structpollfd*fds,unsignedintnfds,inttimeout);与select使用三个位图来表示三个fdset的方式不同,poll是使用一个pollfd指针来实现的。pollfd没有最大数量限制(但如果数量太大,性能会降低)。和select函数一样,poll返回后,需要轮询pollfd获取就绪描述符。epollepoll使用事件通知方式,调用epoll_create创建实例,调用epoll_ctl添加或删除监听的文件描述符,调用epoll_wait阻塞直到有就绪的文件描述符,通过epoll_event返回就绪状态的文件描述符和事件范围。epoll运行过程需要三个接口,分别是:intepoll_create(intsize);//创建一个epoll句柄,size用来告诉内核监听器的数量有多大,生成一个epoll专用的文件描述符,这实际上是一个应用程序内核空间,用于存储你要关注的socketfd上是否发生以及发生了什么事件。intepoll_ctl(intepfd,intop,intfd,structepoll_event*event);epoll文件描述符上的控制事件:注册、修改、删除。参数epfd是epoll_create(),为epoll创建一个特殊的文件描述符。intepoll_wait(intepfd,structepoll_event*events,intmaxevents,inttimeout);等待I/O事件的发生;返回事件的数量。参数说明:epfd:epoll_create()生成的epoll专用文件描述符;epoll_event:用于返回生成处理事件的数组;maxevents:每次可以处理的事件数;timeout:等待I/O事件发生的超时值;区别总结(1)select,poll实现需要不断轮询所有fd集合,直到设备就绪,期间可能要交替睡眠和唤醒多次。其实epoll还需要调用epoll_wait来不断轮询就绪链表。期间sleep和wakeup可能会交替多次,但是当设备就绪时,调用回调函数,将就绪的fd放入就绪链表,在epoll_wait进程中唤醒到sleep。虽然sleep和alternate,select和poll在“唤醒”的时候都要遍历整个fd集合,而epoll在“唤醒”的时候只需要判断就绪链表是否为空,这样就节省了大量的CPU时间。这就是回调机制带来的性能提升。(2)select,poll每次调用都要将fd集合从用户态复制到内核态,而epoll通过mmap将内核空间和用户空间映射到同一内存,省去了复制操作。应用实例Tornado:使用单线程的方式避免线程切换带来的性能开销,同时避免使用一些函数接口时线程不安全。它支持异步非阻塞网络IO模型,避免阻塞和等待主进程。Tornado的IOLoop模块是异步机制的核心,它包含一系列打开的文件描述符和每个描述符的处理程序。这些handler是对select、poll、epoll等的封装(所以本质上是IO多路复用)Django没有使用异步,通过使用多进程的WSGI服务器(比如uWSGI)来实现并发,这也是WSGI中的常见做法。Linux后端服务器开发需要学习IO网络有哪些知识点IO是网络通信的血管,数据是血液。血液的流动不能离开血管。源码实现服务器IO核心—epoll编程实战客户端多网络连接机制poll文件IO管理select实战框架实战高性能时间循环libev跨平台异步I/Olibuv跨平台C++库Boost.Asio事件通知库libevent理论详解BlockingBIOAsynchronousIOAIONon-blockingIONIO