跳转到内容
主菜单
主菜单
移至侧栏
隐藏
导航
首页
最近更改
随机页面
MediaWiki帮助
代码酷
搜索
搜索
中文(中国大陆)
外观
创建账号
登录
个人工具
创建账号
登录
未登录编辑者的页面
了解详情
贡献
讨论
编辑“︁
C 语言多路复用
”︁
页面
讨论
大陆简体
阅读
编辑
编辑源代码
查看历史
工具
工具
移至侧栏
隐藏
操作
阅读
编辑
编辑源代码
查看历史
常规
链入页面
相关更改
特殊页面
页面信息
外观
移至侧栏
隐藏
您的更改会在有权核准的用户核准后向读者展示。
警告:
您没有登录。如果您进行任何编辑,您的IP地址会公开展示。如果您
登录
或
创建账号
,您的编辑会以您的用户名署名,此外还有其他益处。
反垃圾检查。
不要
加入这个!
{{Note|本教程假设读者已掌握基础C语言语法及Linux系统调用概念。}} = C语言多路复用 = 多路复用(Multiplexing)是网络编程中一种高效处理多个I/O流的机制,允许单个进程/线程同时监控多个文件描述符(如套接字)。在C语言中,主要通过'''select'''、'''poll'''和'''epoll'''三种系统调用实现。 == 核心概念 == 多路复用解决的核心问题是:'''如何避免为每个连接创建独立线程/进程带来的资源消耗'''。其数学模型可表示为: <math> \text{效率} = \frac{\text{有效事件数}}{\text{系统调用次数}} </math> 典型应用场景包括: * 聊天服务器 * 实时数据采集系统 * HTTP代理服务器 === 工作原理对比 === <mermaid> flowchart TD A[阻塞I/O] -->|单线程阻塞| B(低效) C[多线程I/O] -->|线程切换开销| D(高内存占用) E[多路复用] -->|事件驱动| F(高效低耗) </mermaid> == select系统调用 == 最古老的多路复用实现,通过位掩码监控文件描述符集合。 === 函数原型 === <syntaxhighlight lang="c"> #include <sys/select.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); </syntaxhighlight> === 示例:监控标准输入 === <syntaxhighlight lang="c"> #include <stdio.h> #include <sys/select.h> int main() { fd_set readfds; struct timeval timeout; while(1) { FD_ZERO(&readfds); FD_SET(0, &readfds); // 监控stdin timeout.tv_sec = 5; timeout.tv_usec = 0; int ret = select(1, &readfds, NULL, NULL, &timeout); if (ret == -1) { perror("select"); } else if (ret) { if(FD_ISSET(0, &readfds)) { printf("Data available on stdin\n"); char buf[256]; fgets(buf, sizeof(buf), stdin); printf("You typed: %s", buf); } } else { printf("Timeout occurred\n"); } } return 0; } </syntaxhighlight> '''输出示例:''' <pre> (等待5秒) Timeout occurred (用户输入"hello"后回车) Data available on stdin You typed: hello </pre> === select的局限性 === * 文件描述符数量限制(通常1024) * 每次调用需重新设置fd_set * 线性扫描所有描述符 == poll系统调用 == 改进select的设计,使用链表结构避免数量限制。 === 函数原型 === <syntaxhighlight lang="c"> #include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int timeout); </syntaxhighlight> === 结构体定义 === <syntaxhighlight lang="c"> struct pollfd { int fd; // 文件描述符 short events; // 监控的事件 short revents; // 实际发生的事件 }; </syntaxhighlight> == epoll系统调用 == Linux特有的高效实现,采用事件回调机制。 === 三种关键操作 === <syntaxhighlight lang="c"> int epoll_create(int size); int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); </syntaxhighlight> === 性能对比表 === {| class="wikitable" |- ! 方法 !! 时间复杂度 !! 最大fd数 !! 触发方式 |- | select || O(n) || FD_SETSIZE || 水平触发 |- | poll || O(n) || 无限制 || 水平触发 |- | epoll || O(1) || 系统限制 || 水平/边缘触发 |} == 实际案例:简易聊天服务器 == 以下展示使用epoll的服务器核心逻辑: <syntaxhighlight lang="c"> #define MAX_EVENTS 10 struct epoll_event ev, events[MAX_EVENTS]; int epoll_fd = epoll_create1(0); // 添加监听socket到epoll ev.events = EPOLLIN; ev.data.fd = listen_sock; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, &ev); while(1) { int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); for(int i = 0; i < nfds; i++) { if(events[i].data.fd == listen_sock) { // 处理新连接 int conn_sock = accept(listen_sock, NULL, NULL); ev.events = EPOLLIN | EPOLLET; // 边缘触发模式 ev.data.fd = conn_sock; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_sock, &ev); } else { // 处理客户端数据 char buf[1024]; int len = read(events[i].data.fd, buf, sizeof(buf)); if(len > 0) { // 广播给所有客户端 for(每个已连接客户端) { write(client_fd, buf, len); } } } } } </syntaxhighlight> == 触发模式详解 == * '''水平触发(LT)''':只要文件描述符就绪就会持续通知 * '''边缘触发(ET)''':仅当状态变化时通知一次 <mermaid> stateDiagram-v2 [*] --> 未就绪 未就绪 --> 就绪: 数据到达(ET/LT) 就绪 --> 未就绪: 数据读完(仅ET) 就绪 --> 就绪: 仍有数据(LT持续通知) </mermaid> == 最佳实践建议 == 1. 小规模连接(<1000):poll/select更便携 2. 大规模高并发:优先选择epoll 3. 边缘触发需配合非阻塞I/O使用 4. 注意处理EAGAIN/EWOULDBLOCK错误 {{Warning|多路复用不解决所有问题,CPU密集型任务仍需配合多线程/进程}} == 扩展思考 == * 与多线程模型的性能对比 * 在不同操作系统上的移植性考虑(kqueue, IOCP) * 与异步I/O(AIO)的关系与区别 [[Category:C语言网络编程]] [[Category:系统编程]] [[Category:编程语言]] [[Category:C]] [[Category:C 语言网络编程]]
摘要:
请注意,所有对代码酷的贡献均被视为依照知识共享署名-非商业性使用-相同方式共享发表(详情请见
代码酷:著作权
)。如果您不希望您的文字作品被随意编辑和分发传播,请不要在此提交。
您同时也向我们承诺,您提交的内容为您自己所创作,或是复制自公共领域或类似自由来源。
未经许可,请勿提交受著作权保护的作品!
取消
编辑帮助
(在新窗口中打开)
该页面使用的模板:
模板:Mbox
(
编辑
)
模板:Note
(
编辑
)
模板:Warning
(
编辑
)
模块:Arguments
(
编辑
)
模块:Message box
(
编辑
)
模块:Message box/ambox.css
(
编辑
)
模块:Message box/configuration
(
编辑
)
模块:Yesno
(
编辑
)