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. 優化內存管理:頁對齊的內存分配可以簡化內存管理操作,提高內存管理的效率。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/77058.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/77058.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/77058.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用 Azure AKS 保護 Kubernetes 部署的綜合指南

企業不斷尋求增強其軟件開發和部署流程的方法。DevOps 一直是這一轉型的基石,彌合了開發與運營之間的差距。然而,隨著安全威脅日益復雜,將安全性集成到 DevOps 流水線(通常稱為 DevSecOps)已變得勢在必行。本指南深入探討了如何使用 Azure Kubernetes 服務 (AKS) 來利用 D…

2025年常見滲透測試面試題-webshell免殺思路(題目+回答)

網絡安全領域各種資源&#xff0c;學習文檔&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各種好玩的項目及好用的工具&#xff0c;歡迎關注。 目錄 webshell免殺思路 PHP免殺原理 webshell免殺測試&#xff1a; webshell免殺繞過方法&#xff1a; 編…

訪問不到服務器上啟動的llamafactory-cli webui

采用SSH端口轉發有效&#xff0c;在Windows上面進行訪問 在服務器上啟動 llamafactory-cli webui 后&#xff0c;訪問方式需根據服務器類型和網絡環境選擇以下方案&#xff1a; 一、本地服務器&#xff08;物理機/虛擬機&#xff09; 1. 直接訪問 若服務器與操作設備處于同一…

基于 LSTM 的多特征序列預測-SHAP可視化!

往期精彩內容&#xff1a; 單步預測-風速預測模型代碼全家桶-CSDN博客 半天入門&#xff01;鋰電池剩余壽命預測&#xff08;Python&#xff09;-CSDN博客 超強預測模型&#xff1a;二次分解-組合預測-CSDN博客 VMD CEEMDAN 二次分解&#xff0c;BiLSTM-Attention預測模型…

C++ 編程指南35 - 為保持ABI穩定,應避免模板接口

一&#xff1a;概述 模板在 C 中是編譯期展開的&#xff0c;不同模板參數會生成不同的代碼&#xff0c;這使得模板類/函數天然不具備 ABI 穩定性。為了保持ABI穩定&#xff0c;接口不要直接用模板&#xff0c;先用普通類打個底&#xff0c;模板只是“外殼”&#xff0c;這樣 AB…

【iOS】OC高級編程 iOS多線程與內存管理閱讀筆記——自動引用計數(二)

自動引用計數 前言ARC規則所有權修飾符**__strong修飾符**__weak修飾符__unsafe_unretained修飾符__autoreleasing修飾符 規則屬性數組 前言 上一篇我們主要學習了一些引用計數方法的內部實現&#xff0c;現在我們學習ARC規則。 ARC規則 所有權修飾符 OC中&#xff0c;為了處…

可信空間數據要素解決方案

可信空間數據要素解決方案 一、引言 隨著數字經濟的蓬勃發展&#xff0c;數據已成為重要的生產要素。可信空間數據要素解決方案旨在構建一個安全、可靠、高效的數據流通與應用環境&#xff0c;促進數據要素的合理配置和價值釋放&#xff0c;推動各行業的數字化轉型和創新發展…

mysql刪除表后重建表報錯Tablespace exists

版本 mysql:8.0.23 復現步驟 1、刪除表 DROP TABLE IF EXISTS xxx_demo; 2、新建表 CREATE TABLE xxx_demo (id bigint NOT NULL AUTO_INCREMENT COMMENT 主鍵id,creator varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT COMMENT 創建者,c…

【Leetcode-Hot100】缺失的第一個正數

題目 解答 有一處需要注意&#xff0c;我使用注釋部分進行交換值&#xff0c;報錯&#xff1a;超出時間限制。有人知道是為什么嗎&#xff1f;難道是先給nums[i]賦值后&#xff0c;從而改變了后一項的索引&#xff1f; class Solution(object):def firstMissingPositive(sel…

從單模態到多模態:五大模型架構演進與技術介紹

前言 1. ResNet — 殘差神經網絡背景核心問題與解決方案原理模型架構ResNet 系列變體技術創新與影響 2. ViT — Vision Transformer背景核心思想發展歷程Transformer的起源&#xff1a;ViT的出現&#xff1a;ViT的進一步發展&#xff1a; 模型架構技術創新與影響 3. Swin Trans…

JavaScript事件循環

目錄 JavaScript 執行機制與事件循環 一、同步與異步代碼 1. 同步代碼&#xff08;Synchronous Code&#xff09; 2. 異步代碼&#xff08;Asynchronous Code&#xff09; 二、事件循環&#xff08;Event Loop&#xff09; 1. 核心組成 2. 事件循環基本流程 3. 運行機制…

Java Collection(7)——Iterable接口

1.Iterator接口 1.1 Iterator接口和其他集合類的關系 Java集合類中&#xff0c;Iterable接口屬于頂層接口&#xff0c;除Map接口外&#xff0c;其他都實現了Iterable接口&#xff0c;這意味著它們都可以重寫和使用Iterable接口中的方法 1.2 Iterable接口簡介 在JDK1.7以前&a…

若依微服務版啟動小程序后端

目錄標題 本地啟動&#xff0c;dev對應 nacos里的 xxx-xxx-dev配置文件 本地啟動&#xff0c;dev對應 nacos里的 xxx-xxx-dev配置文件

STM32基礎教程——DMA+ADC多通道

目錄 前言 ?編輯 技術實現 連線圖 代碼實現 技術要點 實驗結果 問題記錄 前言 DMA(Direct Memory Access)直接存儲器存取&#xff0c;用來提供在外設和存儲器 之間或者存儲器和存儲器之間的高速數據傳輸。無需CPU干預&#xff0c;數據可以通過DMA快速地移動&#xff0…

23黑馬產品經理Day01

今天過了一遍23黑馬產品經理的基礎視頻 問題思考維度 抓住核心用戶 為什么需要抓住核心用戶&#xff1f; 主要原因&#xff1a;用戶越來越細分&#xff0c;保持市場競爭力&#xff0c;產品開發推廣更聚焦 做產品為什么要了解用戶&#xff1a;了解用戶的付費點&#xff0c;…

C/C++ 通用代碼模板

? C 語言代碼模板&#xff08;main.c&#xff09; 適用于基礎項目、算法競賽或刷題&#xff1a; #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #include <math.h>// 宏定義區 #define MAX_N 1000 #defi…

【數據結構_7】棧和隊列(上)

一、概念 棧和隊列&#xff0c;也是基于順序表和鏈表實現的 棧是一種特殊的線性表&#xff0c;其只允許在固定的一段進行插入和刪除元素操作。 遵循后進先出的原則 此處所見到的棧&#xff0c;本質上就是一個順序表/鏈表&#xff0c;但是&#xff0c;實在順序表/鏈表的基礎…

git UserInterfaceState.xcuserstate 文件頻繁更新

1> 退出 Xcdoe&#xff0c;打開終端&#xff08;Terminal&#xff09;&#xff0c;進入到你的項目目錄下。 2> 在終端鍵入 git rm --cached <YourProjectName>.xcodeproj/project.xcworkspace/xcuserdata/<YourUsername>.xcuserdatad/UserInterfaceState.x…

【Ai】MCP實戰:手寫 client 和 server [Python版本]

什么是mcp MCP 是一個開放協議&#xff0c;它為應用程序向 LLM 提供上下文的方式進行了標準化。你可以將 MCP 想象成 AI 應用程序的 USB-C 接口。就像 USB-C 為設備連接各種外設和配件提供了標準化的方式一樣&#xff0c;MCP 為 AI 模型連接各種數據源和工具提供了標準化的接口…

ESP8266/32作為AVR編程器(ISP programmer)的使用介紹

ESP8266作為AVR編程器( ISP programmer)的使用介紹 &#x1f33f;ESP8266自帶庫例程&#xff1a;https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266AVRISP&#x1f4cd;支持ESP8266/32的ESP_AVRISP其它開源工程&#xff08;個人沒有再去驗證&#xff09;&…