linux kernel中的變長引數巨集

2021-08-15 06:35:41 字數 2218 閱讀 1349

1 函式宣告

首先,要實現類似printf()的變參函式,函式的最後乙個引數要用 ... 表示,如

int log(char * arg1, ...)

這樣編譯器才能知道這個函式是變參函式。這個引數與變參函式的內部實現完全沒有關係,只是讓編譯器在編譯呼叫此類函式的語句時不計較引數多少老老實實地把全部引數壓棧而不報錯,當然...之前至少要有乙個普通的引數,這是由實現手段限制的。

2 函式實現

c語言通過幾個巨集實現變參的定址。下面是linux2.18核心原始碼裡這幾個巨集的定義,相信符合c89,c99標準的c語言基本都是這樣定義的。

typedef char *va_list;

/*storage alignment properties -- 堆疊按機器字對齊

*/#define _aupbnd (sizeof (acpi_native_uint) - 1)

#define _adnbnd (sizeof (acpi_native_uint) - 1)

/*variable argument list macro definitions -- 變參函式內部實現需要用到的巨集

*/#define _bnd(x, bnd) (((sizeof (x)) + (bnd)) & (~(bnd)))

#define va_arg(ap, t) (*(t *)(((ap) += (_bnd (t, _aupbnd))) - (_bnd (t,_adnbnd))))

#define va_end(ap) (void) 0

#define va_start(ap, a) (void) ((ap) = (((char *) &(a)) + (_bnd (a,_aupbnd))))

下面以x86 32位機為例分析這幾個巨集的用途

要理解這幾個巨集需要對c語言如何傳遞引數有一定了解。與pascal相反,與stdcall 相同,c語言傳遞引數時是用push指令從右到左將引數逐個壓棧,因此c語言裡通過棧指標來訪問引數。雖然x86的push一次可以壓2,4或8個位元組入棧,c語言在壓引數入棧時仍然是機器字的size為最小單位的,也就是說引數的位址都是字對齊的,這就是_bnd(x,bnd)存在的原因。另外補充一點常識,不管是彙編還是c,編譯出的x86函式一般在進入函式體後立即執行

push ebp

mov ebp, esp

這兩條指令。首先把ebp入棧,然後將當前棧指標賦給ebp,以後訪問棧裡的引數都使用ebp作為基指標。

一一解釋這幾個巨集的作用。

_bnd(x,bnd) ,計算型別為x的引數在棧中佔據的位元組數,當然是字對齊後的位元組數了。acpi_native_unit是乙個機器字,32位機的定義是:typedef u32 acpi_native_uint;

顯然,_aupbnd ,_adnbnd 的值是 4-1 == 3 == 0x00000003 ,按位取反( ~(bnd))就是0xfffffffc 。

因此,_bnd(x,bnd) 巨集在32位機下就是

( (sizeof(x) + 3)&0xfffffffc )

很明顯,其作用是--倘若sizeof(x)不是4的整數倍,去餘加4。

_bnd(sizeof(char),3) == 4

_bnd(sizeof(struct size7struct),3) == 8

va_start(ap,a) ,初始化引數指標ap,將函式引數a右邊第乙個引數的位址賦給ap。 a必須是乙個引數的指標,所以此種型別函式至少要有乙個普通的引數啊。像下面的例子函式,就是將第二個引數的指標賦給ap。

va_arg(ap,t) ,獲得ap指向引數的值,並使ap指向下乙個引數,t用來指明當前引數型別。

注意((ap) += (_bnd (t, _aupbnd))) 是被一對括號括起來的,然後才減去(_bnd (t, _adnbnd),

而_aupbnd和_adnbnd是相等的。所以取得的值是ap當前指向的引數值,但是先給ap加了當前引數在字對齊後所佔的位元組數,使其指向了下乙個引數。

va_end(ap), 作用是美觀。

3 總結

先用乙個 ... 引數宣告函式是變參函式,接下來在函式內部以va_start(ap,a)巨集初始化引數指標,然後就可以用va_arg(ap,型別)從左到右逐個獲取引數值了

分析到此處算是一清二白了,下面給乙個例子

[c-sharp] 

view plain

copy

print

?int log(char * fmt,...)  

va_end(ap);  

}

linux kernel中timer的使用

在kernel中如果想週期性的幹些什麼事情,或者某個特定時間幹些什麼事情,可以使用timer。例如像周期性地dump某段buffer的資料等等。先來看看使用方法。先定義乙個struct timer list的物件。eg struct timer list dump t 這個物件相當於乙個鬧鐘,其中包...

Linux kernel中module相關命令集

linux的kernel能夠以動態的方式載入,解除安裝模組,以達到減小核心的大小,複雜度,以及增加核心的靈活性。目前,我知道的有lsmod,insmod,rmmod,modprobe四條命令,現在分別總結記錄這四條命令,命令後的模組名稱均不加字尾 如.ko或.o 1 lsmod 即list modu...

Linux Kernel啟動中引數的處理

1.在bootloader中會以taglist的形式儲存板子mem cmdline cmdline中也可以定義mem資訊 等相關資訊,cmdline通過getenv從環境變數取得 該環境變數是之前使用setenv以字串形式配置 addr和size。跳轉到kernel前,r0 0,r1 proc ty...