標籤:
轉自:http://edsionte.com/techblog/archives/category/linux%E5%86%85%E6%A0%B8%E7%BC%96%E7%A8%8B背景
如何在Linux核心中執行某些使用者態程式或系統命令?在使用者態中,可以通過execve()實現;在核心態,則可以通過call_usermodehelpere()實現該功能。如果您查閱了call_usermodehelper()核心功能的源碼實現,就可以發現該函數最終會執行do_execve()。而execve系統調用在經曆核心的系統調用流程後,也會最終調用do_execve()。
使用舉例
1.無輸出的可執行檔測試
載入函數demo如下所示:
1 |
static int __init call_usermodehelper_init(void) |
4 |
char path[] = "/bin/mkdir"; |
5 |
char *argv[] = {path, "-p", "/home/tester/new/new_dir", NULL}; |
7 |
printk("call_usermodehelper module is starting..!\n"); |
8 |
ret = call_usermodehelper(path, argv, envp, UMH_WAIT_PROC); |
9 |
printk("ret=%d\n", ret); |
卸載函數demo如下所示:
1 |
static void __exit call_usermodehelper_exit(void) |
4 |
char path[] = "/bin/rm"; |
5 |
char *argv[] = {path, "-r", "/home/tester/new", NULL}; |
8 |
printk("call_usermodehelper module is starting..!\n"); |
9 |
ret = call_usermodehelper(path, argv, envp, UMH_WAIT_PROC); |
10 |
printk("ret=%d\n", ret); |
2.有輸出的可執行檔測試
如果該可執行檔有輸出,則可以利用輸出重新導向,不過此時的可執行檔應該是/bin/bash,而實際的可執行檔則稱為bash的參數。比如如果想在核心執行ls -la命令,並且將其輸出重新導向到ls_output中,則在上述的argv[]={“/bin/bash”, “-c”, “ls”, “-la”, “>”, “/home/tester/ls_output”, NULL};
本文雖然說明的是在核心態如何調用使用者態程式,不過可以將這種方法抽象一下,看作是核心態主動向使用者態發起通訊的一種方式。
沒有評論 »
發表在Linux核心編程
Tags: 核心態 資料互動 使用者態
Linux核心中通過檔案描述符擷取絕對路徑
2014年3月19日背景
在Linux核心中,已知一個進程的pid和其開啟檔案的檔案描述符fd,如何擷取該檔案的絕對路徑?基本思路是先擷取該檔案在核心中的file結構體,再通過d_path()擷取到整個檔案的絕對路徑。
方法一
如果理解了進程和檔案系統資料結構之間的關係,那麼這種方法可以採用。基本的方法如下:
1.通過進程pid擷取進程描述符task_struct;
2.通過task_struct擷取該進程開啟檔案結構files_struct,從而擷取檔案描述符表;
3.以fd為索引在檔案描述符表中擷取對應檔案的結構體file;
4.通過file擷取對應path結構,該結構封裝當前檔案對應的dentry和掛載點;
5.通過核心功能d_path()擷取該檔案的絕對路徑;
通過進程pid擷取進程描述符demo:
1 |
struct task_struct *get_proc(pid_t pid) |
3 |
struct pid *pid_struct = NULL; |
4 |
struct task_struct *mytask = NULL; |
6 |
pid_struct = find_get_pid(pid); |
9 |
mytask = pid_task(pid_struct, PIDTYPE_PID); |
通過fd以及d_path()擷取絕對路徑demo:
1 |
int get_path(struct task_struct *mytask, int fd) |
3 |
struct file *myfile = NULL; |
4 |
struct files_struct *files = NULL; |
5 |
char path[100] = {‘\0‘}; |
10 |
printk("files is null..\n"); |
13 |
myfile = files->fdt->fd[fd]; |
15 |
printk("myfile is null..\n"); |
18 |
ppath = d_path(&(myfile->f_path), ppath, 100); |
20 |
printk("path:%s\n", ppath); |
從上面的代碼可以看出,從fd到file結構的擷取均通過各個資料結構之間的指向關係擷取。
方法二
與方法一的思路相同,但是可以直接使用核心提供的函數fget()進行fd到file的擷取。這種方法使用比較簡單,程式更加安全,不過就是少了對資料結構關係的思考過程。其實也可以將fget()函數的實現過程作為參考,欣賞核心中代碼實現的嚴謹性。
Linux核心中通過檔案描述符擷取絕對路徑已關閉評論 »
發表在Linux核心編程
Tags: 核心編程 檔案系統 檔案路徑
libc庫和系統調用
2012年6月2日
Linux系統調用這部分經常出現兩個詞:libc庫和封裝函數,不知道你是否清楚它們的含義?
libc
libc是Standard C library的簡稱,它是符合ANSI C標準的一個標準函數庫。libc庫提供C語言中所使用的宏,類型定義,字串操作函數,數學計算函數以及輸入輸出函數等。正如ANSI C是C語言的標準一樣,libc只是一種函數庫標準,每個作業系統都會按照該標準對標準庫進行具體實現。通常我們所說的libc是特指某個作業系統的標準庫,比如我們在Linux作業系統下所說的libc即glibc。glibc是類Unix作業系統中使用最廣泛的libc庫,它的全稱是GNU C Library。
類Unix作業系統通常將libc庫作為作業系統的一部分,它被視為作業系統與使用者程式之間的介面。libc庫不僅實現標準C語言中的函數,而且也包含自己所屬的函數介面。比如在glibc庫中,既包含標準C中的fopen(),又包含類Unix系統中的open()。在類Unix作業系統中,如果缺失了標準庫,那麼整個作業系統將不能正常運轉。
與類Unix作業系統不同的是,Windows系統並不將libc庫作為整個核心作業系統的一部分。通常每個編譯器都附屬自己的libc庫,這些libc既可以靜態編譯到程式中,又可以動態編譯到程式中。也就是說應用程式依賴編譯器而不是作業系統。
封裝函數
在Linux系統中,glibc庫中包含許多API,大多數API都對應一個系統調用,比如應用程式中使用的介面open()就對應同名的系統調用open()。在glibc庫中通過封裝常式(Wrapper Routine)將API和系統調用關聯起來。API是標頭檔中所定義的函數介面,而位於glibc中的封裝常式則是對該API對應功能的具體實現。事實上,我們知道介面open()所要完成的功能是通過系統調用open()完成的,因此封裝常式要做的工作就是先將介面open()中的參數複製到相應寄存器中,然後引發一個異常,從而系統進入核心去執行sys_open(),最後當系統調用執行完畢後,封裝常式還要將錯誤碼返回到應用程式中。
需要注意的是,函數庫中的API和系統調用並沒有一一對應的關係。應用程式藉助系統調用可以獲得核心所提供的服務,像字串操作這樣的函數並不需要藉助核心來實現,因此也就不必與某個系統調用關聯。
不過,我們並不是必須通過封裝常式才能使用系統調用,syscall()和_syscallx()兩個函數可以直接調用系統調用。具體使用方法man手冊中已經說明的很清楚了。
參考:
1. http://en.wikipedia.org/wiki/Libc
2. man syscalls
通過call_usermodehelper()在核心態執行使用者程式【轉】