首页 > 编程语言 >【网络编程】IO多路复用

【网络编程】IO多路复用

时间:2025-01-21 09:31:44浏览次数:3  
标签:文件 多路复用 int 编程 描述符 fd IO include 监听

目录

IO多路复用

场景假设

处理思想

接口

参考程序

总结

其它多路复用方案


IO多路复用

场景假设

假设妈妈有三个孩子,分别不同的房间里睡觉,需要及时获知每个孩子是否醒了,如何做?

  1. 不停进每个房间看一下:简单,空闲时间还能干点别的,但是很累
  2. 告诉爸爸,让爸爸帮忙监听,妈妈可以干别的或者睡觉,孩子醒了后,爸爸告诉妈妈哪个孩子需要照顾,及时处理即可:既能得到休息,也能及时获知每个孩子的状态

处理思想

  • 应用程序中同时处理多路输入输出流,若采用阻塞模式,将得不到预期的目的;
  • 若采用非阻塞模式,对多个输入进行轮询,但又太浪费CPU时间;
  • 若设置多个进程/线程,分别处理一条数据通路,将新产生进程/线程间的同步与通信问题,使程序变得更加复杂;
  • 比较好的方法是使用I/O多路复用技术。其基本思想是:
    • 先构造一张监听描述符表(最大1024),然后调用一个函数进入监听状态。
    • 当这些文件描述符中的一个或多个有数据时,函数才返回。
    • 函数返回时告诉进程哪个描述符已就绪,可以进行I/O操作。

接口

int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
功能:
	实现IO的多路复用
参数:
	nfds:关注的最大的文件描述符+1
	readfds:关注的读表
	writefds:关注的写表
	exceptfds:关注的异常表
	timeout:超时的设置
		NULL:一直阻塞,直到有文件描述符就绪或出错
		时间值为0:仅仅检测文件描述符集的状态,然后立即返回
		时间值不为0:在指定时间内,如果没有事件发生,则超时返回0,并清空设置的时间值

struct timeval {
      
      
	long tv_sec;		/* 秒 */
	long tv_usec;	/* 微秒 = 10^-6秒 */
};
		
返回值:
	准备好的文件描述符的个数
	-1 :失败:
	0:超时检测时间到并且没有文件描述符准备好	

注意:
	select返回后,关注列表中只存在准备好的文件描述符

void FD_CLR(int fd, fd_set *set); //清除集合中的fd位
int  FD_ISSET(int fd, fd_set *set);//判断fd是否在集合中  是--》1   不是---》0
void FD_SET(int fd, fd_set *set);//将fd放入关注列表中
void FD_ZERO(fd_set *set);//清空关注列表

编程步骤

  1. 把关注的文件描述符放入集合--FD_SET
  2. 监听集合中的文件描述符--select
  3. 依次判断哪个文件描述符有数据--FD_ISSET
  4. 依次处理有数据的文件描述符的数据

以下提到的监听表,都是位表,第几位被置位,那么就代表这个文件描述符需要被监听或者有数据

参考程序

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>


//需求:同时监听标准输入和鼠标,不管哪一个有数据,及时处理,并且主进程不能占用太多资源。

int main(int argc, char const *argv[])
{
      
      
    int fd;
    //打开鼠标设备
    fd = open("/dev/input/mouse0", O_RDONLY);
    if(fd < 0)
    {
      
      
        perror("open err");
        return -1;
    }

    fd_set rdfds;

    int ret;
    char buf[64] = {
      
      0};
    while (1)
    {
      
      
        //1.把关注的文件描述符放入集合--FD_SET
        FD_ZERO(&rdfds);  //清空关注列表
        FD_SET(0, &rdfds);  //把标准输入放入到表中进行监听
        FD_SET(fd, &rdfds);  //把鼠标放入到表中进行监听
        
        memset(buf, 0, 64);
        //2.监听集合中的文件描述符--select
        ret = select(fd + 1, &rdfds, NULL, NULL, NULL);
        if(ret < 0)
        {
      
      
            perror("select err");
            return -1;
        }

        //3.依次判断哪个文件描述符有数据--FD_ISSET
        if(FD_ISSET(0, &rdfds))  //判断一下标准输入是否有数据
        {
      
      
            //标准输入有数据,那么就获取它的数据
            gets(buf);
            printf("stdin = %s\n", buf);
        }

        if(FD_ISSET(fd, &rdfds))  //判断一下鼠标是否有数据
        {
      
      
            //鼠标有数据,那么就获取它的数据
            int len = read(fd, buf, 64);
            if(len > 0)
            {
      
      
                printf("read mouse len = %d\n", len);
            }
        }
    }
    
    return 0;
}

总结

阻塞模型:不占用CPU,但是不能同时处理多个设备

非阻塞模型:能同时处理多个设备,但是非常耗费CPU

异步IO/信号驱动IO:完全解放了主线程,有数据会主动通知,异步调用处理方法,但是也不能同时处理多个设备。

IO多路复用:既能同时处理多个设备,又不占用CPU

以上这4种方法没有绝对优劣之分,在不同的场合使用不同的方法。

其它多路复用方案

select

• 一个进程最多只能监听1024个文件描述符 (千级别)

•  select每次会清空表,每次都需要拷贝用户空间的表到内核空间,效率低

poll

• 优化文件描述符个数的限制(根据poll函数第一个函数的参数来定,如果监听的事件为1个,则结构体数组的大小为1,如果想监听100个,那么这个结构体数组的大小就为100,由程序员自己来决定)

• poll不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
作用:监视并等待多个文件描述符的属性变化
参数:
    fds:指向一个结构体数组的第0个元素的指针,每个数组元素都是一个struct        pollfd结构,用于指定某个给定的fd的条件
    nfds:一共需要监听几个描述符
    timeout:等待的超时时间,-1:永远等待,0:立即返回,>0:等待相应的ms
    
返回值:同select

struct pollfd{
      
      
	int fd;			//文件描述符
	short events;  	//等待的事件--POLLIN、POLLOUT、POLLERR
	short revents;	//实际发生的事件
};

poll机制参考程序

#include <stdio.h>
#include <poll.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, const char *argv[])
{
      
      
	int fd_mouse;
	// 1.创建一个用于保存要监测文件描述符的结构体
	struct pollfd fds[2];

	// 2.将关心的文件描述符及对应关心事件添加到结构体数据中
	fd_mouse = open("/dev/input/mouse0", O_RDONLY);
	if (fd_mouse < 0)
	{
      
      
		perror("open mouse2 err.");
		return -1;
	}
    
    //标准输入放到表里,监听读事件
	fds[0].fd = 0;
	fds[0].events = POLLIN;
    
    //鼠标放到表里,监听读事件
	fds[1].fd = fd_mouse;
	fds[1].events = POLLIN;

	char buf[32];
	int ret;
	while (1)
	{
      
      
		if ((ret = poll(fds, 2, -1)) < 0)
		{
      
      
			perror("poll err.");
			return -1;
		}
		else if (ret == 0)
		{
      
      
			printf("timeout .........\n");
		}
		if (fds[0].revents == POLLIN)
		{
      
      
			fgets(buf, 32, stdin);
			printf("recv from stdin:%s\n", buf);
		}
		if (fds[1].revents == POLLIN)
		{
      
      
			memset(buf, 0, 32);
			int ret = read(fd_mouse, buf, 32);
			printf("read from mouse len = %d\n", ret);
		}
	}

	close(fd_mouse);
	return 0;
}

select() 和 poll() 系统调用的本质一样,poll() 的机制与 select() 类似,与 select() 在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是 poll() 没有最大文件描述符数量的限制(但是数量过大后性能也是会下降)。poll() 和 select() 同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

epoll(了解即可)epoll详解

• 监听的最大的文件描述符没有个数限制(理论上,取决与你自己的系统)

• 异步I/O,Epoll当有事件产生被唤醒之后,文件描述符主动调用callback(回调函数)函数直接拿到唤醒的文件描述符,不需要轮询,效率极高

标签:文件,多路复用,int,编程,描述符,fd,IO,include,监听
From: https://blog.csdn.net/ciweic/article/details/145264857

相关文章

  • relation goes to calmness that is reassuring but not very exciting
    Whenweconfuseassertionwithaggression,neutralizeotherness,adjustourlongings,andreasonawayourhostility,weassembleacalmnessthatisreassuringbutnotveryexciting.StephenMitchellmakesthepointthatthecapacitytocontainaggressionis......
  • 【高创新】基于matlab斑马算法ZOA-CNN-LSTM-Attention用客流量预测【含Matlab源码 842
    ......
  • 【C++提高篇】—— C++泛型编程之模板基本语法和使用的详解
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录前言一、模板的概念二、函数模板2.1函数模板的使用2.2函数模板注意事项2.3普通函数与函数模板的区别2.4普通函数与函数模板的调用规则2.5模板的局限性三、类模板3.1类模板的使用3.2类模板......
  • ISO镜像软件 ISO Workshop Professional v13 中文注册版
    在数字时代,管理光盘和创建ISO映像是一项必备技能。ISOWorkshopPro正是为此而生,这款强大的PC端ISO镜像工具,能够助你轻松完成从备份、刻录到转换的全方位光盘任务。无论是CD、DVD还是蓝光光盘,ISOWorkshopPro都能一一搞定。该版本已注册,可以使用全部功能。使用说明:1、将压......
  • copype 是 Windows PE (Preinstallation Environment) 中的一个命令行工具,是 Windows
    Copype命令行选项|MicrosoftLearn copype是WindowsPE(PreinstallationEnvironment)中的一个命令行工具,通常用于创建和准备WindowsPE的工作环境。它是WindowsADK(WindowsAssessmentandDeploymentKit)中的一个实用工具,用来帮助用户快速复制WindowsPE文件......
  • TIA SCL编程清除字符串中所有的空格
    今天做一个小的练习,这是2025年第一个记录的学习笔记。在IA新建一个FC,名字叫做TrimSpace,建立以下内部变量: 写一段SCL代码:#len:=LEN(#str_in);#str_trim_out:='';FOR#i:=1TO#lenDOIFMID(IN:=#str_in,L:=1,P:=#i)<>''THEN#str_t......
  • P11269 【MX-S5-T3】IMAWANOKIWA (Construction ver.)
    P11269【MX-S5-T3】IMAWANOKIWA(Constructionver.)题目翻译:对一个初始长度为\(n\)的序列\(a\)进行操作,每次操作可以任选两个相邻的数\(a_j,a_{j+1}\)将这两个数删去,在加上\(popc(a_j+a_{j+1})\)\(popc(x)\),表示\(x\)在二进制下\(1\)的个数而我们要求出一个操......
  • AAAI2024论文解读|Bidirectional Contrastive Split Learning for Visual Question An
    论文标题BidirectionalContrastiveSplitLearningforVisualQuestionAnswering双向对比分裂学习用于视觉问答论文链接BidirectionalContrastiveSplitLearningforVisualQuestionAnswering论文下载论文作者YuweiSun,HideyaOchiai内容简介本文提出了一种名......
  • U455764 The Rotation Game
    U455764TheRotationGame题目理解本题要求移动\(A-H\)中的一列或一行,使其整个一行和一列的数字移动,使最后的中间8个的数字相同。求最少需要移动的步数和它的操纵顺序思路1.本题可以很显然的想到用\(BFS\)来枚举执行不同字母操作后结果,但每\(BFS\)一次就会增加八倍最后......
  • Doris 2.1 Queries Acceleration -Tuning Plan学习笔记
    1OptimizingTableSchemaDesign1.1Case1:TableEngineSelection1.1.1Thequeryperformanceofthesetablemodels,frombesttoworst,is:Duplicate>MOW>MOR==Aggregate.1.2Case2:BucketColumnSelection1.2.1Selectingappropriatebucket......