Linux下IO多路复用

本文详细介绍了Linux下的三种IO多路复用技术:select、poll和epoll。select系统调用用于监视多个文件描述符状态变化,但其可监控文件描述符数量有限。poll提供更灵活的接口,但仍有轮询问题。epoll则通过回调机制和高效的数据结构解决了这些问题,适用于大量并发连接的场景,其工作模式包括边缘触发(ET)和水平触发(LT)。
摘要由CSDN通过智能技术生成

一、IO多路复用处理数据报文

在这里插入图片描述

二、select

1. 简介

select系统调用是用来让我们的程序监视多个文件描述符的状态变化的。程序会阻塞到select这里,直到被监听的文件描述符一个或者多个发生了状态变化。

2. 函数原型

int select(int nfds, fd_set* read_fds, fd_set* write_fds, 
           fd_set* except_fds, struct timeval* timeout);
2.1 参数说明
  • nfds:是需要监视的最大的文件描述符值+1
  • read_sets:对应于需要检测的可读文件描述符的集合
  • write_sets:对应于需要检测的可写文件描述符的集合
  • except_sets:对应于需要检测的异常文件描述符的集合
  • timeout:用来设置select的等待时间
    • NULL:表示不设置select的等待时间,select在监听到描述符状态变化之前将一直阻塞
    • 0:表示仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生
    • 特定的时间值:如果在这个设置的时间值内没有事件发生,select将超时返回
2.2 fd_set结构说明

首先,我们可以看一下这个结构的定义:

/* The fd_set member is required to be an array of longs. */
typedef long int __fd_mask;

typedef struct{
   
 	/* something */
	__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
    /* something */
};

注:上面代码节选自<sys/select.h>中,其中我只保留了便于理解的部分。

从定义中我们可以看出,其实fd_set结构就是一个long型数组,或者说,它代表一种数据结构----“位图”。使用位图中对应的位来表示要监视的文件描述符。

select提供了一组操纵位图的接口:

void FD_CLR(int fd, fd_set *set);  // 用来清除描述位图set中相关fd的位
int FD_ISSET(int fd, fd_set *set); // 用来测试描述位图set中相关fd的位是否为真
void FD_SET(int fd, fd_set *set);  // 用来设置描述位图set中相关fd的位
void FD_ZERO(fd_set* set);         // 用来清空位图set中的所有位
2.3 timeval结构说明

这个结构的定义:

/* A time value that is accurate to the nearest
   microsecond but also has a range of years.
*/
struct timeval{
   
    __time_t tv_sec; /* Second. */
    __suseconds_t tv_usec; /* Microseconds. */
};

注:上面代码节选自<time.h>中,其中我只保留了便于理解的部分。

2.4 返回值说明

执行成功

  • 返回文件描述符中状态已经改变的描述符个数

其他结果

  • 返回0,表示在参数传入的timeout时间内没有文件描述符状态发生变化
  • 返回-1,表示在执行中发生错误,错误原因存储于errno中,errno可能的结果有以下几种:
    • EBADF:文件描述符无效或文件已关闭
    • EINTR:此次select调用被信号打断
    • EINVAL:参数n为负值
    • ENOMEM:内存不足

3. 就绪条件

3.1 读就绪
  • socket内核中, 接收缓冲区中的字节数, 大于等于低水位标记SO_RCVLOWAT;
  • socket TCP通信中, 对端关闭连接, 此时对该socket读, 则返回0;
  • 监听的socket上有新的连接请求;
  • socket上有未处理的错误。
3.2 写就绪
  • socket内核中, 发送缓冲区中的可用字节数(发送缓冲区的空闲位置大小), 大于等于低水位标记

    SO_SNDLOWAT;

  • socket的写操作被关闭(close或者shutdown). 对一个写操作被关闭的socket进行写操作, 会触发SIGPIPE

    信号;

  • socket使用非阻塞connect连接成功或失败之后;

  • socket上有未处理的错误。

4. 函数使用

使用select实现一个本地回显程序。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>

int main() {
   
  fd_set read_fds;
  FD_ZERO(&read_fds); //初始化fd_set结构
  FD_SET(STDIN_FILENO, &read_fds); //监听标准输入
  while(1){
   
    printf("> ");
    fflush(stdout);
    int ret = select(STDIN_FILENO+1, &read_fds, NULL, NULL, NULL);
    if(ret < 0){
   
      perror("select");
      continue;
    }
    if(FD_ISSET(STDIN_FILENO, &read_fds)){
   
      char buf[1024] = {
   0}; 
      read(STDIN_FILENO, buf, sizeof(buf) - 1); //读取键盘输入
      printf("Echo: %s\n", buf);
    } else {
   
      printf("error! invalid fd\n");
      continue;
    }
    FD_ZERO(
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值