#include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/syscalls.h> #include <asm/unistd.h> #include <asm/uaccess.h>
#define MY_FILE "/root/LogFile"
char buf[128]; struct file *file = NULL;
static int __init init(void) { mm_segment_t old_fs; printk("Hello, I'm the module that intends to write messages to file.\n");
if(file == NULL) file = filp_open(MY_FILE, O_RDWR | O_APPEND | O_CREAT, 0644); if (IS_ERR(file)) { printk("error occured while opening file %s, exiting...\n", MY_FILE); return 0; }
sprintf(buf,"%s", "The Messages.");
old_fs = get_fs(); set_fs(KERNEL_DS); file->f_op->write(file, (char *)buf, sizeof(buf), &file->f_pos); set_fs(old_fs);
return 0; }
static void __exit fini(void) { if(file != NULL) filp_close(file, NULL); }
module_init(init); module_exit(fini); MODULE_LICENSE("GPL");
其中: typedef struct { unsigned long seg; } mm_segment_t;
#define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFFUL)
#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) })
基本思想: 一個(gè)是要記得編譯的時(shí)候加上-D__KERNEL_SYSCALLS__ 另外源文件里面要#include <linux/unistd.h> 如果報(bào)錯(cuò),,很可能是因?yàn)槭褂玫木彌_區(qū)超過(guò)了用戶空間的地址范圍。一般系統(tǒng)調(diào)用會(huì)要求你使用的緩沖區(qū)不能在內(nèi)核區(qū),。這個(gè)可以用set_fs(),、get_fs()來(lái)解決。在讀寫(xiě)文件前先得到當(dāng)前fs: mm_segment_t old_fs=get_fs(); 并設(shè)置當(dāng)前fs為內(nèi)核fs:set_fs(KERNEL_DS); 在讀寫(xiě)文件后再恢復(fù)原先f(wàn)s: set_fs(old_fs); set_fs(),、get_fs()等相關(guān)宏在文件include/asm/uaccess.h中定義,。 個(gè)人感覺(jué)這個(gè)辦法比較簡(jiǎn)單。 另外就是用flip_open函數(shù)打開(kāi)文件,,得到struct file *的指針fp,。使用指針fp進(jìn)行相應(yīng)操作,如讀文件可以用fp->f_ops->read,。最后用filp_close()函數(shù)關(guān)閉文件,。 filp_open()、filp_close()函數(shù)在fs/open.c定義,,在include/linux/fs.h中聲明,。
解釋一點(diǎn): 系統(tǒng)調(diào)用本來(lái)是提供給用戶空間的程序訪問(wèn)的,所以,,對(duì)傳遞給它的參數(shù)(比如上面的buf),,它默認(rèn)會(huì)認(rèn)為來(lái)自用戶空間,,在->write()函數(shù)中,為了保護(hù)內(nèi)核空間,,一般會(huì)用get_fs()得到的值來(lái)和USER_DS進(jìn)行比較,,從而防止用戶空間程序“蓄意”破壞內(nèi)核空間;
而現(xiàn)在要在內(nèi)核空間使用系統(tǒng)調(diào)用,,此時(shí)傳遞給->write()的參數(shù)地址就是內(nèi)核空間的地址了,,在USER_DS之上(USER_DS ~ KERNEL_DS),如果不做任何其它處理,,在write()函數(shù)中,,會(huì)認(rèn)為該地址超過(guò)了USER_DS范圍,所以會(huì)認(rèn)為是用戶空間的“蓄意破壞”,,從而不允許進(jìn)一步的執(zhí)行,; 為了解決這個(gè)問(wèn)題; set_fs(KERNEL_DS);將其能訪問(wèn)的空間限制擴(kuò)大到KERNEL_DS,這樣就可以在內(nèi)核順利使用系統(tǒng)調(diào)用了,!
補(bǔ)充: 我看了一下源碼,,在include/asm/uaccess.h中,有如下定義: #define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) #define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFF) #define USER_DS MAKE_MM_SEG(PAGE_OFFSET) #define get_ds() (KERNEL_DS) #define get_fs() (current->addr_limit) #define set_fs(x) (current->addr_limit = (x))
而它的注釋也很清楚: /* * The fs value determines whether argument validity checking should be * performed or not. If get_fs() == USER_DS, checking is performed, with * get_fs() == KERNEL_DS, checking is bypassed. * * For historical reasons, these macros are grossly misnamed. */
因此可以看到,,fs的值是作為是否進(jìn)行參數(shù)檢查的標(biāo)志,。系統(tǒng)調(diào)用的參數(shù)要求必須來(lái)自用戶空間,所以,,當(dāng)在內(nèi)核中使用系統(tǒng)調(diào)用的時(shí)候,,set_fs(get_ds())改變了用戶空間的限制,即擴(kuò)大了用戶空間范圍,,因此即可使用在內(nèi)核中的參數(shù)了,。
|