Linux 內核學習(11) --- Linux 鏈表結構

文章目錄

      • Linked List 簡介
      • Linked List 操作方法
        • 鏈表頭結點初始化
        • 創建鏈表節點
        • 添加節點到鏈表中
        • 從鏈表中刪除節點
        • 從鏈表中替換節點
        • 移動鏈表中的節點
        • 檢查鏈表
        • 鏈表遍歷
        • demo 實例

Linked List 簡介

鏈表是一種數據結構,由一系列節點組成,每個節點包含數據部分和指向下一個節點的指針
鏈表可以動態增長或者縮小,適合頻繁的插入和刪除操作,常見的鏈表類型有單向鏈表和雙向鏈表

在 linux 內核開發中,開發者無需自己實現鏈表或者使用第三方庫,內核內置了雙向鏈表實現 struct list_head
定義在 linux/list.h

Linked List 操作方法

鏈表頭結點初始化

Linux 內核鏈表用鏈表頭 list_head 來表示,因為鏈表初始化其實就是初始化一個鏈表頭節點

LIST_HEAD(demo_list)

LIST_HEAD 宏將創建一個名稱為 demo_list 的鏈表,其實是一個鏈表頭節點,在沒有插入如何節點之前,它的首位指針指向自身,也可以認為首尾指針指向自身時鏈表就是空的

#define LIST_HEAD_INIT(name) {&(name), &(name)};#define LIST_HAED(name) \struct list_head name = LIST_HEAD_INIT(name);
struct list_head {struct list_head *next;struct list_head *prev;
}

注意 LIST_HAED 宏可以用于定義個鏈表頭節點,LIST_HEAD_INIT 只能用于初始化鏈表頭結點

創建鏈表節點

Linux 內核鏈表節點也使用 list_head 來表示,通常內嵌在自定義數據結構中:

struct my_node {struct list_head list;int data;
};struct my_node node

鏈表節點在插入鏈表之前也需要初始化,使用 INIT_LIST_HEAD

添加節點到鏈表中

鏈表有節點初始化后,就可以往鏈表中添加節點:

static inline void list_add(struct list_head *new, struct list_head *head)
static inline void list_add_tail(struct list_head *new, struct list_head *head)

其中的 head 表示鏈表頭,new 表示要添加的節點,

list_add 將 new 添加到鏈表頭部
list_add_tail 將 new 添加到鏈表尾部

從鏈表中刪除節點

從鏈表中刪除節點實際上就是修改下一節點及其相鄰節點的 prev 和 next 指針指向,并不會釋放節點的內存
其中的 list_del_init 刪除節點之后還會對該節點重新進行初始化操作

static inline void list_del(struct list_head *entry)
static inline void list_del_init(struct list_head *entry)
從鏈表中替換節點

和刪除節點同理,替換節點也只是修改了 prev 和 next 的指針指向,并且 list_replace_init 還會對替換出來的節點
進行重新的初始化操作

static inline void list_replace(struct list_head *old, struct list_head *new)
static inline void list_replace_init(struct list_head *old, struct list_head *new)
移動鏈表中的節點

下面的函數中,list 表示要移動的鏈表,list_move 表示將其移動到鏈表首部,
list_move_tail 將其移動到鏈表尾部

static inline void list_move(struct list_head *list, struct list_head *head)
static inline void list_move_tail(struct list_head *list,struct list_head *head)
檢查鏈表

Linux 內核還提供了檢查鏈表的相關函數,例如:

  • list_is_last: 檢查節點是否是鏈表最后一個節點
  • list_empty: 鏈表是否為空
  • list_is_singular: 鏈表是否只有一個節點
static inline int list_is_last(const struct list_head *list,const struct list_head *head)
static inline int list_empty(const struct list_head *head)
static inline int list_is_singular(const struct list_head *head)
鏈表遍歷

Linux 提供了一系列函數來遍歷和操作鏈表中的元素:

  • list_entry(ptr, type, member): 通過鏈表節點的地址獲取包含該節點的結構體指針
  • list_for_each(pos, head) : 遍歷鏈表的每個節點
  • list_for_each_entry(pos, head, member) : 遍歷鏈表中的每個結構體實例
  • list_for_each_entry_safe(pos, n, head, member) : 安全的遍歷鏈表中每個結構體實例,可以在遍歷過程中安全刪除節點
  • list_for_each_prev(pos, head): 逆向遍歷鏈表中的每個節點
  • list_for_each_entry_reverse(pos, head, member): 逆向遍歷鏈表中每個結構體實例
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)#define list_for_each(pos, head) \for (pos = (head)->next; pos != (head); pos = pos->next)#define list_for_each_entry(pos, head, member)				\for (pos = list_first_entry(head, typeof(*pos), member);	\&pos->member != (head);					\pos = list_next_entry(pos, member))#define list_for_each_entry_safe(pos, n, head, member)			\for (pos = list_first_entry(head, typeof(*pos), member),	\n = list_next_entry(pos, member);			\&pos->member != (head); 					\pos = n, n = list_next_entry(n, member))#define list_for_each_prev(pos, head) \for (pos = (head)->prev; pos != (head); pos = pos->prev)#define list_for_each_entry_reverse(pos, head, member)			\for (pos = list_last_entry(head, typeof(*pos), member);		\&pos->member != (head); 					\pos = list_prev_entry(pos, member))

linux linked_list

demo 實例
//static LIST_HEAD(demo_list);
static struct list_head demo_list;static struct demo_linklistnode {char buffer[LINKEDLIST_BUFFER_SIZE];struct list_head list;
};static ssize_t mdrv_read(struct file *file, char __user *buf, size_t size, loff_t *offset) {size_t to_read = min(size, BUFFER_SIZE - (size_t)*offset);if(to_read == 0) {return 0;}printk(KERN_DEBUG "%s: to_read size:%zu .\n", __func__, to_read);if(copy_to_user(buf, device_buffer + *offset, to_read)) {return -EFAULT;}struct demo_linklistnode* tempnode, *nextnode;int i = 0;list_for_each_entry_safe(tempnode, nextnode, &demo_list, list) {printk(KERN_DEBUG "Node[%d] buffer:%s", i, tempnode->buffer);if(list_is_first(&tempnode->list, &demo_list)) {printk(KERN_DEBUG "==> fist node");} else if(list_is_last(&tempnode->list, &demo_list)) {printk(KERN_DEBUG "==> last node");}printk(KERN_DEBUG "\n");i++;}*offset += to_read;return to_read;
}static ssize_t mdrv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) {size_t to_write = min(size, BUFFER_SIZE - (size_t)*offset);if(to_write == 0) {return -ENOMEM;}printk(KERN_DEBUG "%s: to_write size %zu .\n", __func__, to_write);if(copy_from_user(device_buffer + *offset, buf, to_write)) {return -EFAULT;}struct demo_linklistnode *node = NULL;node = kmalloc(sizeof(struct demo_linklistnode), GFP_KERNEL);if(!node) {printk(KERN_DEBUG "fail to allocate demo_linklistnode buffer\n");return -ENOMEM;}memset(node->buffer, 0, LINKEDLIST_BUFFER_SIZE);memcpy(node->buffer, device_buffer + *offset, to_write);INIT_LIST_HEAD(&node->list);//list_add(&node->list, &demo_list);list_add_tail(&node->list, &demo_list);*offset += to_write;return to_write;
}

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

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

相關文章

一分鐘部署nginx-公網IP訪問內網

前言 服務器內網下有nacos cluster(3個節點),開放到公網并指定公司網絡訪問需要配置三次IP白名單,因此需要簡化流程,通過nginx反向代理只配置1次IP白名單。 現在通過docker容器模擬環境,準備1臺云服務器。…

C 語言分支與循環

目錄 一. 分支結構:if 語句與 switch 語句 1. if 語句 2. switch 語句 二、關系操作符、條件操作符與邏輯操作符 1. 關系操作符 2. 條件操作符 3. 邏輯操作符 三、循環結構:while 循環、for 循環與 do - while 循環 1. while 循環 2. for 循…

【一文看懂Spring Boot2.x升級Spring Boot3.x】springboot2.x升級springboot3.x

springboot2.x升級springboot3.x 背景升級jdk版本為17以上springboot版本修改javax包更新mybatis-plus升級swagger升級springdocspringdoc配置背景 當前項目是springboot2.5.9版本的springboot+mybatis-plus項目,需要升級到springboot3.5.0項目。 升級jdk版本為17以上 Spri…

陽臺光伏防逆流電表革新者:安科瑞ADL200N-CT/D16-WF

——為家庭能源管理提供高精度、智能化解決方案 一、陽臺光伏爆發的背景 在全球能源轉型與碳中和目標的驅動下,陽臺光伏正以革命性姿態重塑家庭能源消費模式。從歐洲的“微型發電站”到中國的“萬億藍海”,這一創新技術不僅撬動了能源市場的結構性變革…

美團完整面經

面試崗位 面試的崗位 - 2025春季校招 【轉正實習】軟件服務工程師-后端方向(成都 - 軟硬件服務-SaaS事業部) 一面(業務初試 - 30min) 問題 自我介紹 Java基礎 HashMap底層用的數據結構是什么?是線程安全的嗎&…

pysnmp 操作流程和模塊交互關系的可視化總結

1. SNMP GET 操作序列圖 #mermaid-svg-KALvv8WkHJTsNCeu {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-KALvv8WkHJTsNCeu .error-icon{fill:#552222;}#mermaid-svg-KALvv8WkHJTsNCeu .error-text{fill:#552222;str…

關于 /proc/net/tcp 與 /proc/$pid/net/tcp 的關系分析

關于 /proc/net/tcp 與 /proc/$pid/net/tcp 的關系分析 1. 基礎概念 在 Linux 系統中,每個進程必定歸屬于一個且僅一個網絡命名空間(Network Namespace)。這是 Linux 命名空間隔離機制的核心特性之一。 /proc/net/tcp 顯示當前網絡命名空間…

微信小程序 - 保存手機號等信息到通訊錄

主要使用小程序 wx.addPhoneContact 這個api 一、界面 <view class"tab-item" bindtap"addToPhoneContacts">保存</view> 二、js 邏輯文件中 addToPhoneContacts() {wx.addPhoneContact({firstName: this.data.firstName, // 姓名mobilePh…

計算機視覺一些定義解析

1.GCT&#xff08;Gated Channel Transformation&#xff09; 定義 GCT&#xff08;Gated Channel Transformation&#xff09;是一種用于增強卷積神經網絡特征提取能力的模塊。它的核心思想是通過門控機制對特征圖的通道進行動態調整&#xff0c;從而突出對任務更有幫助的特…

美團NoCode的Database 使用指南

系列文章目錄 第一篇&#xff1a;美團NoCode設計網站的嘗試經驗分 第二篇&#xff1a;美團NoCode中的Dev Mode 使用指南 文章目錄 系列文章目錄Database 適用場景一、什么是 Database&#xff1f;二、準備流程1. 申請賬號 三、使用流程1.申請資源的同時可搭建 NoCode 頁面&…

MVC 數據庫

MVC 數據庫 引言 在軟件開發領域,Model-View-Controller(MVC)是一種流行的軟件架構模式,它將應用程序分為三個核心組件:模型(Model)、視圖(View)和控制器(Controller)。這種模式有助于提高代碼的可維護性和可擴展性。本文將深入探討MVC架構與數據庫之間的關系,以…

1.11 HTTP 文件上傳的核心協議

HTTP 文件上傳是 Web 開發中的常見需求&#xff0c;涉及到特殊的請求格式和處理機制。 一、HTTP 文件上傳的核心協議 1. 兩種主要方式 multipart/form-data&#xff08;主流&#xff09; 支持二進制文件和表單字段混合傳輸&#xff0c;由 Content-Type 頭部標識。applicatio…

安裝 Poppler(Windows)

下載 Poppler&#xff08;Windows&#xff09;&#xff1a;https://github.com/oschwartz10612/poppler-windows/releases/ 解壓在自己目錄下 配置系統環境變量&#xff1a;把 poppler-xx.x.x\bin 目錄加入你的環境變量 PATH 中。 檢查是否配置成功 pdfinfo

Java學習筆記之:初識nginx

Java學習筆記之&#xff1a;初識nginx PS&#xff1a;雖然總結的都很簡單&#xff0c;但是作為初學者并且本人記憶力較差所以每次學習新知識點后習慣性記錄下來&#xff0c;這樣加深一遍記憶并且便于日后復習。 介紹&#xff1a; Nginx是一款輕量級的Web服務器/反向代理服務器…

Middleware

中間件的定義&#xff1a;中間件是位于操作系統和應用程序之間的軟件層&#xff0c;用于解決分布式系統中通信、數據共享、資源管理等共性問題。消息隊列屬于通信中間件&#xff0c;用于在分布式系統中傳遞消息&#xff0c;實現應用解耦、異步通信和流量削峰。解耦系統&#xf…

Mac如何配置ZSH并使用Oh-my-zsh?讓你的終端更加實用、美觀

前言 現在&#xff0c;越來越多的人趨向使用ZSH取代(Linux)原本的Bash作為自己的終端Shell。的確&#xff0c;ZSH才是適用于現代的Shell&#xff1a; 更豐富的命令提示更鮮明的演示標記更強大的插件支持 什么是ZSH 回答什么是ZSH前&#xff0c;我們先解釋什么是Bash&#x…

C++11新標準

重點 auto 類型推導范圍 for 迭代初始化列表變參模板 新類型 C11新增了類型 long long 和 unsigned long long&#xff0c;以支持64位(或更寬)的整型;新增了類型 char16_t和 char32_t&#xff0c;以支持 16位和 32 位的字符表示;還新增了“原始”字符串。 常量 nullptr nu…

SpringAI Prompt提示詞

基本概念 Prompts提示詞 ? 提示詞的是引導AI模型輸出的輸入&#xff0c;提示詞的正確性直接影響模型輸出的。 Message消息 Message 接口封裝了 Prompt 文本內容、一組元數據屬性以及稱為 MessageType 的分類。Spring AI消息API&#xff1a; 其中最重要的就是角色&#xff1a; …

力扣刷題——二分查找

數組是存放在連續內存空間上的相同類型數據的集合數組下標都是從0開始的數組內存空間的地址是連續的正是因為數組在內存空間的地址是連續的&#xff0c;所以我們在刪除或者增添元素的時候&#xff0c;就難免要移動其他元素的地址。 使用二分查找法返回的元素下標可能不是唯一的…

黑群暉NAS部署DeepSeek模型與內網穿透實現本地AI服務

文章目錄 前言1.安裝Container Manager2. 啟動ssh功能3. ssh連接黑群暉4. 安裝Ollama5. 安裝deepseek模型6. 安裝open-webui圖形界面7. 安裝內網穿透7.1 下載cpolar套件7.2 配置群輝虛擬機7.3 配置公網地址小結 7.4 配置固定公網地址 總結 前言 在追求自建網絡存儲方案的極客群…