大厂面试八股文——网络编程
文章目录
什么是同步I/O,什么是异步I/O
- 同步(阻塞)I/O:在一个线程中,CPU执行代码的速度极快,然而,一旦遇到IO操作,如读写文件、发送网络数据时,就需要等待IO操作完成,才能继续进行下一步操作。这种情况称为同步IO。
- 异步(非阻塞)I/O:当代码需要执行一个耗时的IO操作时,它只发出IO指令,并不等待IO结果,然后就去执行其他代码了。一段时间后,当IO返回结果时,再通知CPU进行处理。
事件处理模式
reactor模式中,主线程(I/O处理单元)只负责监听文件描述符上是否有事件发生,有的话立即通知工作线程(逻辑单元 ),读写数据、接受新连接及处理客户请求均在工作线程中完成。通常由同步I/O实现。
proactor模式中,主线程和内核负责处理读写数据、接受新连接等I/O操作,工作线程仅负责业务逻辑,如处理客户请求。通常由异步I/O实现。
模拟proactor模式:主线程执行数据读写操作,读写完后,主线程向工作线程通知这以完成事件,接下里工作线程对读写的结果进行处理
主线程往epoll内核事件表注册socket上的读就绪事件。
主线程调用epoll_wait等待socket上有数据可读
当socket上有数据可读,epoll_wait通知主线程,主线程从socket循环读取数据,直到没有更多数据可读,然后将读取到的数据封装成一个请求对象并插入请求队列。
睡眠在请求队列上某个工作线程被唤醒,它获得请求对象并处理客户请求,然后往epoll内核事件表中注册该socket上的写就绪事件
主线程调用epoll_wait等待socket可写。
当socket上有数据可写,epoll_wait通知主线程。主线程往socket上写入服务器处理客户请求的结果。
工作模式
epoll 的描述符事件有两种触发模式:LT(level trigger)和 ET(edge trigger)。
1. LT 模式
当 epoll_wait() 检测到描述符事件到达时,将此事件通知进程,进程可以不立即处理该事件,下次调用 epoll_wait() 会再次通知进程。是默认的一种模式,并且同时支持 Blocking 和 No-Blocking。
2. ET 模式
和 LT 模式不同的是,通知之后进程必须立即处理事件,下次再调用 epoll_wait() 时不会再得到事件到达的通知。
很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。只支持 No-Blocking,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
5中I/O模型
- 阻塞IO:调用者调用了某个函数,等待这个函数返回,期间什么也不做,不停的去检查这个函数有没有返回,必须等这个函数返回才能进行下一步动作
- 非阻塞IO:非阻塞等待,每隔一段时间就去检测IO事件是否就绪。没有就绪就可以做其他事。非阻塞I/O执行系统调用总是立即返回,不管时间是否已经发生,若时间没有发生,则返回-1,此时可以根据errno区分这两种情况,对于accept,recv和send,事件未发生时,errno通常被设置成eagain
- 信号驱动IO:linux用套接口进行信号驱动IO,安装一个信号处理函数,进程继续运行并不阻塞,当IO时间就绪,进程收到SIGIO信号。然后处理IO事件。
- IO复用:linux用select/poll函数实现IO复用模型,这两个函数也会使进程阻塞,但是和阻塞IO所不同的是这两个函数可以同时阻塞多个IO操作。而且可以同时对多个读操作、写操作的IO函数进行检测。知道有数据可读或可写时,才真正调用IO操作函数
- 异步IO:内核执行读写操作并触发读写完成事件,程序没有阻塞阶段。
注意:阻塞I/O,非阻塞I/O,信号驱动I/O和I/O复用都是同步I/O。同步I/O指内核向应用程序通知的是就绪事件,比如只通知有客户端连接,要求用户代码自行执行I/O操作,异步I/O是指内核向应用程序通知的是完成事件,比如读取客户端的数据后才通知应用程序,由内核完成I/O操作。
半同步/半反应堆并发模式是半同步/半异步的变体,将半异步具体化为某种事件处理模式
select、poll、epoll
I/O复用虽然可以同时监听多个文件描述符,但是它本身是阻塞的,并且当有多个文件描述符同时就绪的时候,如果不采取额外措施,程序则只能按顺序处理其中就绪的每一个文件描述符,所以为提高效率,可以通过线程池来实现并发(多线程并发),为每个就绪的文件描述符分配一个逻辑单元(线程)来处理。
你用了epoll,说一下为什么用epoll,还有其他复用方式吗?区别是什么?
select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间
select
select返回(可读、可写和异常)文件描述符的总数,如果超时没有任何文件描述符就绪,返回0,失败返回-1并设置errno
-
调用 select ,对所有文件描述符循环,检测就绪事件
-
每次调用 select 函数时都需要向该函数传递监视对象信息,
-
每次调用select,都需将全部描述符从应用进程缓冲区复制到内核缓冲区。
- select支持的文件描述符数量太小了,默认是1024
poll
poll的实现和select非常相似,只是描述文件描述符集合的方式不同,poll使用pollfd结构数组指定文件描述符上发生的可读、可写和异常等事件,其他的都差不多。几乎所有的系统都支持 select,但是只有比较新的系统支持 poll
epoll
- epoll把文件用户关心的文件描述符上的事件放在内核里的一个事件表中,无须像select和poll每次调用都要重复传入文件描述符集或事件集
- epoll 只需要将描述符从进程缓冲区向内核缓冲区拷贝一次,并且进程不需要通过轮询来获得事件完成的描述符。
- epoll 仅适用于 Linux OS, 比 select 和 poll 更加灵活而且没有描述符数量限制。
Epoll对文件操作符的操作有两种模式:LT(电平触发)和ET(边缘触发),二者的区别在于当你调用epoll_wait的时候内核里面发生了什么:
- LT(电平触发):类似select,LT会去遍历在epoll事件表中每个文件描述符,来观察是否有我们感兴趣的事件发生,如果有(触发了该文件描述符上的回调函数),epoll_wait就会以非阻塞的方式返回。若该epoll事件没有被处理完(返回EWOULDBLOCK),该事件还会被后续的epoll_wait再次触发。
- ET(边缘触发):ET在发现有我们感兴趣的事件发生后,立即返回,并且sleep这一事件的epoll_wait,不管该事件有没有结束。
在使用ET模式时,必须要保证该文件描述符是非阻塞的(确保在没有数据可读时,该文件描述符不会一直阻塞);并且每次调用read和write的时候都必须等到它们返回EWOULDBLOCK(确保所有数据都已读完或写完)
两种高效的并发模式
并发编程的目的:为了让程序执行多个任务
计算密集型:并发编程没有优势,反而由于任务的切换导致效率低下
I/O密集型:I/O操作的速度远没有CPU快,让程序阻塞于I/O操作将浪费大量的CPU时间。如果程序有多个线程,则当前被I/O操作所阻塞的执行线程可主动放弃CPU,并将执行权转移到其他线程
半同步/半异步模式
I/O模型中:同步和异步区分:内核向应用进程通知的是何种事件(是就绪事件还是完成事件),以及由谁来完成I/O操作(是应用进程还是内核)
并发模式中中
- 同步:程序完全按照代码序列的顺序执行
- 异步:程序的执行需要系统事件来驱动,常见系统事件包括:中断、信号等
半同步/半异步模式中,同步线程用于处理客户逻辑,异步线程用于处理I/O事件
领导者/追随者模式
线程数多少合适
使用多线程的主要目的提高性能:降低延迟 (发送请求到接收到数据的时间)和提高吞吐量 (单位时间能可以处理更多的请求)
降低延迟和提高吞吐量对应的方法有两种: 优化算法 和 将机器的硬件性能发挥到极致
- 优化算法:降低时间和空间复杂度,使得程序执行时间更短。
- 将硬件的性能发挥到极致,具体的指提高I/O和cpu的利用率
如何提高I/O 和cpu的利用率的举例:
- 如果单核系统中,只有一个程序执行又有IO操作 和Cpu计算的代码,当程序执行IO操作的时候,Cpu其实是空闲的,反之IO是空闲的,如果这个时候用两个线程去跑这段代码,一个线程执行IO操作 ,一个线程执行Cpu计算 ,这时IO和Cpu的利用率是不是发挥了极致?

空间换时间,浪费服务器的硬件资源,换取运行效率.
池是一组资源的集合,这组资源在服务器启动之初就被完全创建好并初始化,这称为静态资源.
当服务器进入正式运行阶段,开始处理客户请求的时候,如果它需要相关的资源,可以直接从池中获取,无需动态分配.
当服务器处理完一个客户连接后,可以把相关的资源放回池中,无需执行系统调用释放资源.
gevent
Gevent是一个基于greenlet的Python的并发框架,以微线程greenlet为核心,使用了epoll事件监听机制以及诸多其他优化而变得高效。
于greenlet、eventlet相比,性能略低,但是它封装的API非常完善,最赞的是提供了一个monkey类,可以将现有基于Python线程直接转化为greenlet,相当于proxy了一下(打了patch)。
功能包括:
- 基于libev或libuv的快速事件循环。
- 基于greenlet的轻量级执行单元。
- 重复使用Python标准库中的概念的API(例如,有event和 queues)。
- 具有SSL支持的协作套接字
- 通过线程池,dnspython或c-ares执行的合作DNS查询。
- 猴子修补实用程序,使第三方模块能够合作
- TCP / UDP / HTTP服务器
- 子流程支持(通过gevent.subprocess)
- 线程池
更多推荐
所有评论(0)