IO多路复用技术总结

目录

  • IO 多路复用概述
  • 网络IO模型
    • 同步/异步
    • 阻塞/非阻塞
  • IO多路复用
  • select
    • select调用
    • timeval结构体
    • select置位
    • fd_set结构体
  • select使用
    • Server
    • client
  • 简易聊天室select版本
    • server
    • client
  • poll调用
    • struct pollfd
    • nfds
    • timeout
    • 返回值
  • poll使用
  • epoll调用
    • epoll_create
    • epoll_ctl
    • struct epoll_event
      • Epoll Events:
    • epoll_wait
  • 基于epoll的简易http服务器
    • Build
    • Usage

IO 多路复用概述

I/O 多路复用技术是为了解决进程或线程阻塞到某个 I/O 系统调用而出现的技术,使进程不阻塞于某个特定的 I/O 系统调用。

在IO多路复用技术描述前,先讲解下同步,异步,阻塞,非阻塞的概念。

网络IO模型

linux网络IO中涉及到的模型如下:

(1)阻塞式IO

(2)非阻塞式IO

(3)IO多路复用

(4)信号驱动IO

(5)异步IO

今天不谈信号驱动IO,略过..

同步/异步

在学习IO模型的时候,我们必须明确一个概念,处理 IO 的时候,阻塞和非阻塞都是同步 IO。

只有使用了特殊的 API 才是异步 IO,例如Linux网络中的AIO。

IO多路复用技术总结_第1张图片

再看下POSIX对同步和异步这两个术语的定义:

  • 同步IO操作:导致请求进程阻塞,直到I/O操作完成;
  • 异步IO操作:不导致请求进程阻塞;

通俗的理解下同步和异步

  • 同步:当执行系统调用read时,需要用户等待内核完成从内核缓冲区到用户缓冲区的数据拷贝。

  • 异步:当执行异步IO操作例如aio_read时,用户不需要等待,只需要接收内核完成操作的通知,由内核来完成数据的读取。

阻塞/非阻塞

在知晓阻塞和非阻塞都是同步 IO后,阻塞和非阻塞就很好理解了

阻塞IO:由系统调用read,导致线程一直等待数据返回。

非阻塞IO:系统调用read后立即返回一个状态,当数据达到内核缓冲区之前都是非阻塞的,即返回一个系统调用状态。

ps:闪客的动图做的非常的形象,上述gif动图来源「低并发编程」

IO多路复用

IO多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄;

select

select 是操作系统提供的系统调用函数,select()用来等待文件描述词(普通文件、终端、伪终端、管道、FIFO、套接字及其他类型的字符型)状态的改变。是一个轮循函数,循环询问文件节点,可设置超时时间,超时时间到了就跳过代码继续往下执行。

通过select,我们可以把一个文件描述符的数组发给操作系统, 让操作系统去遍历,确定哪个文件描述符可以读写, 然后告诉我们去处理:

头文件

#include 
#include 
#include 
#include 

select调用

拥塞函数,拥塞等待文件描述符事件的到来

int select(int maxfdp
	, fd_set *readset
	, fd_set *writeset
	, fd_set *exceptset
	,struct timeval *timeout);

参数说明:

maxfdp:被监听的文件描述符的最大值,它比所有文件描述符集合中的文件描述符的最大值大1,因为文件描述符是从0开始计数的;

readfds、writefds、exceptset:分别指向可读、可写和异常等事件对应的描述符集合。

timeout:用于设置select函数的超时时间,即告诉内核select等待多长时间之后就放弃等待。timeout == NULL 表示等待无限长的时间,timeout == 0,select立即返回

timeval结构体

struct timeval
{      
    long tv_sec;   /*秒 */
    long tv_usec;  /*微秒 */   
};

select置位

int FD_ZERO(int fd, fd_set *fdset);   //一个 fd_set类型变量的所有位都设为 0
int FD_CLR(int fd, fd_set *fdset);  //清除某个位时可以使用
int FD_SET(int fd, fd_set *fd_set);   //设置变量的某个位置位
int FD_ISSET(int fd, fd_set *fdset); //测试某个位是否被置位

当声明了一个文件描述符集后,必须用FD_ZERO将所有位置零

调用 select函数,拥塞等待文件描述符事件的到来 ;如果超过设定的时间,则不再等待,继续往下执行

select返回后,用FD_ISSET测试给定位是否置位:

if(FD_ISSET(fd, &rset)   
{ 
    ... 
    //do something  
}

fd_set结构体

fd_set其实这是一个数组的宏定义,实际上是一long类型的数组,每一个数组元素都能与一打开的文件句柄(socket、文件、管道、设备等)建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪个句柄可读。

select使用

整个 select 的流程图如下:
IO多路复用技术总结_第2张图片

Demo1:select示例

Serve

你可能感兴趣的:(java,网络,服务器,linux,spring,java)