当前位置: 首页 > news >正文

poll为什么使用poll_list链表结构而不是数组 - 深入内核源码分析

一:引言

在Linux内核中,poll机制是一个非常重要的I/O多路复用机制。它允许进程监视多个文件描述符,等待其中任何一个进入就绪状态。poll的内部实现使用了poll_list链表结构而不是数组,这个设计选择背后有其深层的技术考量。本文将从内核源码层面深入分析这个设计决策的原因。

二:poll的基本工作原理

poll系统调用的基本接口如下:

#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

fds 是一个 struct pollfd 数组,每个元素包含一个文件描述符及其事件掩码。

nfds 是数组的大小。

timeout 是等待的时间(以毫秒为单位),如果为-1则无限期等待。

struct pollfd 结构体定义如下:

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

在Linux内核中,poll 的实现主要位于 fs/select.c 文件中。我们可以通过以下步骤来追踪 poll 的执行流程:

1、用户空间到内核空间的转换:

用户空间的 poll 调用通过系统调用进入内核,内核调用 sys_poll 函数。

2、文件描述符集合的构建:

内核需要将用户传递的 pollfd 数组转换为内核可以处理的数据结构。

3、轮询文件描述符:

内核遍历文件描述符集合,检查每个文件描述符是否满足指定的事件条件。

4、结果返回:

将结果返回给用户空间。

其中第2点,应用层我们传递的是pollfd数组,要转换为内核的数据结构,内核对应的数据结构为:(V5.15版本)

// include/linux/poll.h
struct poll_list {struct poll_list *next;   // 指向下一个poll_list节点int len;                  // 本节点包含的pollfd数量  struct pollfd entries[]; // 弹性数组成员
};// 文件系统层面的poll实现接口
struct file_operations {...__poll_t (*poll) (struct file *file, struct poll_table_struct *wait);...
};

poll_list 是一个动态分配的链表节点,每个节点包含一个 poll_table_entry 数组,用于存储文件描述符及其相关的等待队列

三:为什么选择链表而不是数组

1、内存分配的灵活性

使用链表结构的第一个重要原因是内存分配的灵活性。让我们看看内核是如何为poll_list分配内存的:

#define POLLFD_PER_PAGE  ((PAGE_SIZE-sizeof(struct poll_list)) / sizeof(struct pollfd))
//这个宏定义计算了在一个页面大小内可以容纳多少个 struct pollfd 结构体。PAGE_SIZE 是系统页面的大小,
//sizeof(struct poll_list) 是 poll_list 结构体的大小,sizeof(struct pollfd) 是 pollfd 结构体的大小。
//这个计算结果表示在一个页面内除了存储 poll_list 结构体本身外,还能存储多少个 pollfd 结构体。static struct poll_list *alloc_poll_list(int size)
{struct poll_list *p;int entries = POLLFD_PER_PAGE;if (size < entries) {entries = size;}p = kmalloc(struct_size(p, entries, sizeof(struct pollfd)), GFP_KERNEL);if (!p)return NULL;p->next = NULL;p->len = entries;return p;
}

从上面的代码可以看出:

poll_list使用页对齐的内存分配,每个节点尽量利用一个完整的页面

通过链表结构,可以根据实际需要的pollfd数量动态分配多个节点

避免了一次性分配大块连续内存的问题

如果使用数组结构,则存在以下问题:

// 假设使用数组方式
struct poll_array {int len;struct pollfd entries[]; // 需要一次性分配足够大的连续内存
};// 问题演示
struct poll_array *alloc_poll_array(int size) 
{// 1. 需要一次性分配大块连续内存// 2. 可能导致内存碎片// 3. 在内存压力大时更容易分配失败return kmalloc(sizeof(*p) + size * sizeof(struct pollfd), GFP_KERNEL);
}

2、支持大量文件描述符

链表结构的第二个重要优势是能够支持大量文件描述符。内核中的实现:

// fs/select.c
static int do_poll(struct poll_list *list, struct poll_wqueues *wait,struct timespec64 *end_time)
{poll_table* pt = &wait->pt;int error = 0;for (;;) {struct poll_list *walk;for (walk = list; walk != NULL; walk = walk->next) {struct pollfd * pfd = walk->entries;int len = walk->len;for (int i = 0; i < len; i++) {// 处理每个文件描述符error = do_pollfd(&pfd[i], pt, ...);}}if (!pt->qproc)break; // 所有fd都已就绪或出错if (signal_pending(current))break;if (end_time && time_after64(ktime_get(), *end_time))break; // 超时schedule(); // 让出CPU}return error;
}

通过链表结构:

可以支持远超过单页内存大小的文件描述符数量

每个节点的大小限制在一个页面内,便于内存管理

遍历性能损失可以忽略不计,因为poll本身就是一个相对耗时的操作

如果使用数组:

// 使用数组的问题
struct poll_array {int len; struct pollfd entries[MAX_POLL_FDS];
};// 1. 需要预定义最大FD数量
// 2. 过大的数组导致栈内存浪费
// 3. 难以支持动态扩容

3、高效的插入和删除操作

链表的优势:

常数时间复杂度:链表在插入和删除节点时具有常数时间复杂度 O(1),而数组需要移动大量元素,时间复杂度为 O(n)。

减少数据移动:链表不需要移动其他元素,只需要修改指针即可完成插入和删除操作。

数组的限制:

移动元素:数组在插入或删除元素时需要移动大量元素,特别是在数组中间位置进行操作时,性能下降明显。

复杂度高:数组的操作复杂度较高,不适合频繁的插入和删除操作

总结

  1. 动态扩展性:链表可以根据实际需要动态分配和释放内存,避免了固定大小数组带来的内存浪费或不足问题。

  2. 高效的插入和删除操作:链表在插入和删除节点时具有常数时间复杂度,而数组需要移动大量元素,导致性能下降。

  3. 优化内存管理:页对齐的内存分配可以简化内存管理操作,提高内存管理的效率。

相关文章:

  • Java文件批量复制工具实现解析
  • 【npm install 一直转圈的问题】
  • 力扣HOT100——560.和为k的子数组
  • Kaggle竞赛——商店销售时序预测(Store Sales)
  • ROS---<angles>
  • 分布式锁+秒杀异步优化
  • 从零开始:Python运行环境之VSCode与Anaconda安装配置全攻略 (1)
  • 4.vtk光照vtkLight
  • 使用Python爬取豆瓣电影Top250并保存到Excel完整教程
  • 测试基础笔记第四天(html)
  • 交换机与路由器的默契配合:它们的联系与区别
  • Zookeeper 命令返回数据的含义
  • 【软考-架构】13.4、质量属性-架构评估
  • 【HDFS入门】HDFS核心组件Failover Controller:高可用保障机制解析
  • K8s 生产落地
  • 计算机网络:实验五路由器的应用
  • 360壁纸如何卸载
  • 阿里云 MSE Nacos 发布全新“安全防护”模块,简化安全配置,提升数据保护
  • 关于SQLite轻量数据库的研究
  • 控制理论与应用Latex模版/中文Latex
  • 铁路上海站今日预计发送旅客65.8万人次,同比增长超16%
  • 广东省副省长刘红兵跨省调任湖南省委常委、宣传部长
  • 中国人保不再设监事会,国寿集团未再设置监事长职务
  • 陈文清:推进扫黑除恶常态化走深走实,有力回应人民群众对安居乐业的新期待
  • 辽宁辽阳火灾事故饭店经营者已被控制,善后处置全面展开
  • 五一假期上海推出首批16条“市民健康路线”,这些健康提示请收好