日期:2014-05-16  浏览次数:20656 次

探索Linux内核空间文件IO实现

探索Linux内核空间文件IO实现
2011年05月04日
  http://blog.csdn.net/wby0322/archive/2010/11/11/6002249.aspx
  本文参考《深入理解Linux内核》完成,详情请查阅相关书籍
  前言
  将磁盘上的文件读入到内存中,将内存中的一段数据写到磁盘的文件上或另一个存储设备。看似很简单的事情,却有一些出人意料的东西蕴含其中。此篇文章介简单绍了在用户空间与内核空间两个不同内存区域下访问文件的方式。
  误区1:C程序语言利用标准C函数库中的相关函数可以实现文件的打开关闭读写等操作,因此可以随意使用。
  解释:linux操作系统的内存管理将内存空间划分为用户态和内核态(即常规的1G大小的内核空间和3G大小的用户空间)。标准C函数库中的文件操作函数只能运行在用户空间,无权访问到内核空间。因此在诸如驱动一类的内核程序中,要实现文件的操作,是不可以使用标准C函数库中的函数的。而应该使用内核提供的文件操作函数。
  误区2:为了在内核中实现标准函数库中的函数,编译时候试图将stdio.h等标准函数的头文件加入到内核的include中。
  注释:你即使这么做也是不会成功的,这些标准函数在用户空间编程时可以使用,但是是无法编译到内核中的。与其实相类似的功能,内核已经提供了足够多函数调用来实现。日后再遇到标准函数库的问题,要首先去查询一下内核是否提供了你想实现的功能函数。
  误区3:include/linux文件夹中的头文件是通用的
  注释:include中的头文件可以说大部分是通用的,但是也有相当一部分不是通用的,是与体系结构密切相关的,比如说锁操作,比如说内存与I/O操作。这些函数在底层的实现都是汇编一级的,而汇编代码本身就与体系结构密切相关。设计到与体系结构相关的头文件,往往优先使用存储在arch/xxx/lnclude/asm中的。另外建议熟悉一下lnclude的文件夹构成,有些头文件并不放在include/linux文件夹下面。
  接下来介绍在用户空间下访问文件和在内核空间下访问文件的两种不用类型操作集。
  第一部分 用户空间下的文件访问
  (参考谭浩强 《C语言程序设计》――第十二章 文件,相关概念讲解的比较详细)
  用户空间文件操作函数注释
  读写文件的常规流程
  第1步:打开文件
  第2步:将文件读入到一段内存中
  第3步:将一段内存中的数据写入到另一个文件中。
  常见的文件类型:字符型 or 二进制型
  在C语言中,上面的流程,可以由以下四个函数集合来实现
  ◆ 打开文件:fopen(file_path,flag);
  ◆ 获取文件大小:stat(file_path,&file); (long)file.st_size;
  ◆ 读取文件到内存中:fread(buf,file_size,count,fp);
  ◆ 将内存中的文件写入到文件:fwrite(buf,file_size,count,fp);
  函数 fopen(file_path,flag)
  功能:打开一个文件
  函数原型:fopen(file_path,flag)
  返回值:指向所打开文件的文件指针
  参数:
  file_path:此处为文件所在绝对路径(包含文件名)
  flag:读写标志位,只读"r"、只写"w"、追加"a"、只读(二进制) "rb"、只写(二进制) "wb"、追加(二进制) "wb"、读写(先读后写) "r+"、读写(先写后读) "w+"、读写(追加) "a+"、读写(二进制先读后写) "rb+"、读写(二进制先写后读) "wb+"、读写(二进制追加) "ab+".
  示例:
  FILE *fp; //用户空间定义一个文件类型的指针
  fp = fopen(/root/test.txt, "r"); //以只读方式打开文件/root/test.txt,并将文件指针返回给fp.函数 stat(file_path,&file)
  功能之一:获取文件大小
  参数:
  file_path:文件路径
  &file:stat类型的结构体的地址,用来保存文件信息
  示例:
  int filesize;
  struct stat file; //定义一个stat类型的结构体变量 file
  stat(file_path,&file); //取得文件信息并将其保存在结构体file中
  filesize=(long)file.st_size; //stat类型的结构变量file中的成员st_size记录着文件大小,我们获取它并将其赋给filesize,供其它函数使用。函数 fread(buf,file_size,count,fp)
  功能:将文件读取到内存中的一个地方
  参数:
  buf:开辟的一块内存空间首地址,将用来存放读入的文件
  file_size:指定读入的字节数
  count:读入指定字节数的次数
  fp:读入的文件的文件指针(你要读的那个文件的文件指针,由fopen获得)
  示例
  buf = (int *)malloc(file_size+1000);
  //在用户空间申请一块内存,大小为将要读取的文件大小(由st_size获得),且预留1000字节的边界。并返回内存的首地址。以供fread使用。//
  fread(buf,file_size,1,fp); //将fp指向的文件,大小为file_size,读入到内存中,保存在内存中的地址为buf,且读入一次。函数 fwrite(buf,file_size,count,fp)
  功能:将内存中的一段数据写入到文件中
  参数:
  buf:将要被写入到文件的那段内存的起始地址
  file_size:数据写入大小
  count:按file_size大小的写入次数
  fp:被写入的文件的文件指针
  示例
  fwrite(buf,file_size,1,fp); //将内存中位置在buf的一段数据,写入到fp所指向的文件中,写入大小为file_size,写入次数为1次。在用户空间获取文件大小有很多种方式,这里选用了stat函数。读写文件也有多种操作函数,这里选用的函数fread fwrite主要以二进制形式对文件进行操作。关于fopen的flag参数,是否为二进制影响不大。
  示例代码
  Eclipse上跑通的代码如下。因为涉及的参数比较多,为了清楚地重现重要步骤,对每步骤的函数进行了简单的封装。参数的传递只要理解上面的介绍即可区分清楚。
  /*
  * hello_file.c
  *
  * Created on: 2010-11-9
  * Author: Wang BaoYi(zats)
  * Email:wby0322@gmail.com
  */
  #include
  #include
  #include
  /* read_file_size封装了获取文件大小的函数
  * 参数为文件路径(包含文件名)。
  * 返回值为文件真实大小,单位字节,长整型。
  */
  static long int read_file_size(char *file_path)
  {
  struct stat file;
  stat(file_path,&file);
  return (long)file.st_size;
  }
  /*read_file_to_mem封装了读取文件到内存的函数
  *参数为(读入文件大小,文件路径(包含文件名))。
  *返回值为存储该文件的内存指针。
  */
  void *read_file_to_mem(int file_size,char *file_path)
  {
  int *buf;
  FILE *fp;
  if((fp = fopen(file_path,"r")) == NULL) //判断文件是否打开    {
  printf("Can not open this file\n");
  exit(0);
  }
  else
  {
  buf = (in