#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 "/tmp/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(get_ds()); //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 exit(void){ if(file != NULL) filp_close(file, NULL);}module_init(init);module_exit(exit);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) })
基本思想:
一個是要記得編譯的時候加上-D__KERNEL_SYSCALLS__
另外源檔案裡面要#include <linux/unistd.h>
如果報錯,很可能是因為使用的緩衝區超過了使用者空間的位址範圍。一般系統調用會要求你使用的緩衝區不能在核心區。這個可以用set_fs()、get_fs()來解決。在讀寫檔案前先得到當前fs:
mm_segment_t old_fs=get_fs();
並設定當前fs為核心fs:set_fs(KERNEL_DS);
在讀寫檔案後再恢複原先fs: set_fs(old_fs);
set_fs()、get_fs()等相關宏在檔案include/asm/uaccess.h中定義。
個人感覺這個辦法比較簡單。
另外就是用flip_open函數開啟檔案,得到struct file *的指標fp。使用指標fp進行相應操作,如讀檔案可以用fp->f_ops->read。最後用filp_close()函數關閉檔案。 filp_open()、filp_close()函數在fs/open.c定義,在include/linux/fs.h中聲明。
解釋一點:
系統調用本來是提供給使用者空間的程式訪問的,所以,對傳遞給它的參數(比如上面的buf),它預設會認為來自使用者空間,在->write()函數中,
為了保護核心空間,一般會用get_fs()得到的值來和USER_DS進行比較,從而防止使用者空間程式“蓄意”破壞核心空間;
而現在要在核心空間使用系統調用,此時傳遞給->write()的參數地址就是核心空間的地址了,在USER_DS之上(USER_DS ~ KERNEL_DS),如果不做任何其它處理,在write()函數中,會認為該地址超過了USER_DS範圍,所以會認為是使用者空間的“蓄意破壞”,從 而不允許進一步的執行; 為瞭解決這個問題; set_fs(KERNEL_DS);將其能訪問的空間限制擴大到KERNEL_DS,這樣就可以在核心順利使用系統調用了!
補充:
我看了一下源碼,在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的值是作為是否進行參數檢查的標誌。系統調用的參數要求必須來自使用者空間,所以,當在核心中使用系統調用的時候,set_fs(get_ds())改變了使用者空間的限制,即擴大了使用者空間範圍,因此即可使用在核心中的參數了。