內(nèi)核模塊化編程講解_第1頁
內(nèi)核模塊化編程講解_第2頁
內(nèi)核模塊化編程講解_第3頁
內(nèi)核模塊化編程講解_第4頁
內(nèi)核模塊化編程講解_第5頁
已閱讀5頁,還剩4頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

一、實驗?zāi)康膶W(xué)習(xí)內(nèi)核的系統(tǒng)調(diào)用,理解、掌握系統(tǒng)調(diào)用的實現(xiàn)框架、用戶界面、參數(shù)傳遞、進入返回過程。閱讀內(nèi)核源代碼,通過添加一個簡單的系統(tǒng)調(diào)用實驗,進一步理解操作系統(tǒng)處理系統(tǒng)調(diào)用的統(tǒng)一流程。二、設(shè)備環(huán)境Linux環(huán)境:彼口口014.04,內(nèi)核linux3.19.0-49-generic三、實驗內(nèi)容利用內(nèi)核模塊在現(xiàn)有的系統(tǒng)中添加一個不用傳遞參數(shù)的系統(tǒng)調(diào)用。這個系統(tǒng)調(diào)用的功能是實現(xiàn)遍歷進程并打印進程樹。1、模塊模塊是內(nèi)核的一部分,但是并沒有被編譯到內(nèi)核里面去。它們被分別編譯并連接成一組目標(biāo)文件,這些文件能被插入到正在運行的內(nèi)核,或者從正在運行的內(nèi)核中移走。內(nèi)核模塊至少必須有2個函數(shù):int_module和cleanup_module。第一個函數(shù)是在把模塊插入內(nèi)核時調(diào)用的;第二個函數(shù)則在刪除該模塊時調(diào)用。由于內(nèi)核模塊是內(nèi)核的一部分,所以能訪問所有內(nèi)核資源。根據(jù)對linux系統(tǒng)調(diào)用機制的分析,如果要增加系統(tǒng)調(diào)用,可以編寫自己的函數(shù)來實現(xiàn),然后在sys_call_tabfc中增加一項,使該項中的指針指向自己編寫的函數(shù),就可以實現(xiàn)系統(tǒng)調(diào)用。2、系統(tǒng)調(diào)用相關(guān)的數(shù)據(jù)結(jié)構(gòu)函數(shù)名以“sys_”開頭,后跟該系統(tǒng)調(diào)用的名字。例如,系統(tǒng)調(diào)用fork()的響應(yīng)函數(shù)是sys_fork()(見Kernel/fork.c),exit()的響應(yīng)函數(shù)是sys_exit()(見kernel/fork.c)。文件include/asm/unisted.h為每個系統(tǒng)調(diào)用規(guī)定了唯一的編號。假設(shè)用name表示系統(tǒng)調(diào)用的名稱,那么系統(tǒng)調(diào)用號與系統(tǒng)調(diào)用響應(yīng)函數(shù)的關(guān)系是:以系統(tǒng)調(diào)用號—NR—name作為下標(biāo),可找出系統(tǒng)調(diào)用表sys_call_table中對應(yīng)表項的內(nèi)容,它正好是該系統(tǒng)調(diào)用的響應(yīng)函數(shù)sys_name的入口地址。系統(tǒng)調(diào)用表sys_call_table記錄了各sys_name函數(shù)在表中的位置,共190項。有了這張表,就很容易根據(jù)特定系統(tǒng)調(diào)用在表中的偏移量,找到對應(yīng)的系統(tǒng)調(diào)用響應(yīng)函數(shù)的入口地址。系統(tǒng)調(diào)用表共256項,余下的項是可供用戶自己添加的系統(tǒng)調(diào)用空間。3、task_struct數(shù)據(jù)結(jié)構(gòu)task_struct是linux進程描述符的數(shù)據(jù)結(jié)構(gòu),其定義位置在include/linux/sched.h,其信息組成包括:⑴進程狀態(tài)信息(state,flags,ptrace)(2)調(diào)度信息(static_prio,normal_proi,run_list,array,policy)(3)內(nèi)存管S(mm,active_mm)(4)進程狀態(tài)位信息(binfmt,exit_state,exit_code,exit_signal)(5)身份信息(pid,tgid,uid,suid,fsuid,gid,egid,sgid,fsgid)(6)家族信息(real_parent,parent,children,sibling)(7)進程耗間信息(realtime,utime,stime,starttime)(8)時鐘信息(it_prof_expires,it_virt_expires,it_sched_expires)⑼文件系統(tǒng)信息(link_count,fs,files)(10)IPC信息(sysvsem,signal,sighand,blocked,sigmask,pending)本次實驗所要用到的是其家族信息parent,children,sibling。4、list_head數(shù)據(jù)結(jié)構(gòu)children,sibling都是list_head結(jié)構(gòu)的變量,list_head其實是一個簡單的雙向循環(huán)鏈表結(jié)構(gòu),其結(jié)構(gòu)定義為:structlist_head{structlist_head*next,*prev;};list_entry(ptr,type,member):如果type結(jié)構(gòu)中member的地址是ptr,則返回type結(jié)構(gòu)的地址。方式如下:((type*)((char*)(ptr)-(unsignedlong)(&((type*)0)->member)))5、實驗主要內(nèi)容⑴添加系統(tǒng)調(diào)用的名字碑并添加系統(tǒng)調(diào)用號⑵在系統(tǒng)調(diào)用表中添加相應(yīng)表項⑶編寫內(nèi)核調(diào)用模塊⑷使用內(nèi)核模塊加載模塊程序⑸實現(xiàn)sys_mycall()⑹編寫用戶態(tài)程序測試四、程序模塊程序一共分為三大模塊,分別為初始化模塊、結(jié)束模塊、內(nèi)核模塊插入和打印進程樹模塊。三個模塊結(jié)合可打印出系統(tǒng)當(dāng)下的所有進程。初始化模塊包括:init_syscall(void)和clear_and_return_cr0(void)。主要用途是修改sys_call_table表首地址的只讀狀態(tài)和現(xiàn)場保護。Sys_call_table首地址屬性是只讀,因此修改之使其可以通過系統(tǒng)調(diào)用號獲取系統(tǒng)調(diào)用程序的地址,在修改完成后恢復(fù)sys_call_table首地址的只讀屬性。現(xiàn)場保護是預(yù)存被使用服務(wù)號的原來所對應(yīng)的系統(tǒng)調(diào)用程序。結(jié)束模塊包括:exit_syscall(void),setback_cr0(unsignedintval。主要用途是設(shè)置cr0可更改并恢復(fù)原有的中斷向量表中的函數(shù)指針的值和恢復(fù)原有的cr0的值。內(nèi)核模塊插入包括:module_init(init_syscall,module_exit(exit_syscall。主要用途是將設(shè)計好的程序插入到正在運行的內(nèi)核,或者從正在運行的內(nèi)核中移走。五、具體實施步驟及結(jié)果1、查找系統(tǒng)調(diào)用號系統(tǒng)調(diào)用號在文件unistd.h里面定義。這個文件在ubuntu14.04下位于/usr/include/asm-generic/unist(現(xiàn)在我們在unistd.h中查找我們需要的系統(tǒng)調(diào)用號。系統(tǒng)調(diào)用號集中在1-278和1000以后,所以本次實驗選擇了300作為調(diào)用號。如圖1。

flundef_NRsyscalls"define_NRsyscalls278/*Allsyscallsbelowhereshouldgoawayreally,theseareprovidedforbothreviewandasaportinghelpfortheClibraryversion.★L(fēng)astchance:areanyoftheseimportantenoughtoenablebydefault?*/tfifdef_ARCH_WANT_SYSCALL_NO_ATfldeftne_NRopen1024_S¥SCALL(_NRopen,sysopen)^define_NRlink1025_SYSCALL(_NR_link,sys_link)ffdefine_NRunlink1026_SYSCALL(_NRunlink,sysunlink)圖12、在系統(tǒng)調(diào)用表中添加或修改相應(yīng)表項添加系統(tǒng)調(diào)用號之后,系統(tǒng)才能根據(jù)這個號,作為索引,去找sys_call_table中的相應(yīng)表項。這是2.6內(nèi)核以前使用的方式,但是2.6以后linux隱藏了sys_call_table的地址,所以查看了System.map文件(文件位置在/boot/中,查看方式為cat/boot/System.map-$(uname-r)Igrepsys_call_table),得至^sys_call_table表的首地址,首地址為0xc16ce14。如圖2。rootgubuntu:/boot#Isabi-3.19.0-25-genertcabi-3.19.0-49-genertcconftg-3.19.9-25-genericconftg-3.19.9-49-generic'grubrootgubuntu:/boot#Isabi-3.19.0-25-genertcabi-3.19.0-49-genertcconftg-3.19.9-25-genericconftg-3.19.9-49-generic'grubtnitrd.img-3.19.0-25-genertctnitrd.img-3.19.9-49-genertcFiemtest86+.btnFiemtest86+.elfFiemtest86f_multtboot*binSystem,nap-3.19.0-25-genertcSystem,nap-3.19.0-49-genertcvmlinuz-3.19.。-25-genertcvmlinuz-3.19.。-49-genertcrootgubuntu:/boot#cat/boot/System.map-$(uname-r)|grepsyscalltablecl6cel40Rrootgubuntu:/boot#|圖23、實現(xiàn)sys_mycall()使用遞歸的方式,通過list數(shù)據(jù)結(jié)構(gòu),由父進程到子進程進行層次遍歷。進程樹第一層為linux的初始進程init,init加入list表中,再遍歷init的子進程,接著遍歷子進程的子進程,如此往復(fù),可以完整遍歷進程樹。主要代碼如下。asmlinkagevoidpstreeMy(structtask_struct*p,intb){for(l=p->children.next;l!=&(p->children);l=l->next){structtask_struct*t=list_entry(l,structtask_struct,sibling);pstreeMy(t,b+1);}}list_entry(ptr,type,member)宏定義主要作用是從一個結(jié)構(gòu)的成員指針找到其容器的指針。在這里用來獲取子進程task_struct結(jié)構(gòu)的基地址。4、系統(tǒng)調(diào)用程序插入內(nèi)核模塊源代碼寫好后,編輯Makefile文件,通過make編譯后,產(chǎn)生test.ko文件。

使用insmod命令將test.ko插入到內(nèi)核模塊中。查看是否加入成功,可使用lsmod命令。刪除模塊可使用rmmod命令。如圖3。root@ubuntu:/home/luchao/linuxTest/tests#IsMakefileModule.synverstest.kotestMain.ctest.mod.omodulesiordertest.ctestMatntest*mod.ctest,oinsmodtest.koLsnod|greptestrmmodtestlsmod|insmodtest.koLsnod|greptestrmmodtestlsmod|greptestroot@ubuntu:/home/luchao/ltnuxTest/test3#rootgubuntu:/home八uchao/linuxTest/tests#root@ubuntu:/home/luchao/linuxTest/test3#圖35、打印進程樹首先編寫用戶程序testMain.c,用來測試加載在內(nèi)核中的test模塊。使用gcc編譯文件產(chǎn)生運行文件testMain,在運行testMain查看進程樹。如圖4。rootgubuntu:/home/luchao/linuxTest/test3ifIsMakefileModule.symverstest.kotestMain?ctest.mod.omodules.ordertest.ctestMaintest.mod.ctest.orootgubuntu:/home/luchao/ltnuxTest/test3ifgcc-otestMaintestMain.cTootgubuntu:/hcime八uchaci/li_nuxTest/test3#./testMain出947root@ubuntu:/hone/luchao/linuxTest/test3#|圖4使用printk無法在終端打印出進程樹,printk是將信息存在linux中的ringbuffer緩存中,可以通過dmesg查看,或者查看var/log/kern.log文件。詳見附件。六、所遇到的問題及解決的方法1、對list_entry()的疑問由于對遍歷進程樹的不熟悉,造成遍歷進程樹數(shù)次錯誤,在查找了多次資料后,看到一種使用遞歸且很簡潔的方式,其中使用了一個宏定義——list_entry()。雖然看文檔知道其可以通過成員查詢成員所對應(yīng)數(shù)據(jù)結(jié)構(gòu)的基地址,但是對其內(nèi)部實現(xiàn)還是不清楚。終于在程序完成后,查找資料,進一步加深了解list_entry()宏定義。#definelist_entry(ptr,type,member)((type*)((char*)(ptr)-(unsignedlong)(&((type*)0)->member)))從一個結(jié)構(gòu)的成員指針找到其容器的指針。ptr是找容器的那個變量的指針,把它減去自己在容器中的偏移量的值就應(yīng)該得到容器的指針。(容器就是包含自己的那個結(jié)構(gòu))。指針的加減要注意類型,用(char*)ptr是為了計算字節(jié)偏移。((type*)0)->member是一個小技巧。自己理解吧。前面的(type*)再轉(zhuǎn)回容器的類型。把“0”強制轉(zhuǎn)化為指針類型,則該指針一定指向“0”(數(shù)據(jù)段基址)。因為指針是“type*”型的,所以可取到以“0”為基地址的一個type型變量member域的地址。那么這個地址也就等于member域到結(jié)構(gòu)體基地址的偏移字節(jié)數(shù)。(char*)(ptr)使得指針的加減操作步長為一字節(jié),(unsignedlong)(&((type*)0)->member)等于ptr指向的member到該member所在結(jié)構(gòu)體基地址的偏移字節(jié)數(shù)。二者一減便得出該結(jié)構(gòu)體的地址。轉(zhuǎn)換為(type*)型的指針,大功告成。2、sys_call_table表首地址無法獲得由于linux因為安全的原因在2.6后隱藏了sys_call_table表的首地址,致使我所使用的linux3.16無法查看表的首地址。最初我使用的方式是:cat/proc/kallsyms|grepsys_call_tables但是并終端并沒有顯示任何內(nèi)容,通過查找資料,最終找到查看/boot/system.map文件獲得首地址。cat/boot/System.map-$(uname-r)|grepsys_call_table3、sys_call_table表首地址屬性只讀在獲得地址后,并代入程序后,程序在insmod時,依然報錯。通過對地址分析,發(fā)現(xiàn)地址是只讀的,所以在程序更改地址時,程序出錯。最后通過在linux論壇查找資料,發(fā)現(xiàn)可以修改內(nèi)存中的權(quán)限,這時程序不再出問題。代碼如下:intset_page_rw(longunsignedint_addr){structpage*pg;pgprot_tprot;pg=virt_to_page(_addr);prot.pgprot=VM_READ|VM_WRITE;returnchange_page_attr(pg,1,prot);}intset_page_ro(longunsignedint_addr){structpage*pg;pgprot_tprot;pg=virt_to_page(_addr);prot.pgprot=VM_READ;returnchange_page_attr(pg,1,prot);}最終我并沒有采取這種方法,因為這種方法并不安全。最終采用的方式見附件代碼。附件1、源碼#include<linux/init.h>#include<linux/module.h>#include<linux/kernel.h>#include<linux/unistd.h>#include<asm-generic/uaccess.h>#include<linux/sched.h>#include<linux/list.h>#definemy_syscall300〃要查啊啊,/boot/中的System.map-$(uname-r)#definesys_call_table_adress0xc16ce140intb=0;intorig_cr0;unsignedlong*sys_call_table=0;staticint(*anything_saved)(void);staticstructtask_struct*pParent;asmlinkagelongsys_mycall(void);asmlinkagevoidpstreeMy(structtask_struct*p,intb);unsignedintclear_and_return_cr0(void){unsignedintcr0=0;unsignedintret;asm("movl%%cr0,%%eax":"=a"(cr0));ret=cr0;cr0&=0xfffeffff;asm("movl%%eax,%%cr0"::"a"(cr0));returnret;}voidsetback_cr0(unsignedintval){asmvolatile("movl%%eax,%%cr0"::"a"(val));}staticint__initinit_syscall(void){printk("hello,kernel\n");//獲取系統(tǒng)調(diào)用服務(wù)首地址sys_call_table=(unsignedlong*)sys_call_table_adress;//保存原始系統(tǒng)調(diào)用的地址anything_saved=(int(*)(void))(sys_call_table[my_syscall]);〃設(shè)置cr0可更改orig_cr0=clear_and_return_cr0();//更改原始的系統(tǒng)調(diào)用服務(wù)地址sys_call_table[my_syscall]=(unsignedlong)&sys_mycall;setback_cr0(orig_cr0);//設(shè)置為原始的只讀cr0//回溯到初始父進程for(pParent=current;pParent!=&init_task;pParent=pParent->parent){}return0;}staticvoid__exitexit_syscall(void){〃設(shè)置cr0中對sys_call_table的更改權(quán)限。orig_cr0=clear_and_return_cr0();//設(shè)置cr0可更改//恢復(fù)原有的中斷向量表中的函數(shù)指針的值。sys_call_table[my_syscall]=(unsignedlong)anything_saved;〃恢復(fù)原有的cr0的值setback_cr0(orig_cr0);printk("callexit\n");}asmlinkagelongsys_mycall(void){printk("Thisismy_syscall!\n");pstreeMy(pParent,b);returncurrent->pid;}asmlinkagevoidpstreeMy(structtask_struct*p,intb){inti;structlist_head*l;for(i=0;i<b;i++){printk("");}printk("|%s\n",p->comm);for(l=p->children.next;l!=&(p->children);l=l->next){structtask_struct*t=list_entry(l,structtask_struct,sibling);pstreeMy(t,b+1);}}module_init(init_syscall);module_exit(exit_syscall);MODULE_LICENSE("GPL");2、進程樹0一3、-、h7二、-t-mM———、0、.te.mM干—H【:0WM中—p-pne干—、』-』。章—m局科哥說M國圖周博忘之依耳曷夠?qū)佣厝展ぁ沆ΩΩΩ︵嵛混c甫甫6i-5,-8「Mlq。。^筆。嘈—一I』筆1'III一oi筆£。。一筆''=。一「"|"筆1'8。,PMEd^nq干一?"上干—-H。:IWM中—TypEWOS干—oopvpl羊工於喟宣——』ppe5&——一PJOIO。我3中「oluuep—3hum一一一二u一——-黑畿底器然盤器籌器建器照第脛醛甑翳噩器鰥蜜崽舞器罪罪■患斐器患翳王監(jiān)翦器弱翳王岸疆翳鴕皇身器器翳瞌靛能馥髭器莪戰(zhàn)戲戲熊熊崽然戲舞景熊疑戢然要泰最然戲線柔靛柔彭熊慧漲翅莪渴戲畿影戲皴感雅舞最熊森會戲畿馥燕器籌弱翳弱翳弱弱弱弱弱器*三sgsgsgssgsgsgssgs§s§ssgs§s§ss§§§s§ss§§§s§ss§§§s§ss§§§ssgs§s§ssgs§s§ssgs§s§ssgs§s§ss§§§s§ss§§§s§ss§§§s§ss§§§ssgs§s§ssgs§s§ssgs§s§ssg§§s§ss§§§s§ss§§§s§ss§§§s§ss§s§ssgs§s§ssgs§s§ssgs§s§ss§§§s§ss§§§s§ss§§§s§ss§§§s§ss§s§ssg§§s§sllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllillli勺除日勺倍勺等勺除日勺倍勺等勺除日勺倍勺等勺除日勺專勺等勺勺青勺號勺等勺勺青勺號勺等

溫馨提示

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

評論

0/150

提交評論