在使用socket编程中,经常会看到阻塞、非阻塞、同步、异步,那么它们之间到底有什么关系跟区别呢?
本次将那Nginx的异步非阻塞的事件驱动模型来解释一下它们之间的关系。
阻塞IO
在linux中,默认所有socket都是阻塞的。
这意味着使用该socket调用诸如recv的函数时,在没有数据到达之前,该函数将不会返回,导致线程被阻塞,直到数据到达。
非阻塞IO
我们可以使用fcntl把socket设置为非阻塞的。
这意味着使用该socket调用诸如recv的函数时,该函数将立刻返回,可以根据返回值来确定是否有数据到达。
相信阻塞/非阻塞IO的大家都非常的清楚,本次主要说的是同步IO跟异步IO之间的区别。
首先要说的是,同步/异步只是跟具体实现有关,就是说非阻塞的必定是异步的的说法是错误的,它们之间并没有确定的关系。
同步IO
一般来说,我们调用的函数都是同步的。
何为同步呢?就是说我们调用了一个函数,在该函数返回之后就说明该函数要做的东西已经完成了。
这是我们一般的做法,例如说我们调用recv接收数据,在该函数返回之后,就代表我们已经接收好数据了。
异步IO
异步IO大概可以这样理解:
我们调用一个函数,在该函数返回之后其实该函数要完成的事情还没真正完成,而是被托管到其它地方了(如Nginx的事件驱动)。
可见该函数的功能并不是立刻完成的(但是最终会由托管的模块去完成)。
可能读者会有这样的疑问:异步IO到底是怎么样实现的呢?它有什么应用呢?下面将拿Nginx的事件驱动模型来解决这些问题。
异步非阻塞IO的应用
拿一个比较容易理解的例子:
在处理HTTP请求的时候,如果要忽略请求包体的内容(我们必须接收包体,但是不做处理),我们会ngx_discard_reqeust_body函数。
忽略请求包体的过程可能比较长,但是我们不需要理会包体的内容(该函数返回的内容我们并不在意),因此该函数是异步非阻塞的:
这样,在调用该函数之后我们可以继续执行下面的代码,而接收并忽略包体的动作则托管给事件驱动去完成。
异步非阻塞IO在Nginx的实现
Nginx的高并发来源于其异步非阻塞的事件驱动,下面将对Nginx基于epoll的事件驱动模型来说明。
关于epoll的使用方法跟Nginx事件驱动模型可以查看文章的前面两部分,这里将不做详细说明。
可见,Nginx主循环会调用ngx_epoll_process_events方法,而该方法则调用epoll_wait来获取事件并且处理事件。
我们只要调用epoll_ctl向epoll中添加或者删除事件(托管事件),Nginx事件驱动就会帮我们在事件准备好的时候处理相应的事件了。
网友评论