Linux內(nèi)核中增加一個(gè)系統(tǒng)調(diào)用_第1頁
Linux內(nèi)核中增加一個(gè)系統(tǒng)調(diào)用_第2頁
Linux內(nèi)核中增加一個(gè)系統(tǒng)調(diào)用_第3頁
Linux內(nèi)核中增加一個(gè)系統(tǒng)調(diào)用_第4頁
Linux內(nèi)核中增加一個(gè)系統(tǒng)調(diào)用_第5頁
已閱讀5頁,還剩13頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

1、選題要求:在Linux內(nèi)核中增加一個(gè)系統(tǒng)調(diào)用,并編寫對(duì)應(yīng)的linux應(yīng)用程序。利用該系統(tǒng)調(diào)用能夠遍歷系統(tǒng)當(dāng)前所有進(jìn)程的任務(wù)描述符,并按進(jìn)程父子關(guān)系將這些描述符所對(duì)應(yīng)的進(jìn)程id(PID)組織成樹形結(jié)構(gòu)顯示。目錄一.程序的主要設(shè)計(jì)思路,實(shí)現(xiàn)方式11.1 添加系統(tǒng)調(diào)用的兩種方法1編譯內(nèi)核法1內(nèi)核模塊法11.2 程序的主要設(shè)計(jì)思路11.3 環(huán)境2二.程序的模塊劃分,及對(duì)每個(gè)模塊的說明22.1 通過內(nèi)核模塊實(shí)現(xiàn)添加系統(tǒng)調(diào)用2修改系統(tǒng)調(diào)用的模塊2獲取sys_call_table的地址2清除內(nèi)存區(qū)域的寫保護(hù)32.2 編寫系統(tǒng)調(diào)用指定自己的系統(tǒng)調(diào)用4內(nèi)核的初始化函數(shù)4自己的系統(tǒng)調(diào)用服務(wù)例程4移除內(nèi)核模塊時(shí),將

2、原有的系統(tǒng)調(diào)用進(jìn)行還原6模塊注冊相關(guān)62.3 編寫用戶態(tài)的測試程序62.4 編寫Makefile文件7三.所遇到的問題及解決的方法83.1 進(jìn)程個(gè)數(shù)確定83.2 被更改的系統(tǒng)調(diào)用號(hào)的選擇83.3 獲取系統(tǒng)調(diào)用表的地址83.4 內(nèi)核和用戶態(tài)數(shù)據(jù)交換8四.程序運(yùn)行結(jié)果及使用說明84.1 將編譯出來的內(nèi)核模塊hello.ko加載到內(nèi)核中84.2通過dmesg查看輸出信息是否正確94.3運(yùn)行測試程序,輸出樹狀打印結(jié)果(部分結(jié)果截圖)94.4卸載自定義模塊10五.附錄115.1 內(nèi)核模塊程序hello.c115.2 測試程序hello_test.c145.3 Makefile文件14一.程序的主要設(shè)計(jì)思

3、路,實(shí)現(xiàn)方式1.1 添加系統(tǒng)調(diào)用的兩種方法1.1.1編譯內(nèi)核法編寫好源碼之后· 修改內(nèi)核的系統(tǒng)調(diào)用庫函數(shù) /usr/include/asm-generic/unistd.h,在這里面可以使用在syscall_table中沒有用到的223號(hào)· 添加系統(tǒng)調(diào)用號(hào),讓系統(tǒng)根據(jù)這個(gè)號(hào),去找到syscall_table中的相應(yīng)表項(xiàng)。在/arch/x86/kernel/syscall_table_32.s文件中添加系統(tǒng)調(diào)用號(hào)和調(diào)用函數(shù)的對(duì)應(yīng)關(guān)系· 接著就是my_syscall的實(shí)現(xiàn)了,在這里有兩種方法:第一種方法是在kernel下自己新建一個(gè)目錄添加自己的文件,但是要編寫Mak

4、efile,而且要修改全局的Makefile。第二種比較簡便的方法是,在kernel/sys.c中添加自己的服務(wù)函數(shù),這樣子不用修改Makefile.以上準(zhǔn)備工作做完之后,然后就要進(jìn)行編譯內(nèi)核了,以下是編譯內(nèi)核的一個(gè)過程1. make menuconfig (使用圖形化的工具,更新.config文件)2. make -j3 bzImage(編譯,-j3指的是同時(shí)使用3個(gè)cpu來編譯,bzImage指的是更新grub,以便重新引導(dǎo))3. make modules(對(duì)模塊進(jìn)行編譯)4. make modules_install(安裝編譯好的模塊)5. depmod(進(jìn)行依賴關(guān)系的處理)6. reb

5、oot (重啟看到自己編譯好的內(nèi)核)內(nèi)核模塊法內(nèi)核模塊可以作為獨(dú)立程序來編譯的函數(shù)和數(shù)據(jù)類型的集合。之所以提供模塊機(jī)制,是因?yàn)長inux本身是一個(gè)單內(nèi)核。單內(nèi)核由于所有內(nèi)容都集成在一起,效率很高,但可擴(kuò)展性和可維護(hù)性相對(duì)較差,模塊機(jī)制可以彌補(bǔ)這一缺陷。Linux模塊可以通過靜態(tài)或動(dòng)態(tài)的方法加載到內(nèi)核空間,靜態(tài)加載是指在內(nèi)核啟動(dòng)過程中加載;動(dòng)態(tài)加載是指在內(nèi)核運(yùn)行的過程中隨時(shí)加載。一個(gè)模塊被加載到內(nèi)核中時(shí),就成為內(nèi)核代碼的一部分。模塊加載入系統(tǒng)時(shí),系統(tǒng)修改內(nèi)核中的符號(hào)表,將新加載的模塊提供的資源和符號(hào)添加到內(nèi)核符號(hào)表中,以便模塊間通信。這種方法是采用系統(tǒng)調(diào)用攔截的一種方式,改變某一個(gè)系統(tǒng)調(diào)用號(hào)對(duì)應(yīng)

6、的服務(wù)程序?yàn)槲覀冏约旱木帉懙某绦颍瑥亩喈?dāng)于添加了我們自己的系統(tǒng)調(diào)用。下面的內(nèi)容,會(huì)詳述用內(nèi)核模塊法實(shí)現(xiàn)目標(biāo)的過程。1.2 程序的主要設(shè)計(jì)思路程序分三部分,一部分是通過內(nèi)核模塊實(shí)現(xiàn)添加系統(tǒng)調(diào)用,二是編寫系統(tǒng)調(diào)用指定自己的系統(tǒng)調(diào)用,最后是編寫用戶態(tài)的測試程序。1.3 環(huán)境Ubuntu14.04 + 3.13.0內(nèi)核版本內(nèi)核版本:二.程序的模塊劃分,及對(duì)每個(gè)模塊的說明2.1 通過內(nèi)核模塊實(shí)現(xiàn)添加系統(tǒng)調(diào)用這種方法其實(shí)是系統(tǒng)調(diào)用攔截的實(shí)現(xiàn)。系統(tǒng)調(diào)用服務(wù)程序的地址是放在sys_call_table中通過系統(tǒng)調(diào)用號(hào)定位到具體的系統(tǒng)調(diào)用地址,那么我們通過編寫內(nèi)核模塊來修改sys_call_table中的系統(tǒng)

7、調(diào)用的地址為我們自己定義的函數(shù)的地址,就可以實(shí)現(xiàn)系統(tǒng)調(diào)用的攔截。通過模塊加載時(shí),將系統(tǒng)調(diào)用表里面的那個(gè)系統(tǒng)調(diào)用號(hào)的那個(gè)系統(tǒng)調(diào)用號(hào)對(duì)應(yīng)的系統(tǒng)調(diào)用服務(wù)例程改為我們自己實(shí)現(xiàn)的系統(tǒng)歷程函數(shù)地址。修改系統(tǒng)調(diào)用的模塊在/usr/include/i386-linux-gnu/asm/unistd_32.h文件中查看系統(tǒng)調(diào)用序號(hào):找到結(jié)果(部分截圖):可以看到,222號(hào)和223號(hào)系統(tǒng)調(diào)用是空的,因此選取223作為新的系統(tǒng)調(diào)用號(hào)。獲取sys_call_table的地址在查看系統(tǒng)調(diào)用表的內(nèi)存地址:找到結(jié)果:為0xc165e1402.1.3清除內(nèi)存區(qū)域的寫保護(hù)得到了sys_call_table的地址,該符號(hào)對(duì)應(yīng)的內(nèi)

8、存區(qū)域是只讀的。所以我們要修改它,必須對(duì)它進(jìn)行清除寫保護(hù),這里介紹兩種方法:第一種方法:我們知道控制寄存器cr0的第16位是寫保護(hù)位。cr0的第16位置為了禁止超級(jí)權(quán)限,若清零了則允許超級(jí)權(quán)限往內(nèi)核中寫入數(shù)據(jù),這樣我們可以再寫入之前,將那一位清零,使我們可以寫入。然后寫完后,又將那一位復(fù)原就行了。/使cr0寄存器的第17位設(shè)置為0(即是內(nèi)核空間可寫)unsigned int clear_and_return_cr0(void) unsigned int cr0 = 0; unsigned int ret; asm("movl %cr0, %eax":"=a&quo

9、t;(cr0); /將cr0寄存器的值移動(dòng)到eax寄存器中,同時(shí)輸出到cr0變量中 ret = cr0; cr0 &= 0xfffeffff;/將cr0變量的第17位清0 asm("movl %eax, %cr0":"a"(cr0); /將cr0變量的值放入寄存器eax中,并且放入cr0寄存器中 return ret;/讀取val的值到eax寄存器,再將eax寄存器的值放入cr0寄存器中-改變內(nèi)核地址空間參數(shù)void setback_cr0(unsigned int val) asm volatile("movl %eax, %cr0&

10、quot;:"a"(val);第二種方法:通過設(shè)置虛擬地址對(duì)應(yīng)的也表項(xiàng)的讀寫屬性來設(shè)置。int make_rw(unsigned long address) unsigned int level; pte_t *pte = lookup_address(address, &level);/查找虛擬地址所在的頁表地址 if (pte->pte & _PAGE_RW) /設(shè)置頁表讀寫屬性pte->pte |= _PAGE_RW; return 0; int make_ro(unsigned long address) unsigned int lev

11、el; pte_t *pte = lookup_address(address, &level); pte->pte &= _PAGE_RW; /設(shè)置只讀屬性return 0; 2.2 編寫系統(tǒng)調(diào)用指定自己的系統(tǒng)調(diào)用內(nèi)核的初始化函數(shù)此函數(shù)內(nèi)采用的是2.1.3中的第一種方法。static int _init init_addsyscall(void) printk("hello,yinyu kerneln"); /獲取系統(tǒng)調(diào)用服務(wù)首地址 sys_call_table = (unsigned long *)sys_call_table_address;pr

12、intk("%xn",sys_call_table);/保存系統(tǒng)調(diào)用表中的NUM位置上的系統(tǒng)調(diào)用 anything_saved = (int(*)(void) (sys_call_tablemy_syscall_num); /使內(nèi)核地址空間可寫orig_cr0 = clear_and_return_cr0();/用自己的系統(tǒng)調(diào)用替換NUM位置上的系統(tǒng)調(diào)用sys_call_tablemy_syscall_num= (unsigned long)&sys_mycall; /使內(nèi)核地址空間不可寫setback_cr0(orig_cr0); return 0;自己的系統(tǒng)調(diào)用

13、服務(wù)例程部分一:創(chuàng)建進(jìn)程樹void processtree(struct task_struct * p,int b);結(jié)果需要以樹狀形式展示所有進(jìn)程的父子關(guān)系。為此,我們定義processtree()遞歸函數(shù)來訪問遍歷,并且將結(jié)果存儲(chǔ)在數(shù)組中,以便提供給用戶態(tài)訪問。void processtree(struct task_struct * p,int b)/創(chuàng)建進(jìn)程樹(進(jìn)程,深度) struct list_head * l; acounter.pid = p -> pid; acounter.depth = b; counter +; for(l = p -> children.

14、next; l != &(p->children); l = l->next) struct task_struct *t = list_entry(l,struct task_struct,sibling); processtree(t,b+1); 其中,特別使用了宏:#define list_entry(ptr, type, member) / (type *)(char *)(ptr)-(unsigned long)(&(type *)0)->member) ptr是指向list_head類型鏈表的指針;type為一個(gè)結(jié)構(gòu);member為結(jié)構(gòu)type中的

15、一個(gè)域,類型為list_head;這個(gè)宏返回指向type結(jié)構(gòu)的指針。目的:從一個(gè)結(jié)構(gòu)的成員指針找到其容器的指針部分二:創(chuàng)建自己的系統(tǒng)調(diào)用服務(wù)asmlinkage long sys_mycall(char _user * buf);在sys_mycall()中,從當(dāng)前進(jìn)程開始,遞歸調(diào)用processtree()函數(shù),將進(jìn)程信息存儲(chǔ)在數(shù)組中。然后利用copy_to_user函數(shù)將內(nèi)核信息傳遞給用戶態(tài)下,用戶態(tài)下的測試程序?qū)Y(jié)果進(jìn)行展示。asmlinkage long sys_mycall(char _user * buf) int b = 0; struct task_struct * p; pr

16、intk("This is yinyu_syscall!n"); for(p = current; p != &init_task; p = p->parent ); processtree(p,b); if(copy_to_user(struct process *)buf,a,512*sizeof(struct process) return -EFAULT; else return sizeof(a);移除內(nèi)核模塊時(shí),將原有的系統(tǒng)調(diào)用進(jìn)行還原static void _exit exit_addsyscall(void) /設(shè)置cr0中對(duì)sys_call_

17、table的更改權(quán)限。 orig_cr0 = clear_and_return_cr0(); /恢復(fù)原有的中斷向量表中的函數(shù)指針的值。 sys_call_tablemy_syscall_num= (unsigned long)anything_saved; /恢復(fù)原有的cr0的值 setback_cr0(orig_cr0); printk("call yinyu exit n");2.2.4模塊注冊相關(guān)l 模塊構(gòu)造函數(shù)module_init(init_addsyscall);執(zhí)行insmod或modprobe指令加載內(nèi)核模塊時(shí)會(huì)調(diào)用的初始化函數(shù)。函數(shù)原型必須是module_

18、init(),內(nèi)是函數(shù)指針。l 模塊析構(gòu)函數(shù)module_exit(exit_addsyscall);執(zhí)行rmmod指令卸載模塊時(shí)調(diào)用的函數(shù)。函數(shù)原型是module_exit();l 模塊許可聲明MODULE_LICENSE("GPL");函數(shù)原型是MODULE_LICENSE(),告訴內(nèi)核程序使用的許可證,不然在加載時(shí)它會(huì)提示該模塊污染內(nèi)核。一般會(huì)寫GPL。2.3 編寫用戶態(tài)的測試程序#include <linux/unistd.h>#include <syscall.h>/asmlinkage#include <sys/types.h>

19、;#include <stdio.h>struct process int pid; int depth;struct process a512;int main() int i,j; /在內(nèi)核中將223本來對(duì)應(yīng)的系統(tǒng)調(diào)用,臨時(shí)鏈到我們自定義的sys_mycall()中。通過該系統(tǒng)調(diào)用后獲得數(shù)組a printf("the result is:%dn",syscall(223,&a); for(i = 0; i < 512; i+) for(j = 0; j < ai.depth; j+) printf("|-"); pri

20、ntf("%dn",ai.pid); if(ai+1.pid = 0) break; return 0;2.4 編寫Makefile文件KVERS = $(shell uname -r)# Kernel modulesobj-m += hello.o# Specify flags for the module compilation.#EXTRA_CFLAGS=-g -O0build: kernel_modules user_testkernel_modules:make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modulesu

21、ser_test:gcc -o hello_test hello_test.cclean:make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean三.所遇到的問題及解決的方法3.1 進(jìn)程個(gè)數(shù)確定系統(tǒng)可運(yùn)行的最大進(jìn)程數(shù),通過ulimit u 查看有7863個(gè)我們通過ps ef|wc l命令實(shí)際查看當(dāng)前運(yùn)行進(jìn)程數(shù)量為191個(gè)存儲(chǔ)進(jìn)程信息的數(shù)組大小為512是夠用的。3.2 被更改的系統(tǒng)調(diào)用號(hào)的選擇。3.3 獲取系統(tǒng)調(diào)用表的地址見2.1.2。3.4 內(nèi)核和用戶態(tài)數(shù)據(jù)交換我們在內(nèi)核模塊程序中,將進(jìn)程遍歷信息存儲(chǔ)在數(shù)組中,然后需要將其傳遞給用戶態(tài)下。采用c

22、opy_from_user()和copy_to_user()這兩個(gè)函數(shù),這兩個(gè)函數(shù)負(fù)責(zé)在用戶空間和內(nèi)核空間傳遞數(shù)據(jù)。因此我們在測試程序中,將空數(shù)組a的地址作為參數(shù)傳遞給內(nèi)核模塊程序,在內(nèi)核中使用copy_to_user()函數(shù)將內(nèi)核中的數(shù)組信息傳遞給用戶態(tài)下的地址。四.程序運(yùn)行結(jié)果及使用說明4.1 將編譯出來的內(nèi)核模塊hello.ko加載到內(nèi)核中加載內(nèi)核模塊命令:insmod hello.ko4.2通過dmesg查看輸出信息是否正確4.3運(yùn)行測試程序,輸出樹狀打印結(jié)果(部分結(jié)果截圖)4.4卸載自定義模塊卸載內(nèi)核模塊命令:insmod hello.ko五.附錄5.1 內(nèi)核模塊程序hello.c#

23、include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>/list_head#include <linux/unistd.h>#include <asm/uaccess.h>#include <linux/sched.h>/task_struct#define my_syscall_num 223#define sys_call_table_address 0xc165e140static int counter = 0;struct

24、process int pid; int depth;struct process a512;unsigned int clear_and_return_cr0(void);void setback_cr0(unsigned int val);asmlinkage long sys_mycall(char _user *buf);int orig_cr0;unsigned long *sys_call_table = 0;static int (*anything_saved)(void);void processtree(struct task_struct * p,int b)/創(chuàng)建進(jìn)程樹

25、(進(jìn)程,深度) struct list_head * l; acounter.pid = p -> pid; acounter.depth = b; counter +; for(l = p -> children.next; l != &(p->children); l = l->next) struct task_struct *t = list_entry(l,struct task_struct,sibling); processtree(t,b+1); unsigned int clear_and_return_cr0(void)/使cr0寄存器的第1

26、7位設(shè)置為0(即是內(nèi)核空間可寫) unsigned int cr0 = 0; unsigned int ret; asm("movl %cr0, %eax":"=a"(cr0); /將cr0寄存器的值移動(dòng)到eax寄存器中,同時(shí)輸出到cr0變量中 ret = cr0; cr0 &= 0xfffeffff;/將cr0變量的第17位清0 asm("movl %eax, %cr0":"a"(cr0); /將cr0變量的值放入寄存器eax中,并且放入cr0寄存器中 return ret;void setback_cr

27、0(unsigned int val)/讀取val的值到eax寄存器,再將eax寄存器的值放入cr0寄存器中-改變內(nèi)核地址空間參數(shù) asm volatile("movl %eax, %cr0":"a"(val);static int _init init_addsyscall(void)/保存原來系統(tǒng)調(diào)用表中此地址中的系統(tǒng)調(diào)用 printk("hello,yinyu kerneln"); sys_call_table = (unsigned long *)sys_call_table_address;/獲取系統(tǒng)調(diào)用服務(wù)首地址 prin

28、tk("%xn",sys_call_table); anything_saved = (int(*)(void) (sys_call_tablemy_syscall_num);/保存系統(tǒng)調(diào)用表中的NUM位置上的系統(tǒng)調(diào)用 orig_cr0 = clear_and_return_cr0();/使內(nèi)核地址空間可寫 sys_call_tablemy_syscall_num= (unsigned long)&sys_mycall;/用自己的系統(tǒng)調(diào)用替換NUM位置上的系統(tǒng)調(diào)用 setback_cr0(orig_cr0);/使內(nèi)核地址空間不可寫 return 0;asmlinka

29、ge long sys_mycall(char _user * buf) int b = 0; struct task_struct * p; printk("This is yinyu_syscall!n"); for(p = current; p != &init_task; p = p->parent ); processtree(p,b); if(copy_to_user(struct process *)buf,a,512*sizeof(struct process)/將內(nèi)核空間內(nèi)容復(fù)制到用戶空間 return -EFAULT; else retur

30、n sizeof(a);static void _exit exit_addsyscall(void) /設(shè)置cr0中對(duì)sys_call_table的更改權(quán)限。 orig_cr0 = clear_and_return_cr0(); /恢復(fù)原有的中斷向量表中的函數(shù)指針的值。 sys_call_tablemy_syscall_num= (unsigned long)anything_saved; /恢復(fù)原有的cr0的值 setback_cr0(orig_cr0); printk("call yinyu exit n");/模塊入口函數(shù)為init_addsyscall(),由module_init()宏指定,在模塊被加載的時(shí)候被調(diào)用向系統(tǒng)注冊。入口函數(shù)的返回值:0表示成功,非0表示失敗。module_ini

溫馨提示

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

評(píng)論

0/150

提交評(píng)論