編寫乙個遍歷程序的核心模組

2021-08-29 07:46:03 字數 3917 閱讀 3396

本節主要編寫乙個核心模組,實現程序pid和名稱的列印。這裡,我們需要了解linux核心中,鍊錶的實現、pcb(程序控制塊)的定義。程式設計不是目的,目的是更加了解核心中相關**的實現。至於如何編寫和執行乙個模組,請參考:在ubuntu 18.04環境下編寫乙個簡單的核心模組

# include

# include

# include

# include

# include

static

intprint_pid

(void

)printk

("the number of process is: %d\n"

, count)

;return0;

}static

int __init lkp_init

(void

)static

void __exit lkp_cleanup

(void

)module_init

(lkp_init)

;module_exit

(lkp_cleanup)

;module_license

("gpl"

);

print_pid() 用來列印程序的pid和程序名,有同學可能對上一段**不太了解,所以在這裡,先丟擲**的作用就在於,可以根據不懂的地方,查漏補缺。

我們知道,以雙向鍊錶作為基本的資料結構,可以演化出其他複雜的資料結構,如棧、佇列等。linux核心中,也是如此,當然,隨著核心不斷的優化,如今的核心版本,在設計鍊錶時,處處體現其精妙絕倫的設計思想。

struct list_head

這是核心中煉表的一段原始碼,整個鍊錶的位置在ubuntu中如下所示:

對於核心中的鍊錶,我們不對整個list.h進行分析,只是大體介紹一下linux鍊錶設計的思想。

在核心中,上述結構體是乙個不含資料域的鍊錶,可以嵌入任何結構中,實現各種鍊錶或者其他的資料結構。比如,下面的**就嵌入了之前的核心鍊錶,從而實現乙個雙向鍊錶:

struct test_list

在乙個結構體中巢狀另外乙個結構體,可以把它想象成遞迴。對於list.h的詳細分析,會在以後給與詳細的解釋。

什麼是pcb?

作業系統為了對程序進行管理,勢必需要對程序生命週期中的每乙個階段進行詳細描述,linux核心中就用乙個統一的資料結構來描述程序的狀態,這樣的資料結構我們稱之為程序控制塊pcb(process control block)

核心中的pcb是乙個相當龐大的結構體,主要包含狀態資訊、鏈結資訊、各種識別符號、程序間通訊、時間和定時器資訊、排程資訊、檔案系統資訊、虛擬記憶體資訊和處理器環境等。其在核心中的位置:

關於詳細的pcb分析,日後再進行介紹。

為了對給定型別的程序進行有效搜尋,核心建立了幾個程序鍊錶。每個鍊錶由指向程序pcb的指標組成。在sched.h檔案的task_struct結構體中,有如下**,實現程序鍊錶。

struct task_struct

其實就是通過雙向迴圈鍊錶把所有程序聯絡起來1

鍊錶頭尾都是init_task。init_task是0號程序的pcb,0號程序永遠不會被撤銷,它的pcb被靜態分配到核心資料段中。換句話說,init_task的pcb是預先由編譯器分配,在執行過程中保持不變。對於其他pcb,在執行中,根據系統狀態隨機分配,決定是否分配和撤銷。

至此,我們已經講解了必要的基礎知識,回過頭來,再分析遍歷程序的原始碼,其實就很簡單了。

static

intprint_pid

(void

)printk

("the number of process is: %d\n"

, count)

;return0;

}

list_for_each(pos, &task->tasks)

@pos 是指向struct list_head型別的指標,故它可以指向程序描述符中的children(tasks)或者sibling

@task 指當前程序,tasks指其自程序

linux系統中的每個程序都有乙個父程序(init程序除外);每個程序還有0個或多個子程序。在程序描述符中parent指標指向其父程序,還有乙個名為children的子程序鍊錶(父程序task_struct中的children相當於鍊錶的表頭)。

而我們可以使用list_for_each(/include/linux/list.h)來依次遍歷訪問子程序:

struct task_struct *task;

struct list_head *list;

list_for_each

(list,

¤t->children)

其中task即為某個子程序的位址

我們檢視原始檔找到list_for_each的定義:

#define list_for_each(pos, head) \

for (pos = (head)->next; prefetch(pos->next), pos != (head); pos = pos->next)

從上可以看出list_for_each其實就是乙個for迴圈,for()實現的就是乙個children鍊錶的遍歷,而由children的位址如何取到task_struct的位址呢,它是由list_entry巨集來實現的。知道父程序children的位址,如何得到子程序的task_struct位址,這是目的我們先給出所需函式或巨集的源**

//list_entry(/include/linux/list.h)

//task = list_entry(list, struct task_struct, sibling); 加入做對比

#define list_entry(ptr, type, member) \

container_of(ptr, type, member)

//------------------------------------

//container_of(include/linux/kernel.h)

#define container_of(ptr, type, member) (

)//------------------------------------

//offsetof(/include/linux/stddef.h)

#define offsetof(type, member) ((size_t) &((type *)0)->member)

((size_t)&(

(type *)0

)->member)

對於list_entry巨集來說ptr在這裡為指向children鍊錶的指標,type為task_struct結構體的型別,member為鍊錶成員的變數名,即children。

container_of()思路為先求出結構體成員member(即children)在結構體(即task_struct)中的偏移量,然後再根據member的位址(即ptr)來求出結構體(即task_struct)的位址。

((type *)0)->member,他將位址0強制轉換為type型別的指標,然後再指向成員member,此時((type *)0)->member的位址即為member成員相對於結構體的位移。其中typeof()相當於c的sizeof(),(char *)__mptr這個強制轉換用來計算偏移位元組量,size_t被定義為unsigned int 型別。

《linux作業系統原理與應用》. ↩︎

遍歷某乙個程序的模組

include stdafx.h include include include bool getmodulelist dword dwpid 1.建立乙個模組相關的快照控制代碼 hmodulesnap createtoolhelp32snapshot th32cs snapmodule,指定快照的...

編譯乙個核心模組

1,編寫hello.c include include module license gpl static int init hello init void static void exit hello exit void module init hello init module exit hel...

開始寫乙個核心模組

從hello world開始,乙個完整的核心模組helloword.c如下 include module init 和 module exit 的標頭檔案 include 這個標頭檔案包含了許多符號與函式的定義,這些符號與函式多與載入模組有關 module license dual bsd gpl ...