linux 内核 下 读写 文件


  1. #include <linux/kernel.h>  
  2. #include <linux/module.h>  
  3. #include <linux/init.h>  
  4. #include <linux/fs.h>  
  5. #include <linux/string.h>  
  6. #include <linux/mm.h>  
  7. #include <linux/syscalls.h>  
  8. #include <asm/unistd.h>  
  9. #include <asm/uaccess.h>  
  10.   
  11. #define MY_FILE "/root/LogFile"  
  12.   
  13. char buf[128];  
  14. struct file *file = NULL;  
  15.   
  16. static int __init init(void)  
  17. {  
  18.         mm_segment_t old_fs;  
  19.         printk("Hello, I'm the module that intends to write messages to file.\n");  
  20.   
  21.   
  22.         if(file == NULL)  
  23.                 file = filp_open(MY_FILE, O_RDWR | O_APPEND | O_CREAT, 0644);  
  24.         if (IS_ERR(file)) {  
  25.                 printk("error occured while opening file %s, exiting...\n", MY_FILE);  
  26.                 return 0;  
  27.         }  
  28.   
  29.         sprintf(buf,"%s", "The Messages.");  
  30.   
  31.         old_fs = get_fs();  
  32.         set_fs(KERNEL_DS);  
  33.         file->f_op->write(file, (char *)buf, sizeof(buf), &file->f_pos);  
  34.         set_fs(old_fs);  
  35.         return 0;  
  36. }  
  37.   
  38. static void __exit fini(void)  
  39. {  
  40.         if(file != NULL)  
  41.                 filp_close(file, NULL);  
  42. }  
  43.   
  44. module_init(init);  
  45. module_exit(fini);  
  46. MODULE_LICENSE("GPL");  


其中:

[plain]  view plain  copy
  1.  typedef struct {  
  2.          unsigned long seg;  
  3. } mm_segment_t;  
  4.   
  5. #define KERNEL_DS    MAKE_MM_SEG(0xFFFFFFFFUL)  
  6. #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())改变了用户空间的限制,即扩大了用户空间范围,因此即可使用在内核中的参数了。


转载:http://blog.csdn.net/mike8825/article/details/50906429

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值