Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1136582
  • 博文数量: 573
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 66
  • 用 户 组: 普通用户
  • 注册时间: 2016-06-28 16:21
文章分类

全部博文(573)

文章存档

2018年(3)

2016年(48)

2015年(522)

分类: LINUX

2015-12-04 16:10:38

 

Linux内核态下的文件操作

在VFS的支持下,用户态进程读写任何类型的文件系统都可以使用read和write着两个系统调用,但是在linux内核中没有这样的系统调用我们如何操作文件呢?我们知道read和write在进入内核态之后,实际执行的是sys_read和sys_write,但是查看内核源代码,发现这些操作文件的函数都没有导出(使用EXPORT_SYMBOL导出),也就是说在内核模块中是不能使用的,那如何是好?

 

通过查看sys_open的源码我们发现,其主要使用了do_filp_open()函数,该函数在fs/namei.c中,而在改文件中,filp_open函数也是调用了do_filp_open函数,并且接口和sys_open函数极为相似,调用参数也和sys_open一样,并且使用EXPORT_SYMBOL导出了,所以我们猜想该函数可以打开文件,功能和open一样。使用同样的查找方法,我们找出了一组在内核中操作文件的函数,如下:

 

功能 函数原型
打开文件 struct file *filp_open(const char *filename, int flags, int mode)
读取文件 ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
写文件 ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
关闭文件 int filp_close(struct file *filp, fl_owner_t id)

 

我们注意到在vfs_read和vfs_write函数中,其参数buf指向的用户空间的内存地址,如果我们直接使用内核空间的指针,则会返回-EFALUT。所以我们需要使用
set_fs()和get_fs()宏来改变内核对内存地址检查的处理方式,所以在内核空间对文件的读写流程为:

  1. mm_segment_t fs = get_fs();
  2. set_fs(KERNEL_FS);
  3. //vfs_write();
  4. vfs_read();
  5. set_fs(fs);

下面为一个在内核中对文件操作的例子:


  1. #include  
  2. #include  
  3. #include   
  4. #include   
  5. #include   
  6. #include   
  7. #include   
  8. static char buf[] = "good study wang";  
  9. static char buf1[10];  
  10.    
  11. int __init hello_init(void)  
  12. {  
  13.     struct file *fp;  
  14.     mm_segment_t fs;  
  15.     loff_t pos;  
  16.     printk("hello enter\n");  
  17.     fp = filp_open("/home/niutao/kernel_file", O_RDWR | O_CREAT, 0644);  
  18.     if (IS_ERR(fp)) {  
  19.         printk("create file error\n");  
  20.         return -1;  
  21.     }  
  22.     fs = get_fs();  
  23.     set_fs(KERNEL_DS);  
  24.     pos = 0;  
  25.     vfs_write(fp, buf, sizeof(buf), &pos);  
  26.     pos = 0;  
  27.     vfs_read(fp, buf1, sizeof(buf), &pos);  
  28.     printk("read: %s\n", buf1);  
  29.     filp_close(fp, NULL);  
  30.     set_fs(fs);  
  31.     return 0;  
  32. }  
  33. void __exit hello_exit(void)  
  34. {  
  35.     printk("hello exit\n");  
  36. }  
  37.    
  38. module_init(hello_init);  
  39. module_exit(hello_exit);  
  40.    
  41. MODULE_LICENSE("GPL");  

使用以上函数的其它注意点:
1. 其实Linux Kernel组成员不赞成在kernel中独立的读写文件(这样做可能会影响到策略和安全问题),对内核需要的文件内容,最好由应用层配合完成。
2. 在可加载的kernel module中使用这种方式读写文件可能使模块加载失败,原因是内核可能没有EXPORT你所需要的所有这些函数。
3. 分析以上某些函数的参数可以看出,这些函数的正确运行需要依赖于进程环境,因此,有些函数不能在中断的handle或Kernel中不属于任可进程的代码中执行,否则可能出现崩溃,要避免这种情况发生,可以在kernel中创建内核线程,将这些函数放在线程环境下执行(创建内核线程的方式请参数kernel_thread()函数)。



阅读(471) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~