linux內核中一些常用的數(shù)據(jù)結構和操作-基礎電子_第1頁
linux內核中一些常用的數(shù)據(jù)結構和操作-基礎電子_第2頁
linux內核中一些常用的數(shù)據(jù)結構和操作-基礎電子_第3頁
linux內核中一些常用的數(shù)據(jù)結構和操作-基礎電子_第4頁
linux內核中一些常用的數(shù)據(jù)結構和操作-基礎電子_第5頁
已閱讀5頁,還剩6頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

精品文檔-下載后可編輯linux內核中一些常用的數(shù)據(jù)結構和操作-基礎電子1.前言本文介紹linux內核中一些常用的數(shù)據(jù)結構和操作。2.雙向鏈表(list)linux內核中的雙向鏈表通過結構structlist_head來將各個節(jié)點連接起來,此結構會作為鏈表元素結構中的一個參數(shù):structlist_head{

structlist_head*next,*prev;

};鏈表頭的初始化,注意,結構中的指針為NULL并不是初始化,而是指向自身才是初始化,如果只是按普通情況下的置為NULL,而不是指向自身,系統(tǒng)會崩潰,這是一個容易犯的錯誤:#defineLIST_HEAD_INIT(name){(name),(name)}#defineLIST_HEAD(name)\

structlist_headname=LIST_HEAD_INIT(name)#defineINIT_LIST_HEAD(ptr)do{\

(ptr)-next=(ptr);(ptr)-prev=(ptr);\

}while(0)常用的鏈表操作:插入到鏈表頭:

voidlist_add(structlist_head*new,structlist_head*head);插入到鏈表尾:

voidlist_add_tail(structlist_head*new,structlist_head*head);刪除鏈表節(jié)點:

voidlist_del(structlist_head*entry);將節(jié)點移動到另一鏈表:

voidlist_move(structlist_head*list,structlist_head*head);將節(jié)點移動到鏈表尾:

voidlist_move_tail(structlist_head*list,structlist_head*head);判斷鏈表是否為空,返回1為空,0非空

intlist_empty(structlist_head*head);把兩個鏈表拼接起來:

voidlist_splice(structlist_head*list,structlist_head*head);取得節(jié)點指針:

#definelist_entry(ptr,type,member)\

((type*)((char*)(ptr)-(unsignedlong)(((type*)0)-member)))遍歷鏈表中每個節(jié)點:

#definelist_for_each(pos,head)\

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

pos=pos-next,prefetch(pos-next))逆向循環(huán)鏈表中每個節(jié)點:

#definelist_for_each_prev(pos,head)\

for(pos=(head)-prev,prefetch(pos-prev);pos!=(head);\

pos=pos-prev,prefetch(pos-prev))舉例:LISH_HEAD(mylist);structmy_list{

structlist_headlist;

intdata;

};staticintini_list(void)

{

structmy_list*p;

inti;

for(i=0;i100;i++){

p=kmalloc(sizeof(structmy_list),GFP_KERNEL);

list_add(p-list,mylist);

}

}

在內存中形成如下結構的一個雙向鏈表:++

||

|mylist99980|

|++++++++|

+-|next||list.next||list.next|...|list.next|+

||||||||

+--|prev||list.prev||list.prev|...|list.prev|--+

|++|||||||

||data||data||data||

|++++++|

||

++知道了鏈表頭就能遍歷整個鏈表,如果是用list_add()插入新節(jié)點的話,從鏈表頭的next方向看是一個堆棧型。從鏈表中刪除節(jié)點很容易:staticvoiddel_item(structmy_list*p)

{

list_del(p-list,mylist);

kfree(p);

}重要的宏是list_entry,這個宏的思路是根據(jù)鏈表元素結構中鏈表頭結構list_head的地址推算出鏈表元素結構的實際地址:#definelist_entry(ptr,type,member)\

((type*)((char*)(ptr)-(unsignedlong)(((type*)0)-member)))ptr是鏈表元素結構(如structmy_list)中鏈表頭結構list_head的地址

member是鏈表元素結構(如structmy_list)中鏈表頭結構list_head參數(shù)的名稱

type是鏈表元素結構類型(如structmy_list)計算原理是根據(jù)鏈表頭結構list_head的地址減去其在鏈表元素結構中的偏移位置而得到鏈表元素結構的地址。例如:staticvoidprint_list(void)

{

structlist_head*cur;

structmy_list*p;list_for_each(cur,mylist){

p=list_entry(cur,structmy_list,list);

printk("data=%d\n",p-data);

}

}優(yōu)點:這樣就可以用相同的數(shù)據(jù)處理方式來描述所有雙向鏈表,不用再單獨為各個鏈表編寫各種編輯函數(shù)。缺點:

1)鏈表頭中元素置為NULL不是初始化,與普通習慣不同;

2)仍然需要單獨編寫各自的刪除整個鏈表的函數(shù),不能統(tǒng)一處理,因為不能保證所有鏈表元素結構中鏈表頭結構list_head的偏移地址都是相同的,當然如果把鏈表頭結構list_head都作為鏈表元素結構的個參數(shù),就可以用統(tǒng)一的刪除整個鏈表的函數(shù)。

3.HASH表HASH表適用于不需要對整個空間元素進行排序,而是只需要能快速找到某個元素的場合,是一種以空間換時間的方法,本質也是線性表,但由一個大的線性表拆分為了多個小線性表,由于只需要查找小表,因此搜索速度就會線性查整個大表提高很多,理想情況下,有多少個小線性表,搜索速度就提高了多少倍,通常把小線性表的表頭綜合為一個數(shù)組,大小就是HASH表的數(shù)量。HASH表速度的關鍵是HASH函數(shù)的設計,HASH函數(shù)根據(jù)每個元素中固定的參數(shù)進行計算,算出一個不大于HASH表數(shù)量的索引值,表示該元素需要放在該索引號對應的那個表中,對于固定的參數(shù),計算結果始終是固定的,但對于不同的參數(shù)值,希望計算出來的結果能盡可能地平均到每個索引值,HASH函數(shù)計算得越平均,表示每個小表中元素的數(shù)量都會差不多,這樣搜索性能將越好。HASH函數(shù)也要盡可能的簡單,以減少計算時間,常用的算法是將參數(shù)累加求模,在include/linux/jhash.h中已經定義了一些HASH計算函數(shù),可直接使用。HASH表在路由cache表,狀態(tài)連接表等處用得很多。舉例,連接跟蹤中根據(jù)tuple值計算HASH://net/ipv4/netfilter/ip_conntrack_core.cu_int32_t

hash_conntrack(conststructip_conntrack_tuple*tuple)

{

#if0

dump_tuple(tuple);

#endif

return(jhash_3words(tuple-src.ip,

(tuple-dst.ip^tonum),

(tuple-src.u.all|(tuple-dst.u.all16)),

ip_conntrack_hash_rnd)%ip_conntrack_htable_size);

}//include/linux/jhash.h

staticinlineu32jhash_3words(u32a,u32b,u32c,u32initval)

{

a+=JHASH_GOLDEN_RATIO;

b+=JHASH_GOLDEN_RATIO;

c+=initval;__jhash_mix(a,b,c);returnc;

}4.定時器(timer)linux內核定時器由以下結構描述:/*include/linux/timer.h*/

structtimer_list{

structlist_headlist;

unsignedlongexpires;

unsignedlongdata;

void(*function)(unsignedlong);

};list:timer鏈表

expires:到期時間

function:到期函數(shù),時間到期時調用的函數(shù)

data:傳給到期函數(shù)的數(shù)據(jù),實際應用中通常是一個指針轉化而來,該指針指向一個結構

timer的操作:增加timer,將timer掛接到系統(tǒng)的timer鏈表:

externvoidadd_timer(structtimer_list*timer);刪除timer,將timer從系統(tǒng)timer鏈表中拆除:

externintdel_timer(structtimer_list*timer);

(del_timer()函數(shù)可能會失敗,這是因為該timer本來已經不在系統(tǒng)timer鏈表中了,也就是已經刪除過了)對于SMP系統(tǒng),刪除timer使用下面的函數(shù)來防止沖突:

externintdel_timer_sync(structtimer_list*timer);修改timer,修改timer的到期時間:

intmod_timer(structtimer_list*timer,unsignedlongexpires);通常用法:

structtimer_list通常作為數(shù)據(jù)結構中的一個參數(shù),在初始化結構的時候初始化timer,表示到期時要進行的操作,實現(xiàn)定時動作,通常更多的是作為超時處理的,timer函數(shù)作為超時時的資源釋放函數(shù)。注意:如果超時了運行超時函數(shù),此時系統(tǒng)是處在時鐘中斷的bottomhalf里的,不能進行很復雜的操作,如果要完成一些復雜操作,如到期后的數(shù)據(jù)發(fā)送,不能直接在到期函數(shù)中處理,而是應該在到期函數(shù)中發(fā)個信號給特定內核線程轉到tophalf進行處理。為判斷時間的先后,內核中定義了以下宏來判斷:#definetime_after(a,b)((long)(b)-(long)(a)0)

#definetime_before(a,b)time_after(b,a)#definetime_after_eq(a,b)((long)(a)-(long)(b)=0)

#definetime_before_eq(a,b)time_after_eq(b,a)這里用到了一個技巧,由于linux中的時間是無符號數(shù),這里先將其轉換為有符號數(shù)后再判斷,就能解決時間回繞問題,當然只是回繞,回繞兩次當然是判斷不出來的,具體可自己實驗體會。5.內核線程(kernel_thread)內核中新線程的建立可以用kernel_thread函數(shù)實現(xiàn),該函數(shù)在kernel/fork.c中定義:longkernel_thread(int(*fn)(void*),void*arg,unsignedlongflags)fn:內核線程主函數(shù);

arg:線程主函數(shù)的參數(shù);

flags:建立線程的標志;內核線程函數(shù)通常都調用daemonize()進行后臺化作為一個獨立的線程運行,然后設置線程的一些參數(shù),如名稱,信號處理等,這也不是必須的,然后就進入一個死循環(huán),這是線程的主體部分,這個循環(huán)不能一直在運行,否則系統(tǒng)就死在這了,或者是某種事件驅動的,在事件到來前是睡眠的,事件到來后喚醒進行操作,操作完后繼續(xù)睡眠;或者是定時睡眠,醒后操作完再睡眠;或者加入等待隊列通過schedule()調度獲得執(zhí)行時間??傊遣荒芤恢闭贾鳦PU。以下是內核線程的一個實例,取自kernel/context.c:intstart_context_thread(void)

{

staticstructcompletionstartup__initdata=COMPLETION_INITIALIZER(startup);kernel_thread(context_thread,startup,CLONE_FS|CLONE_FILES);

wait_for_completion(startup);

return0;

}staticintcontext_thread(void*startup)

{

structtask_struct*curtask=current;

DECLARE_WAITQUEUE(wait,curtask);

structk_sigactionsa;daemonize();

strcpy(curtask-comm,"keventd");

keventd_running=1;

keventd_task=curtask;spin_lock_irq(curtask-sigmask_lock);

siginitsetinv(curtask-blocked,sigmask(SIGCHLD));

recalc_sigpending(curtask);

spin_unlock_irq(curtask-sigmask_lock);complete((structcompletion*)startup);/*InstallahandlersoSIGCLDisdelivered*/

sa.sa.sa_handler=SIG_IGN;

sa.sa.sa_flags=0;

siginitset(sa.sa.sa_mask,sigmask(SIGCHLD));

do_sigaction(SIGCHLD,sa,(structk_sigaction*)0);/*

*Ifoneofthefunctionsonataskqueuere-addsitself

*tothetaskqueuewecallschedule()instateTASK_RUNNING

*/

for(;;){

set_task_state(curtask,T

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論