I/O 复用

代码实践:Github

I/O 模型

数据的读取分为两个阶段:

  1. 准备数据
  2. 将数据从内核拷贝到进程(用户空间)

对于套接字的输入操作,第一步通常是等待数据从网络中到达,当所有分组到达时,将它复制到内核的某个缓冲区(准备数据);第二步是把数据从内核缓冲区复制到应用进程缓冲区

5种I/O 模型

阻塞式I/O

blockibg I/O

非阻塞式I/O

采用轮训式,一旦内核中数据准备好,便等待数据复制后返回

nonblocking i/o

I/O复用

分为两步,采用select(poll)函数(阻塞),等待数据报套接字变成可读。当select返回这一条件后,再调用 recvfrom 函数去读取数据

i/o multiplexing

信号驱动式I/O

在这里插入图片描述

异步I/O

在这里插入图片描述

对比

前四种在第二阶段相似,都在等待数据从内核复制到用户进程那里阻塞

在这里插入图片描述

5种模型的对比

select 函数

int   select(int maxfd1, fd_set * __restrict, fd_set * __restrict,
    fd_set * __restrict, struct timeval * __restrict)

struct timeval

_STRUCT_TIMEVAL
{
	__darwin_time_t         tv_sec;         /* seconds */
	__darwin_suseconds_t    tv_usec;        /* and microseconds */
};

告知内核等待所制定描述符中的任何一个就绪可花费多长时间

这个参数有三种可能:

  1. 永远等待下去,有一个描述符准备好后I/O才返回,此时将该 __restrict 设置为 null
  2. 等待一段固定时间, 有一个描述符准备好后I/O才返回, 等待固定时间(__restrict)未收到也直接返回
  3. 不等待, 检查描述符后立刻返回,类似于轮询的方式 __restrict 设置为0

其实第1,3都是第2中的一种特例,区分在于时间的设置

中间三个fd_set

代表 可读、可写、发生异常三种描述符,都是整数数组。在select 函数中可以置为空

异常分为两种

  1. 某个套接字的外带数据到达
  2. 某个已经设置为分组模式的伪终端存在可从其主端读取的控制状态信息

fd_set

是一个整数数组,最大可以表示1024个描述符数量,它底层是一个整数数组(int型数组,大小为32个)

#define __DARWIN_FD_SETSIZE     1024
#define __DARWIN_NBBY           8                               /* bits in a byte */
#define __DARWIN_NFDBITS        (sizeof(__int32_t) * __DARWIN_NBBY) /* bits per mask */
typedef struct fd_set {
	__int32_t       fds_bits[__DARWIN_howmany(__DARWIN_FD_SETSIZE, __DARWIN_NFDBITS)];
} fd_set;

操作fd_set

主要使用以下宏

在这里插入图片描述


    fd_set rset;
    // 重置为0
    FD_ZERO(&rset);
    // 设置位置, 最大是1024
    FD_SET(1000, &rset);
    // 判断此位是否设置
    FD_ISSET(1000, &rset);
    // 将该位置位0
    FD_CLR(1000, &rset);

判定条件

在这里插入图片描述

maxfd1

带测试描述符的个数,通常为最大描述符+1

因为,它扫描 0~maxfd1 区间的描述符

poll 函数

int poll(struct pollfd *, nfds_t, int)

struct pollfd {
	int     fd;
	short   events;
	short   revents;
};

typedef unsigned int nfds_t;

从函数可以看出,它于select的最大区别在于,它将fd组织成了一个链表,这样突破了1024的限制,理论上是无限的。它将三种事件描述符(可读、可写、异常发生)绑定到每个fd上,

struct pollfd {
	int     fd;
	short   events;
	short   revents;
};

他将事件分为以下几种,通过判定revents字段来确认是否准备完毕
在这里插入图片描述

References

UNIX网络编程 卷1:套接字联网API(第3版)

linux select函数详解

Linux IO模式及 select、poll、epoll详解

IO多路复用之epoll总结

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐