在linux核心中操作檔案的方法–使用get_fs()和set_fs(get_ds())

來源:互聯網
上載者:User
#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())改變了使用者空間的限制,即擴大了使用者空間範圍,因此即可使用在核心中的參數了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.