目录
阻塞IO
非阻塞 IO
【IO多路复用的理解/演变过程】select
epoll
总结一下 。
阻塞IO服务端为了处理客户端的连接和请求的数据,写了如下代码 。
这段代码会执行得磕磕绊绊,就像这样 。
文章插图
可以看到,服务端的线程阻塞在了两个地方,一个是 accept 函数,一个是 read 函数 。
如果再把 read 函数的细节展开,我们会发现其阻塞在了两个阶段 。
文章插图
这就是传统的阻塞 IO 。
整体流程如下图 。
文章插图
所以,如果这个连接的客户端一直不发数据,那么服务端线程将会一直阻塞在 read 函数上不返回,也无法接受其他客户端连接 。
这肯定是不行的 。
非阻塞 IO为了解决上面的问题,其关键在于改造这个 read 函数 。
有一种聪明的办法是,每次都创建一个新的进程或线程,去调用 read 函数,并做业务处理 。
- while(1) {
- connfd = accept(listenfd);// 阻塞建立连接
- pthread_create(doWork);// 创建一个新的线程
- }
- void doWork() {
- int n = read(connfd, buf);// 阻塞读数据
- doSomeThing(buf);// 利用读到的数据做些什么
- close(connfd);// 关闭连接,循环等待下一个连接
- }
这样,当给一个客户端建立好连接后,就可以立刻等待新的客户端连接,而不用阻塞在原客户端的 read 请求上 。文章插图
不过,这不叫非阻塞 IO,只不过用了多线程的手段使得主线程没有卡在 read 函数上不往下走罢了 。操作系统为我们提供的 read 函数仍然是阻塞的 。
所以真正的非阻塞 IO,不能是通过我们用户层的小把戏,而是要恳请操作系统为我们提供一个非阻塞的 read 函数 。
这个 read 函数的效果是,如果没有数据到达时(到达网卡并拷贝到了内核缓冲区),立刻返回一个错误值(-1),而不是阻塞地等待 。
操作系统提供了这样的功能,只需要在调用 read 前,将文件描述符设置为非阻塞即可 。
- fcntl(connfd, F_SETFL, O_NONBLOCK);
- int n = read(connfd, buffer) != SUCCESS);
这样,就需要用户线程循环调用 read,直到返回值不为 -1,再开始处理业务 。文章插图
这里我们注意到一个细节 。
非阻塞的 read,指的是在数据到达前,即数据还未到达网卡,或者到达网卡但还没有拷贝到内核缓冲区之前,这个阶段是非阻塞的 。
当数据已到达内核缓冲区,此时调用 read 函数仍然是阻塞的,需要等待数据从内核缓冲区拷贝到用户缓冲区,才能返回 。
整体流程如下图
文章插图
经验总结扩展阅读
- iPhone8怎么刷机(苹果x强制恢复出厂)
- soul怎么找回之前聊天的人 soul恢复聊天列表方法
- 超人怎么死的(超人怎么复活)
- 廉租房一般几室一厅 廉租房享用的国家政策有哪些
- 一般蛋糕店用的是什么奶油
- 记一次多个Java Agent同时使用的类增强冲突问题及分析
- 光遇啵啵先祖裤子怎么搭配
- fastposter v2.10.0 简单易用的海报生成器
- 螺蛳粉要泡多久
- 羊毛衫缩水了有什么办法可以恢复