本文转载自:http://blog.chinaunix.net/uid-13059007-id-5766941.html

有时候需要在Linux kernel--大多是在需要调试的驱动程序--中读写文件数据。在kernel中操作文件没有标准库可用,需要利用kernel的一些函数,这些函数主 要有: filp_open() filp_close(), vfs_read() vfs_write(),set_fs(),get_fs()等,这些函数在linux/fs.h和asm/uaccess.h头文件中声明。下面介绍主 要步骤

  1. 打开文件

  filp_open()在kernel中可以打开文件,其原形如下:

  strcut file* filp_open(const char* filename, int open_mode, int mode);

  该函数返回strcut file*结构指针,供后继函数操作使用,该返回值用IS_ERR()来检验其有效性。

  参数说明

  filename: 表明要打开或创建文件的名称(包括路径部分)。在内核中打开的文件时需要注意打开的时机,很容易出现需要打开文件的驱动很早就加载并打开文件,但需要打开的文件所在设备还不有挂载到文件系统中,而导致打开失败。

  open_mode: 文件的打开方式,其取值与标准库中的open相应参数类似,可以取O_CREAT,O_RDWR,O_RDONLY等。

  mode: 创建文件时使用,设置创建文件的读写权限,其它情况可以匆略设为0

  2. 读写文件

  kernel中文件的读写操作可以使用vfs_read()和vfs_write,在使用这两个函数前需要说明一下get_fs()和 set_fs()这两个函数。

  vfs_read() vfs_write()两函数的原形如下:

  ssize_t vfs_read(struct file* filp, char __user* buffer, size_t len, loff_t* pos);

  ssize_t vfs_write(struct file* filp, const char __user* buffer, size_t len, loff_t* pos);

  注意这两个函数的第二个参数buffer,前面都有__user修饰符,这就要求这两个buffer指针都应该指向用户空间的内存,如果对该参数传 递kernel空间的指针,这两个函数都会返回失败-EFAULT。但在Kernel中,我们一般不容易生成用户空间的指针,或者不方便独立使用用户空间 内存。要使这两个读写函数使用kernel空间的buffer指针也能正确工作,需要使用set_fs()函数或宏(set_fs()可能是宏定义),如 果为函数,其原形如下:

  void set_fs(mm_segment_t fs);

  该函数的作用是改变kernel对内存地址检查的处理方式,其实该函数的参数fs只有两个取值:USER_DS,KERNEL_DS,分别代表 用户空间和内核空间,默认情况下,kernel取值为USER_DS,即对用户空间地址检查并做变换。那么要在这种对内存地址做检查变换的函数中使用内核 空间地址,就需要使用set_fs(KERNEL_DS)进行设置。get_fs()一般也可能是宏定义,它的作用是取得当前的设置,这两个函数的一般用 法为:

  mm_segment_t old_fs;

  old_fs = get_fs();

  set_fs(KERNEL_DS);

  ...... //与内存有关的操作

  set_fs(old_fs);

  还有一些其它的内核函数也有用__user修饰的参数,在kernel中需要用kernel空间的内存代替时,都可以使用类似办法。

  使用vfs_read()和vfs_write()最后需要注意的一点是最后的参数loff_t * pos,pos所指向的值要初始化,表明从文件的什么地方开始读写。

  3. 关闭读写文件

  int filp_close(struct file*filp, fl_owner_t id);

  该函数的使用很简单,第二个参数一般传递NULL值,也有用current->files作为实参的。

  使用以上函数的其它注意点:

  1. 其实Linux Kernel组成员不赞成在kernel中独立的读写文件(这样做可能会影响到策略和安全问题),对内核需要的文件内容,最好由应用层配合完成。

  2. 在可加载的kernel module中使用这种方式读写文件可能使模块加载失败,原因是内核可能没有EXPORT你所需要的所有这些函数。

  3. 分析以上某些函数的参数可以看出,这些函数的正确运行需要依赖于进程环境,因此,有些函数不能在中断的handle或Kernel中不属于任可进程的代码 中执行,否则可能出现崩溃,要避免这种情况发生,可以在kernel中创建内核线程,将这些函数放在线程环境下执行(创建内核线程的方式请参数 kernel_thread()函数)。

=========================================================================================

转载于: http://blogold.chinaunix.net/u3/113927/showart_2495807.html

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

linux内核驱动中对文件的读写 【转】的更多相关文章

  1. Windows内核驱动中操作文件

    本页主题:如何在windows内核驱动中对文件操作,实现对文件的拷贝.粘贴.删除.查询信息等,这是很常用也是很简单的方法. 部分内容参考:http://www.cppblog.com/aurain/a ...

  2. 嵌入式C语言自我修养 02:Linux 内核驱动中的指定初始化

    2.1 什么是指定初始化 在标准 C 中,当我们定义并初始化一个数组时,常用方法如下: ] = {,,,,,,,,}; 按照这种固定的顺序,我们可以依次给 a[0] 和 a[8] 赋值.因为没有对 a ...

  3. linux内核驱动中对字符串的操作【转】

    转自:http://www.360doc.com/content/12/1224/10/3478092_255969530.shtml Linux内核中关于字符串的相关操作,首先包含头文件: #inc ...

  4. linux内核驱动中_IO, _IOR, _IOW, _IOWR 宏的用法与解析

    在驱动程序里, ioctl() 函数上传送的变量 cmd 是应用程序用于区别设备驱动程序请求处理内容的值.cmd除了可区别数字外,还包含有助于处理的几种相应信息. cmd的大小为 32位,共分 4 个 ...

  5. linux内核驱动中_IO, _IOR, _IOW, _IOWR 宏的用法与解析(引用)

    在驱动程序里, ioctl() 函数上传送的变量 cmd 是应用程序用于区别设备驱动程序请求处理内容的值.cmd除了可区别数字外,还包含有助于处理的几种相应信息. cmd的大小为 32位,共分 4 个 ...

  6. linux内核驱动模型

    linux内核驱动模型,以2.6.32内核为例.(一边写一边看的,有点乱.) 1.以内核对象为基础.用kobject表示,相当于其它对象的基类,是构建linux驱动模型的关键.具有相同类型的内核对象构 ...

  7. Linux设备驱动中的阻塞和非阻塞I/O

    [基本概念] 1.阻塞 阻塞操作是指在执行设备操作时,托不能获得资源,则挂起进程直到满足操作所需的条件后再进行操作.被挂起的进程进入休眠状态(不占用cpu资源),从调度器的运行队列转移到等待队列,直到 ...

  8. Linux设备驱动中的阻塞和非阻塞I/O <转载>

    Green 博客园 首页 新随笔 联系 订阅 管理 Linux设备驱动中的阻塞和非阻塞I/O   [基本概念] 1.阻塞 阻塞操作是指在执行设备操作时,托不能获得资源,则挂起进程直到满足操作所需的条件 ...

  9. Linux内核驱动开发之KGDB原理介绍及kgdboe方式配置

    接博文<Linux内核驱动开发之KGDB单步调试内核(kgdboc方式)>.上篇博文中,仅简单介绍使用串口的Kgbd的流程(kgdboc方式),本文将重点介绍KGDB调试Linux内核的原 ...

随机推荐

  1. OS | Process

    linux多进程 1. fork()创建进程,创建一份父进程的拷贝:在父进程中返回的是子进程id,在子进程中返回的是0:失败时返回-1: 2. fork()经常和exec()结合,exec() 覆盖了 ...

  2. Xamarin.Forms的ActivityIndicator和ProgressBar比较

    Xamarin.Forms的ActivityIndicator和ProgressBar比较   在Xamarin.Forms中,控件ActivityIndicator和ProgressBar都用来表示 ...

  3. java 字符串压缩长度并解压

    package com.wy.data.emaildata.util; import org.apache.commons.io.FileUtils; import java.io.ByteArray ...

  4. 如何使用ssh远程编辑定时任务crontab?

    linxu定时任务使用crontab,编辑crontab可以直接编辑:crontab -e:也可以直接读取文件 crontab file.这两种操作都不需要特殊权限sudo.区别在于,crontab ...

  5. SSH 原理和公匙私匙

    先主要介绍了Telnet.SSH 的通信原理,分析了其通信时的工作流程. Telnet 无论Telnet协议连接的是什么类型终端,都会转换为NVT(Net Virtual Terminal)格式进行通 ...

  6. python matplotlib包图像配色方案

    可选的配色方案: Accent, Accent_r, Blues, Blues_r, BrBG, BrBG_r, BuGn, BuGn_r, BuPu, BuPu_r, CMRmap, CMRmap_ ...

  7. C 标准库 - <stddef.h>

    C 标准库 - <stddef.h> 简介 stddef .h 头文件定义了各种变量类型和宏.这些定义中的大部分也出现在其它头文件中. 库变量 下面是头文件 stddef.h 中定义的变量 ...

  8. ThinkPHP学习(五)图片验证码

    今天用到图片验证码的功能,在网上找到ThinkPHP的下面代码: Public function verify(){ import('think.Image'); Image::buildImageV ...

  9. vue2.0 自定义 提示框(Toast)组件

    1.自定义 提示框 组件 src / components / Toast / index.js /** * 自定义 提示框( Toast )组件 */ var Toast = {}; var sho ...

  10. 前端学习——使用Ajax方式POST JSON数据包

    0.前言     本文解释怎样使用Jquery中的ajax方法传递JSON数据包,传递的方法使用POST(当然PUT又有时也是一个不错的选择).POST JSON数据包相比标准的POST格式可读性更好 ...