1. 程式人生 > >linux核心中的list詳解

linux核心中的list詳解

 

1 list_entry作用就是通過list_head型指標ptr換算成其宿主結構的起始地址,該宿主結構是type型的,

而ptr在其宿主結構中定義為member成員。定義在核心原始檔include/linux/list.h中,

對比list_entry(ptr,type,member)可知有以下結果:
其中list相當於member成員,struct example_struct相當於type成員,ptr相當於ptr成員。而list{}成員巢狀於example_struct{}裡面。ptr指向example_struct{}中的list成員變數的。在
list_entry()作用下,將ptr指標迴轉指向struct example_struct{}結構體的開始處

#define list_entry(ptr, type, member) /
 container_of(ptr, type, member)

 container_of是個巨集,定義如下:

#define container_of(ptr, type, member) ({   /
        const typeof( ((type *)0)->member ) *__mptr = (ptr); /      // typeof是gcc擴充套件,用於得知資料型別的,這句話作用大

               //概是看ptr是否是結構體裡成員變數member的型別,不是編譯時將報錯,型別檢測的


        (type *)( (char *)__mptr - offsetof(type,member) );})      

offsetof是計算成員member在結構體的type裡面的偏移數,用__mptr的地址減去偏移地址就可以得到type結構體的開始地址

container_of向上面這樣定義展開後可以直接賦值如下面這樣

#include <stdio.h>

int main(int argc,char argv[])
{
 int x = 1,y = 2;
 int m =
 ({
  x++;
  x + y;
 });
 
 fprintf(stdout,"----%d/n",m);
}

這種用法應該是gcc的擴充套件

2

在linux核心中,list是無處不在,好多程式碼都include了list.h。在linux原始碼中,由於list的運用過於頻繁,所以就把 list 單獨定義了,如果有哪個結構要成為列表,就把結構的第一個成員定義成list_head,然後初始化就可以了

    list.h應該是在include/linux/list.h,裡面的程式碼很易懂,大家翻翻吧。當然資料結構要過關先,起碼要知道list是雙向連結串列還有雙向連結串列的結構是如何的。     一部分程式碼: typedef struct list_head {
        struct list_head *next, *prev;         //從定義中可以看出是個雙向佇列
} list_t;

#define LIST_HEAD_INIT(name) { &(name), &(name) }      

#define LIST_HEAD(name) /
        struct list_head name = LIST_HEAD_INIT(name)     //定義一個空的列表

#define INIT_LIST_HEAD(ptr) do { /               //初始化一個已定義的列表
        (ptr)->next = (ptr); (ptr)->prev = (ptr); /
} while (0)
.
.
前三個巨集用來初始化一個next和prev指標皆指向自身的空連結串列,巨集可以用到的地方明顯受限於c語法約束。例如,LIST_HEAD_INIT()用來初始化結構元素,第二個巨集用來初始化靜態變數,第三個用於函式內部。 static inline void __list_add(struct list_head *new,
                              struct list_head *prev,
                              struct list_head *next)
{
        next->prev = new;
        new->next = next;
        new->prev = prev;
        prev->next = new;
}
static inline void list_add(struct list_head *new, struct list_head *head)     //加入一個新節點
{
        __list_add(new, head, head->next);
} 在list.h中還有一個比較有趣的巨集

/**
* list_entry - get the struct for this entry
* @ptr:        the &struct list_head pointer.
* @type:       the type of the struct this is embedded in.
* @member:     the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) /
        ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

從上面我們知道list_entry的作用是返回型別type的資料結構地址,但是具體怎麼實現的呢?看程式碼怪怪的。其實我們只要知道 &((type *)0)->member 的意思就明白了。它是取結構成員偏移量的一種方法,將常數0強制為結構指標,取其成員的地址,結果就是成員的偏移量。
list_entry巨集根據list_head型指標ptr換算成其宿主結構的起始地址,該宿主結構是 type型的,而ptr在其宿主結構中定義為member成員。如下圖:

req-->|type型物件起始地址
|
|... ...
ptr-->|ptr指標所指的member成員地址
|
|... ...

ptr指向圖中所示的位置,通過(unsigned long)(&((type*)0)->member)得到ptr 和req之間的差值,ptr減去這個差值就得到了type型宿主結構的指標req,返回 型別為(type*)。
    瞭解上面那個巨集,你就知道了 list_head 是如何工作的了。首先,我們先定義並初始化一個 list_head,然後宣告一個結構,如下

struct demo {
struct list_head list;
int i;
}
struct demo demo1;
list_add(&demo1->list, list_head)        //把demo1加入列表,list_head僅僅就是兩個指標

這樣的列表是非常靈活的,不同型別的變數都可以加到列表中去。如下圖所示:
_________________________________________________
|  list    |         1          |         2         |          3          |
|_head |____________|___________|____________ |____
                     ||                  ||                    ||
                     ||                  ||                    ||
                 demo1             demo2       struct dev dev1

所以,我們只要知道了,list_head的地址,裡面的東西就好找了。
注意:佇列頭list_head不能被嵌入到結構中。 list_add(n,p):把n指向的元素插入p所指向的特定元素之後 list_add_tail(n,h):把n所指向的元素插到第一個元素的地址h所指定的連結串列尾 list_empty(p):檢查由第一個元素的地址指定的連結串列是否為空 list_for_each(p,h):對第一個元素的地址h指定的連結串列進行掃描 3 上面對list所講的東西,都是適用於核心的(module),在使用者態下好像不可以

相關推薦

linux心中list

  1 list_entry作用就是通過list_head型指標ptr換算成其宿主結構的起始地址,該宿主結構是type型的, 而ptr在其宿主結構中定義為member成員。定義在核心原始檔include/linux/list.h中, 對比list_entry(ptr,type

Linux心中RAID5原始碼之基本架構與資料結構

Linux核心中RAID5的基本架構與資料結構解析 眾所周知,早年的計算機儲存資料現在磁帶上,然後發展到了磁碟,然而僅僅是單個盤,速度和效能都不是很好,然是,要知道人類的聰明才智是連ET都想不到的,前輩們不斷的猜想,實驗來提高計算機的效能,於是磁碟陣列問世

Linux心中的RCU機制 (

RCU的設計思想比較明確,通過新老指標替換的方式來實現免鎖方式的共享保護。但是具體到程式碼的層面,理解起來多少還是會有些困難。在《深入Linux裝置驅動程式核心機制》第4章中,已經非常明確地敘述了RCU背後所遵循的規則,這些規則是從一個比較高的視角來看,因為我覺

Linux心中select,poll,epoll的區別

隨著2.6核心對epoll的完全支援,網路上很多的文章和示例程式碼都提供了這樣一個資訊:使用epoll代替傳統的poll能給網路服務應用帶來效能上的提升。但大多文章裡關於效能提升的原因解釋的較少,這裡我將試分析一下核心(2.6.21.1)程式碼中poll與epo

Linux用戶搶占和內搶占(概念, 實現和觸發時機)--Linux進程的管理與調度(二十)

amp 3.1 not 職責 mon 顯式 default hust ron 1 非搶占式和可搶占式內核 為了簡化問題,我使用嵌入式實時系統uC/OS作為例子 首先要指出的是,uC/OS只有內核態,沒有用戶態,這和Linux不一樣 多任務系統中, 內核負責管理各個任務, 或

linux下cat命令

forever ron localhost root sta testing 幫助 查看 一點 1、cat 顯示文件連接文件內容的工具; cat 是一個文本文件查看和連接工具。查看一個文件的內容,用cat比較簡單,就是cat 後面直接接文件名。 比如: de>[[

Linux:at命令

計時 osi 執行 inux days pan 必須 man 一個 at命令 at命令為單一工作調度命令。at命令非常簡單,但是在指定時間上卻非常強大 語法 at [選項] time at > 執行的命令 ctrl+d 選項 -m :當指定的任務被

linux 之awk命令

數學函數 mat loop 多次 finished 數組結構 save pre 新的 awk是一種程序語言,對文檔資料的處理具有很強的功能。awk名稱是由它三個最初設計者的姓氏的第一個字母而命名的: Alfred V. Aho、Peter J. We i n b e rg

Linux vmstat命令實戰

上下文切換 span 一次 過多 sta log 讀取文件 負載 監控 vmstat命令是最常見的Linux/Unix監控工具,可以展現給定時間間隔的服務器的狀態值,包括服務器的CPU使用率,內存使用,虛擬內存交換情況,IO讀寫情況。這個命令是我查看Linux/Unix最喜

Linux入門及命令

linux入門、date、echo一、用戶登錄 root用戶 ① 一個特殊的管理賬戶; ②又叫超級管理員; ③對系統的損害有無限的能力; ④除非必要,否則不要用root登錄; 普通用戶 ①權限有限; ②造成系統損害

Linux命令之CP

linux命令之cp詳解Linux命令之CP詳解嘿嘿,又一周過去了,大家過的怎麽樣呢,在這一周時間裏,小編可是又學到不少新知識呢。今天呢,小編就和大家分享一下Linux中我們常用的CP的命令,這裏的cp可是copy的簡寫噢。(容我嘚瑟一下) 學過linux的都知道,在我們操作的過程中,我們常常會用到cp這個命

C++ STL list

c++ back main 雙向鏈表 第一個元素 cti ans :link 獲得 一.解釋: list是一種序列式容器。list容器完成的功能實際上和數據結構中的雙向鏈表是極其相似的,list中的數據元素是通過鏈表指針串連成邏輯意義上的線性表,list不僅是一個雙向鏈表,

linux下vim命令

但是 左移 功能 命令 file lips 查找替換 括號匹配 所想 高級一些的編輯器,都會包含宏功能,vim當然不能缺少了,在vim中使用宏是非常方便的::qx 開始記錄宏,並將結果存入寄存器xq 退出記錄模式@x 播放記錄在x寄存器中的宏命令稍微

【轉載】Linux下chkconfig命令

name scrip 再次 http 缺省 重新 禁止 level pool chkconfig命令主要用來更新(啟動或停止)和查詢系統服務的運行級信息。謹記chkconfig不是立即自動禁止或激活一個服務,它只是簡單的改變了符號連接。 使用語法:chkconfig [--

Linux的ll命令

uid ets 版本信息 cal 可執行 文件和目錄 tac ssh 鏈接文件 ll 列出來的結果詳細,有時間,是否可讀寫等信息 ,象windows裏的 詳細信息 ls 只列出文件名或目錄名 就象windows裏的 列表 ll -t 是降序, ll -t | tac

Linux定時任務cron

setenv 這樣的 設置 結果 詳解 ron editor tor 其他 摘要:相信不少linux愛好者們或開發過程中都在使用Linux環境吧。其中crontab就是一個非常強大的定時任務執行器。比如我們可以設置好何時執行任務的腳本,系統會在指定的時間內開始任務

Linux下grub.cnf

編輯 操作系統 通過 系統文件 項目 images 背景 all itl   grub.conf跟系統啟動項有關,對於重置密碼。來說小case。。。 1、介紹 在Red Hat Linux7.2之後,默認的引導加載程序從LTLO變為GRUB.這個引導加載程序使用戶

linux中expect命令

linux運維expect介紹expect 是由Don Libes基於Tcl(Tool Command Language )語言開發的,主要應用於自動化交互式操作的場景,借助Expect處理交互的命令,可以將交互過程如:ssh登錄,ftp登錄等寫在一個腳本上,使之自動化完成。尤其適用於需要對多臺服務器執行相同

Linux——vim編輯器

linux——vim編輯器詳解 vim 十六、使用vim編輯多個文件用法: vim FILE1 FILE2 FILE3文件之間切換:末行模式下: :next 切換至下一個文件 :prev 切換至前一個文件 :last 切換至最後一個文件 :first 切換至第