引言
在之后的多篇文章里面我会介绍陈硕老师的网络库,并且用c++20的方式重写一遍网络库的主干代码。所以在这一篇文章里面主要总结一些必备的知识,也就是linux系统编程的一些关键。
阻塞和非阻塞
在数据准备的时候,也就是监听状态的时候,我们有两种状态:一种是阻塞,一种是非阻塞。
阻塞指的是接受数据的时候并不会一直轮询,当有数据来的时候会自动解除阻塞,recv就是这种类型,而非阻塞指的是通过while()循环,可以得到CPU的时间片的,并且会一直占用CPU的时间片。
我们举个例子 int size = recv(sockfd, buf, 1024, 0) ,当返回值是size > 0时,我们表示的是收到的字节数量。然后sockfd当我们设置成阻塞的情况下,size == 0时,表示断开了连接,size == -1时就是出现了错误,而当sockfd设置成非阻塞的情况下,size == -1 并且全局变量errno == EAGAIN的时候就是非阻塞的出错。
同步和非同步
当我们数据准备完成之后,我们就到了数据的读写。数据的读写分为同步和异步。我们先说同步:
数据来到了TCP的缓冲区,数据会被一个一个的读到buf1024里面,当数据读完之后才会返回给size,于是我们就可以接着往下执行代码。所以只要数据没有被读完,我们的代码就不会往下面走。
所以我们阻塞和非阻塞和同步和异步并没有一一对应的关系,比如我们的epoll就是同步IO,也是阻塞调用,epoll_wait()在调用的时候,最后一个参数时timeout,如果不自定义时间的话,相当于工作在阻塞的状态,当数据来到的时候,我们就会调用recv,这个操作肯定是我们自己在读,而不是操作系统在帮忙,所以是同步。
所以当recv没有读完的时候,我们就无法进行下面的代码
然后就是异步:
而异步的IO就是操作系统帮我们读到buf里面,而我们的应用程序可以执行其他的代码,当读完的时候我们就会发送给用户一个sigio的信号,这个时候就可以拿buf执行之后的代码了。
所以我们可以用一句话总结一下同步和异步的区别:在处理IO的时候,阻塞和非阻塞都是同步IO,而只有调用特殊API的时候才是异步。因为异步的读取数据是内核做的,所以必须要内核提供的API。
五种IO模型
一个典型的IO调用就是分为两个阶段,数据准备和数据读写,数据准备阶段就是阻塞和非阻塞,表现得状态就是阻塞等待或者直接返回(没有数据就直接返回,所以我们一般while()循环等待)。
而同步就是A向B发送一个网络IO接口时,数据的读还是写都是由A来完成(无论是阻塞还是非阻塞,而异步表示的是A向B发送网络IO接口,并且向B传入了事件和事件发生时的通知A的方式,A就可以处理其他的逻辑了。当B处理完事件之后,会按照之前原来约定的方式通知A。
阻塞

非阻塞

IO复用
我们的select/poll/epoll 系统调用,而epoll就可以实现一个线程监听多个fd,

信号驱动
其实和IO复用的作用差不多,就是先发送一个信号表示正在等待数据,当数据准备好了之后,就会发送给应用层表示数据来了,但是读取的工作还是应用层来做,也就是recv,所以这个也是同步,而不是异步。

异步

总结
本篇文章到这里就结束了,在之后的文章里面会介绍muduo网络库,希望可以帮助大家理解~~~