为了账号安全,请及时绑定邮箱和手机立即绑定

作业社区

探索学习新天地,共享知识资源!

0 提交作业
0 布置作业
0 满分作业
得分 100
学习任务

沫颖 的学生作业:

【图片】 理解 epoll 是 Linux 特有的高效 I/O 多路复用机制,相比于 select 和 poll,epoll 在处理大量文件描述符时性能更高。它的底层原理基于事件驱动模型,通过内核与用户空间的协作,实现对文件描述符的高效监控。 epoll 的核心概念 事件驱动: epoll 基于事件驱动模型,只有当文件描述符的状态发生变化时,才会通知应用程序。 这种机制避免了 select 和 poll 中需要遍历所有文件描述符的开销。 红黑树: epoll 使用红黑树(一种自平衡二叉搜索树)来存储需要监控的文件描述符。 红黑树的插入、删除和查找操作的时间复杂度为 O(log n),适合处理大量文件描述符。 就绪链表: epoll 使用一个双向链表来存储就绪的文件描述符。 当文件描述符的状态发生变化时,内核会将其添加到就绪链表中。 边缘触发(ET)和水平触发(LT): epoll 支持两种触发模式: 边缘触发(Edge Triggered, ET):只有当文件描述符的状态发生变化时,才会通知应用程序。 水平触发(Level Triggered, LT):只要文件描述符处于就绪状态,就会通知应用程序。 epoll 的工作流程 创建 epoll 实例: 使用 epoll_create 或 epoll_create1 创建一个 epoll 实例,返回一个文件描述符。 注册文件描述符: 使用 epoll_ctl 向 epoll 实例注册需要监控的文件描述符和事件类型(如 EPOLLIN、EPOLLOUT 等)。 等待事件: 使用 epoll_wait 等待文件描述符的状态变化。 epoll_wait 会返回就绪的文件描述符列表。 处理事件: 遍历就绪的文件描述符列表,执行相应的 I/O 操作。 epoll 的底层原理 内核与用户空间的协作 epoll 通过内核与用户空间的协作,实现对文件描述符的高效监控。 内核维护一个红黑树和一个就绪链表: 红黑树存储所有需要监控的文件描述符。 就绪链表存储状态发生变化的文件描述符。 事件通知机制 当文件描述符的状态发生变化时,内核会将其添加到就绪链表中。 用户空间调用 epoll_wait 时,内核会将就绪链表中的文件描述符复制到用户空间。 边缘触发(ET)与水平触发(LT)的实现 边缘触发(ET): 只有当文件描述符的状态发生变化时,才会通知应用程序。 应用程序需要一次性处理所有数据,否则可能会丢失事件。 水平触发(LT): 只要文件描述符处于就绪状态,就会通知应用程序。 应用程序可以分多次处理数据。 高效的原因 红黑树的高效性: 红黑树的插入、删除和查找操作的时间复杂度为 O(log n),适合处理大量文件描述符。 就绪链表的直接访问: epoll_wait 直接返回就绪的文件描述符列表,避免了遍历所有文件描述符的开销。 事件驱动模型: 只有当文件描述符的状态发生变化时,才会通知应用程序,减少了不必要的开销。 epoll 的系统调用 epoll_create / epoll_create1: 创建一个 epoll 实例,返回一个文件描述符。 epoll_create1 支持额外的标志(如 EPOLL_CLOEXEC)。 epoll_ctl: 向 epoll 实例注册、修改或删除文件描述符。 参数: epfd:epoll 实例的文件描述符。 op:操作类型(EPOLL_CTL_ADD、EPOLL_CTL_MOD、EPOLL_CTL_DEL)。 fd:需要监控的文件描述符。 event:事件类型(如 EPOLLIN、EPOLLOUT 等)。 epoll_wait: 等待文件描述符的状态变化。 参数: epfd:epoll 实例的文件描述符。 events:存储就绪事件的数组。 maxevents:events 数组的大小。 timeout:超时时间(毫秒)。

得分 100
学习任务

沫颖 的学生作业:

代码 /** * 1-6 多路复用io-poll(一)基本原理与应用 - 课后练习 示例 使用 poll 函数监控标准输入,如果有输入,则获取标准输入的内容并打印 练习 使用 poll 监听有名管道,当有名管道有数据时,读取数据并打印 */ #include #include #include #include #include #include #include #include #include #define BUFFER_SIZE 1024 #define TIMEOUT_MS 3000 // 超时时间(毫秒) #define MESSAGE_PREFIX "Hello from fifo1-" // 子线程函数:读取 FIFO 数据 void *thread_function(void *arg) { const char *fifo_name = (const char *)arg; int fd; // 打开有名管道 printf("Thread %lu: 打开 FIFO %s\n", pthread_self(), fifo_name); fd = open(fifo_name, O_RDONLY); if (fd == -1) { perror("open"); pthread_exit(NULL); } // 初始化 pollfd 结构体 struct pollfd fds[1]; fds[0].fd = fd; // 监视有名管道的文件描述符 fds[0].events = POLLIN; // 监视可读事件 while (1) { // 调用 poll,设置超时时间为 3000 毫秒(3 秒) int ret = poll(fds, 1, TIMEOUT_MS); if (ret == -1) { perror("poll"); break; } else if (ret == 0) { // 超时 printf("Time Out!\n"); } else { // 检查有名管道是否可读 if (fds[0].revents & POLLIN) { char buffer[BUFFER_SIZE]; ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1); if (bytes_read > 0) { buffer[bytes_read] = '\0'; printf("从 FIFO 读取到数据: %s", buffer); } else if (bytes_read == 0) { // EOF printf("FIFO 已关闭\n"); } else { perror("read"); } } } } close(fd); pthread_exit(NULL); } int main() { const char *fifo_name = "myfifo"; // 创建有名管道 if (mkfifo(fifo_name, 0666) == -1) { perror("mkfifo"); exit(EXIT_FAILURE); } pthread_t thread1; // 创建线程(先启动子线程,确保读取端先打开) printf("Main: 创建子线程\n"); if (pthread_create(&thread1, NULL, thread_function, (void *)fifo_name) != 0) { perror("pthread_create"); unlink(fifo_name); // 删除有名管道 exit(EXIT_FAILURE); } // 等待子线程打开 FIFO 的读取端 sleep(1); // 打开有名管道的写入端 printf("Main: 打开 FIFO %s\n", fifo_name); int fd1 = open(fifo_name, O_WRONLY); if (fd1 == -1) { perror("open write end"); unlink(fifo_name); // 删除有名管道 exit(EXIT_FAILURE); } // 主线程写入数据到有名管道 int counter = 0; while (1) { char message1[BUFFER_SIZE]; // 拼接字符串 snprintf(message1, sizeof(message1), "%s%d\n", MESSAGE_PREFIX, counter++); // 写入数据 if (write(fd1, message1, strlen(message1)) == -1) { perror("write"); } // 等待一段时间(确保大于超时时间) sleep(TIMEOUT_MS/1000 + 1); // 确保写入间隔大于超时时间 } // 关闭写入端 printf("Main: 关闭 FIFO %s\n", fifo_name); close(fd1); // 等待线程结束 printf("Main: 等待子线程结束\n"); pthread_join(thread1, NULL); // 删除有名管道 printf("Main: 删除 FIFO %s...\n", fifo_name); unlink(fifo_name); return 0; } 效果 felix@felixlinux:~/Desktop/Study/CProjects/imooc-embedded/Stage05/Week12/Class12$ ./a.out Main: 创建子线程 Thread 281473663103264: 打开 FIFO myfifo Main: 打开 FIFO myfifo 从 FIFO 读取到数据: Hello from fifo1-0 Time Out! 从 FIFO 读取到数据: Hello from fifo1-1 Time Out! 从 FIFO 读取到数据: Hello from fifo1-2 ^C

微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号