嵌入式Linux:I/O多路复用

原创 美男子玩编程 2025-04-19 08:00

点击上方蓝色字体,关注我们


Linux中的I/O多路复用是指一种同时监控多个文件描述符的机制,允许程序在不阻塞的情况下等待多个I/O事件。


I/O多路复用主要通过select、poll和epoll这三种系统调用来实现,应用程序可以监视多个文件描述符的状态变化,如读、写或异常状态。


多路复用的核心在于通过一个系统调用处理多个I/O请求,减少了进程的切换和阻塞,提高了效率。


在传统的I/O模型中,当进程需要从某个文件描述符(如网络套接字、文件、管道等)读取数据时,通常会进入阻塞状态,直到数据就绪。


这种模型适用于单个I/O操作,但在需要处理多个I/O源时,使用阻塞模式会导致效率低下,因为一个I/O的阻塞会导致整个应用程序被挂起。


I/O多路复用可以避免这种问题,使得应用程序能够同时处理多个I/O事件。


例如,在一个高并发的网络服务器中,I/O多路复用能够同时监视多个客户端的连接请求和数据传输,而不必为每个客户端创建一个独立的线程或进程。


应用场景:

  • 高并发服务器:I/O多路复用特别适合于需要同时处理大量连接的服务器,如HTTP服务器、WebSocket服务器等。
  • 实时数据处理:在数据流处理(如日志处理、数据采集)应用中,可以通过多路复用来高效地管理多个数据源的输入。
  • 图形用户界面:GUI程序中也可以利用多路复用来处理多个用户事件,如键盘输入、鼠标点击和窗口更新。

1


select()系统调用

select() 是一种执行 I/O 多路复用操作的系统调用,可以让程序同时监视多个文件描述符的状态变化,从而实现高效的 I/O 操作。


调用 select() 会阻塞进程,直到某个文件描述符变为就绪状态(可以读或写)。


其函数原型如下所示:


int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);


参数详解:

  • nfds:该参数为需要监视的文件描述符的范围,通常设置为所有被监视文件描述符的最大值加 1。文件描述符从 0 开始递增,所以需要将最大文件描述符编号加 1。
  • readfds:指向文件描述符集合的指针,用于检测是否有文件描述符变为可读状态。如果某个文件描述符可读时,select() 会返回该文件描述符的就绪状态。
  • writefds:指向文件描述符集合的指针,用于检测是否有文件描述符变为可写状态。如果某个文件描述符可写时,select() 会返回该文件描述符的就绪状态。
  • exceptfds:指向文件描述符集合的指针,用于检测是否有文件描述符出现异常(如带外数据)。不是文件描述符发生错误,而是用于检测某些非常规的情况。
  • timeout:用于设定 select() 的阻塞行为。它是一个指向 struct timeval 结构体的指针,包含两个成员:秒和微秒。如果 timeout 被设置为 NULL,select() 将一直阻塞直到有文件描述符变为就绪。如果所有文件描述符都没有就绪,且超过了 timeout 指定的时间上限,select() 将返回 0。


在 select() 函数中,readfds、writefds 和 exceptfds 都是指向 fd_set 类型的指针,它们代表文件描述符集合。


fd_set 数据类型内部实现是一个位掩码,用于存储多个文件描述符,但用户无需了解其具体实现细节。


Linux 提供了四个宏来操作这些文件描述符集合:

  • FD_ZERO(fd_set *set):初始化文件描述符集合,将其清空。
  • FD_SET(int fd, fd_set *set):将文件描述符 fd 添加到集合中。
  • FD_CLR(int fd, fd_set *set):从集合中移除文件描述符 fd。
  • FD_ISSET(int fd, fd_set *set):检查文件描述符 fd 是否在集合中,返回 true 表示在集合中。


例如:


fd_set fset;FD_ZERO(&fset); // 初始化集合FD_SET(3, &fset); // 添加文件描述符3FD_SET(4, &fset); // 添加文件描述符4FD_SET(5, &fset); // 添加文件描述符5


返回值详解:

  • 返回值为 -1:表示发生错误,并会设置 errno。常见的错误包括:
    • EBADF:集合中的某个文件描述符无效。
    • EINTR:系统调用被信号中断。
    • EINVAL:nfds 参数非法。
    • ENOMEM:系统内存不足。
  • 返回值为 0:表示在 timeout 指定的时间内没有文件描述符就绪。
  • 返回正整数:表示有一个或多个文件描述符就绪。返回值为就绪文件描述符的数量。


使用 select() 的注意事项:

  1. 文件描述符集合的最大容量限制:fd_set 的容量由常量 FD_SETSIZE 限制。在 Linux 系统中,默认值为 1024。如果需要监视的文件描述符数量超过 1024 个,应该考虑使用 poll() 或 epoll()。
  2. 重复调用 select() 时,需重新初始化文件描述符集合:在每次调用 select() 之前,必须重新初始化和设置文件描述符集合,否则 select() 的结果可能不正确。
  3. 处理超时的两种方式:timeout 设为 NULL,表示无限阻塞直到有文件描述符就绪。设置 timeout 指向的 struct timeval 的两个成员变量都为 0,表示非阻塞模式,即立即返回结果。

以下是一个简单的示例,展示如何使用 select() 来检测标准输入的可读状态:

int main() {    fd_set readfds;    struct timeval timeout;    int ret;    FD_ZERO(&readfds);    FD_SET(STDIN_FILENO, &readfds); // 监视标准输入(文件描述符为0)    timeout.tv_sec = 5// 超时时间为5秒    timeout.tv_usec = 0;    ret = select(STDIN_FILENO + 1, &readfds, NULLNULL, &timeout);    if (ret == -1) {        perror("select error");    } else if (ret == 0) {        printf("Timeout: No data within 5 seconds.\n");    } else {        if (FD_ISSET(STDIN_FILENO, &readfds)) {            printf("Data is available on standard input.\n");        }    }    return 0;}

在这个例子中,select() 会监视标准输入的可读状态,并等待最多 5 秒。如果在此期间有数据可读,则返回成功,否则返回超时。


尽管 select() 在很多场景下都很有用,但也有其局限性:

  1. 性能问题:当监视大量文件描述符时,select() 的效率较低,因为需要对每个文件描述符进行遍历。
  2. 文件描述符数量限制:FD_SETSIZE 限制了最多可以监视的文件描述符数量。
  3. 重复初始化:每次调用都需要重新初始化文件描述符集合。

2


poll()系统调用

poll()系统调用提供了一种执行I/O多路复用的方式,与select()类似,但在接口和用法上有所不同。


poll()使用一个struct pollfd类型的数组来监视文件描述符的就绪状态。


它的原型如下所示:


int poll(struct pollfd *fds, nfds_t nfds, int timeout);

参数解释:

  • fds:指向struct pollfd类型的数组,每个元素表示一个文件描述符和其关注的事件。
  • nfds:指定数组中元素的数量。类型nfds_t是无符号整型。
  • timeout:用于决定poll()的阻塞行为,单位为毫秒,具体规则如下:
    • timeout = -1:一直阻塞,直到一个文件描述符就绪或捕获到信号(类似于select()中timeout为NULL的情形)。
    • timeout = 0:非阻塞调用,只检查一次文件描述符的状态。
    • timeout > 0:阻塞至多timeout毫秒,若超时则返回。

pollfd结构体定义如下:

struct pollfd {    int fd;        /* 文件描述符 */    short events;  /* 请求的事件 */    short revents; /* 返回的事件 */};


  • fd:文件描述符。设置为负数可以忽略此文件描述符。
  • events:表示我们关心的事件类型,使用位掩码的方式。
  • revents:由内核设置,表示实际发生的事件。


events和revents字段支持多种标志,以下列出常见的标志及其说明:



这些标志可以通过位或操作组合,例如events = POLLIN | POLLOUT,表示同时监视可读和可写事件。


下面的示例展示了如何使用poll()来监视文件描述符的可读事件:

int main() {    struct pollfd fds[1];    fds[0].fd = 0// 标准输入    fds[0].events = POLLIN; // 监视可读事件    int timeout = 5000// 超时5秒    int ret = poll(fds, 1, timeout);    if (ret == -1) {        perror("poll");        return 1;    } else if (ret == 0) {        printf("超时,没有数据可读。\n");    } else {        if (fds[0].revents & POLLIN) {            printf("标准输入有数据可读。\n");        }    }    return 0;}

poll()的优点和局限:

  • 优点:poll()可以同时监视大量的文件描述符,数组形式的struct pollfd更容易动态调整文件描述符的集合。
  • 局限:与select()类似,poll()的性能在处理大量文件描述符时也会下降,因为它需要线性扫描整个数组来寻找就绪的文件描述符。


注意事项:

  • poll()的返回值指示就绪的文件描述符数量,但必须通过检查revents字段来了解具体事件。
  • 如果在poll()中将文件描述符设置为负值,则该条目将被忽略,适合在运行时动态调整监视列表。

3


epoll系统调用

epoll是一种高效的I/O多路复用机制,专为处理大量文件描述符而设计,是poll和select的改进版本。


epoll的主要优点在于其对大规模并发连接的性能支持和事件通知的效率提升。


epoll在Linux内核2.5.44版本后引入,并且只在Linux系统上可用。


epoll采用事件驱动模型,它由三个主要的系统调用组成:

  1. epoll_create或epoll_create1:创建epoll实例。
  2. epoll_ctl:向epoll实例中添加、删除或修改文件描述符。
  • epoll_wait:等待事件的发生,并返回就绪的文件描述符列表。

3.1、epoll_create / epoll_create1系统调用

这些函数用于创建一个新的epoll实例。


int epoll_create(int size);


参数:size参数是一个提示值,用于指定初始的文件描述符个数,但实际上它已经被废弃,不再有实际意义。


int epoll_create1(int flags);


参数:flags参数通常可以为0或EPOLL_CLOEXEC,后者设置文件描述符的close-on-exec标志。


成功时,这些函数返回一个新的epoll文件描述符,失败时返回-1。


3.2、epoll_ctl系统调用

用于管理epoll实例中的文件描述符。函数原型如下:


int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);


参数说明:

  • epfd:由epoll_create返回的epoll实例文件描述符。
  • op:操作类型,可以是以下三个值之一:
    • EPOLL_CTL_ADD:添加新的文件描述符到epoll实例。
    • EPOLL_CTL_MOD:修改已存在的文件描述符的事件。
    • EPOLL_CTL_DEL:从epoll实例中删除文件描述符。
  • fd:要操作的文件描述符。
  • event:指向epoll_event结构体的指针,用于指定关心的事件。

struct epoll_event定义如下:struct epoll_event {    uint32_t events; /* 需要监听的事件 */    epoll_data_t data; /* 关联的用户数据 */};


参数说明:

  • events:表示感兴趣的事件类型,可以是以下一个或多个标志的组合:
    • EPOLLIN:数据可读。
    • EPOLLOUT:数据可写。
    • EPOLLERR:错误发生。
    • EPOLLET:启用边缘触发(Edge Triggered)模式。只在状态变化时通知,更高效但需要一次性处理所有数据。
    • EPOLLONESHOT:事件触发一次后自动移除。
  • data:可以是文件描述符或自定义的数据,用于标识事件来源。

3.3、epoll_wait系统调用

用于等待文件描述符的事件。函数原型如下:


int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);


参数说明:

  • epfd:epoll实例文件描述符。
  • events:指向epoll_event结构体数组的指针,用于存储就绪的事件。
  • maxevents:events数组的大小,表示最多返回的事件个数。
  • timeout:指定等待的超时时间,单位为毫秒。
    • timeout = -1:无限期等待,直到有事件发生。
    • timeout = 0:立即返回,不阻塞。

返回值为触发的事件数量,-1表示发生错误。


下面展示了一个使用epoll监视标准输入的基本例子:

#define MAX_EVENTS 5int main() {    int epfd = epoll_create1(0);    if (epfd == -1) {        perror("epoll_create1");        exit(EXIT_FAILURE);    }    struct epoll_event event;    event.events = EPOLLIN; // 监视可读事件    event.data.fd = STDIN_FILENO; // 标准输入    if (epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &event) == -1) {        perror("epoll_ctl: STDIN_FILENO");        exit(EXIT_FAILURE);    }    struct epoll_event events[MAX_EVENTS];    int timeout = 10000// 10秒超时    int nfds = epoll_wait(epfd, events, MAX_EVENTS, timeout);    if (nfds == -1) {        perror("epoll_wait");        exit(EXIT_FAILURE);    }    for (int i = 0; i < nfds; i++) {        if (events[i].data.fd == STDIN_FILENO) {            printf("标准输入有数据可读。\n");        }    }    close(epfd);    return 0;}

epoll的优势:

  • 性能高:通过内核事件通知机制,避免了遍历整个文件描述符集合。
  • 边缘触发支持:能提高高并发情况下的事件处理效率。


适用场景:

  • 网络服务器,尤其是高并发的场景。
  • 大量I/O操作的多任务系统。


总结来说,epoll比select和poll更高效,适合大规模I/O并发的应用场景,且提供灵活的事件控制能力。

点击阅读原文,更精彩~

美男子玩编程 多领域、有深度的开发者交流平台
评论 (0)
  • 4月22日下午,备受瞩目的飞凌嵌入式「2025嵌入式及边缘AI技术论坛」在深圳深铁皇冠假日酒店盛大举行,此次活动邀请到了200余位嵌入式技术领域的技术专家、企业代表和工程师用户,共享嵌入式及边缘AI技术的盛宴!1、精彩纷呈的展区产品及方案展区是本场活动的第一场重头戏,从硬件产品到软件系统,从企业级应用到高校教学应用,都吸引了现场来宾的驻足观看和交流讨论。全产品矩阵展区展示了飞凌嵌入式丰富的产品线,从嵌入式板卡到工控机,从进口芯片平台到全国产平台,无不体现出飞凌嵌入式在嵌入式主控设备研发设计方面的
    飞凌嵌入式 2025-04-28 14:43 170浏览
  • 文/郭楚妤编辑/cc孙聪颖‍越来越多的企业开始蚕食动力电池市场,行业“去宁王化”态势逐渐明显。随着这种趋势的加强,打开新的市场对于宁德时代而言至关重要。“我们不希望被定义为电池的制造者,而是希望把自己称作新能源产业的开拓者。”4月21日,在宁德时代举行的“超级科技日”发布会上,宁德时代掌门人曾毓群如是说。随着宁德时代核心新品骁遥双核电池的发布,其搭载的“电电增程”技术也走进业界视野。除此之外,经过近3年试水,宁德时代在换电业务上重资加码。曾毓群认为换电是一个重资产、高投入、长周期的产业,涉及的利
    华尔街科技眼 2025-04-28 21:55 180浏览
  • 文/Leon编辑/cc孙聪颖‍2023年,厨电行业在相对平稳的市场环境中迎来温和复苏,看似为行业增长积蓄势能。带着对市场向好的预期,2024 年初,老板电器副董事长兼总经理任富佳为企业定下双位数增长目标。然而现实与预期相悖,过去一年,这家老牌厨电企业不仅未能达成业绩目标,曾提出的“三年再造一个老板电器”愿景,也因市场下行压力面临落空风险。作为“企二代”管理者,任富佳在掌舵企业穿越市场周期的过程中,正面临着前所未有的挑战。4月29日,老板电器(002508.SZ)发布了2024年年度报告及2025
    华尔街科技眼 2025-04-30 12:40 232浏览
  • 在智能硬件设备趋向微型化的背景下,语音芯片方案厂商针对小体积设备开发了多款超小型语音芯片方案,其中WTV系列和WT2003H系列凭借其QFN封装设计、高性能与高集成度,成为微型设备语音方案的理想选择。以下从封装特性、功能优势及典型应用场景三个方面进行详细介绍。一、超小体积封装:QFN技术的核心优势WTV系列与WT2003H系列均提供QFN封装(如QFN32,尺寸为4×4mm),这种封装形式具有以下特点:体积紧凑:QFN封装通过减少引脚间距和优化内部结构,显著缩小芯片体积,适用于智能门铃、穿戴设备
    广州唯创电子 2025-04-30 09:02 277浏览
  • 随着电子元器件的快速发展,导致各种常见的贴片电阻元器件也越来越小,给我们分辨也就变得越来越难,下面就由smt贴片加工厂_安徽英特丽就来告诉大家如何分辨的SMT贴片元器件。先来看看贴片电感和贴片电容的区分:(1)看颜色(黑色)——一般黑色都是贴片电感。贴片电容只有勇于精密设备中的贴片钽电容才是黑色的,其他普通贴片电容基本都不是黑色的。(2)看型号标码——贴片电感以L开头,贴片电容以C开头。从外形是圆形初步判断应为电感,测量两端电阻为零点几欧,则为电感。(3)检测——贴片电感一般阻值小,更没有“充放
    贴片加工小安 2025-04-29 14:59 250浏览
  • 在CAN总线分析软件领域,当CANoe不再是唯一选择时,虹科PCAN-Explorer 6软件成为了一个有竞争力的解决方案。在现代工业控制和汽车领域,CAN总线分析软件的重要性不言而喻。随着技术的进步和市场需求的多样化,单一的解决方案已无法满足所有用户的需求。正是在这样的背景下,虹科PCAN-Explorer 6软件以其独特的模块化设计和灵活的功能扩展,为CAN总线分析领域带来了新的选择和可能性。本文将深入探讨虹科PCAN-Explorer 6软件如何以其创新的模块化插件策略,提供定制化的功能选
    虹科汽车智能互联 2025-04-28 16:00 203浏览
  • 浪潮之上:智能时代的觉醒    近日参加了一场课题的答辩,这是医疗人工智能揭榜挂帅的国家项目的地区考场,参与者众多,围绕着医疗健康的主题,八仙过海各显神通,百花齐放。   中国大地正在发生着激动人心的场景:深圳前海深港人工智能算力中心高速运转的液冷服务器,武汉马路上自动驾驶出租车穿行的智慧道路,机器人参与北京的马拉松竞赛。从中央到地方,人工智能相关政策和消息如雨后春笋般不断出台,数字中国的建设图景正在智能浪潮中徐徐展开,战略布局如同围棋
    广州铁金刚 2025-04-30 15:24 227浏览
  • 网约车,真的“饱和”了?近日,网约车市场的 “饱和” 话题再度引发热议。多地陆续发布网约车风险预警,提醒从业者谨慎入局,这背后究竟隐藏着怎样的市场现状呢?从数据来看,网约车市场的“过剩”现象已愈发明显。以东莞为例,截至2024年12月底,全市网约车数量超过5.77万辆,考取网约车驾驶员证的人数更是超过13.48万人。随着司机数量的不断攀升,订单量却未能同步增长,导致单车日均接单量和营收双双下降。2024年下半年,东莞网约出租车单车日均订单量约10.5单,而单车日均营收也不容乐
    用户1742991715177 2025-04-29 18:28 236浏览
  • 一、gao效冷却与控温机制‌1、‌冷媒流动设计‌采用低压液氮(或液氦)通过毛细管路导入蒸发器,蒸汽喷射至样品腔实现快速冷却,冷却效率高(室温至80K约20分钟,至4.2K约30分钟)。通过控温仪动态调节蒸发器加热功率,结合温度传感器(如PT100铂电阻或Cernox磁场不敏感传感器),实现±0.01K的高精度温度稳定性。2、‌宽温区覆盖与扩展性‌标准温区为80K-325K,通过降压选件可将下限延伸至65K(液氮模式)或4K(液氦模式)。可选配475K高温模块,满足材料在ji端温度下的性能测试需求
    锦正茂科技 2025-04-30 13:08 283浏览
  • 贞光科技代理品牌紫光国芯的车规级LPDDR4内存正成为智能驾驶舱的核心选择。在汽车电子国产化浪潮中,其产品以宽温域稳定工作能力、优异电磁兼容性和超长使用寿命赢得市场认可。紫光国芯不仅确保供应链安全可控,还提供专业本地技术支持。面向未来,紫光国芯正研发LPDDR5车规级产品,将以更高带宽、更低功耗支持汽车智能化发展。随着智能网联汽车的迅猛发展,智能驾驶舱作为人机交互的核心载体,对处理器和存储器的性能与可靠性提出了更高要求。在汽车电子国产化浪潮中,贞光科技代理品牌紫光国芯的车规级LPDDR4内存凭借
    贞光科技 2025-04-28 16:52 280浏览
  • 你是不是也有在公共场合被偷看手机或笔电的经验呢?科技时代下,不少现代人的各式机密数据都在手机、平板或是笔电等可携式的3C产品上处理,若是经常性地需要在公共场合使用,不管是工作上的机密文件,或是重要的个人信息等,民众都有防窃防盗意识,为了避免他人窥探内容,都会选择使用「防窥保护贴片」,以防止数据外泄。现今市面上「防窥保护贴」、「防窥片」、「屏幕防窥膜」等产品就是这种目的下产物 (以下简称防窥片)!防窥片功能与常见问题解析首先,防窥片最主要的功能就是用来防止他人窥视屏幕上的隐私信息,它是利用百叶窗的
    百佳泰测试实验室 2025-04-30 13:28 378浏览
  •  探针台的维护直接影响其测试精度与使用寿命,需结合日常清洁、环境控制、定期校准等多维度操作,具体方法如下:一、日常清洁与保养1.‌表面清洁‌l 使用无尘布或软布擦拭探针台表面,避免残留清洁剂或硬物划伤精密部件。l 探针头清洁需用非腐蚀性溶剂(如异丙醇)擦拭,检查是否弯曲或损坏。2.‌光部件维护‌l 镜头、观察窗等光学部件用镜头纸蘸取wu水jiu精从中心向外轻擦,操作时远离火源并保持通风。3.‌内部防尘‌l 使用后及时吹扫灰尘,防止污染物进入机械滑
    锦正茂科技 2025-04-28 11:45 118浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦