Linux C编程第七回 - 文件内存映射



这次再次讨论文件的操作,只是对象换成了大型文件。

之所以讨论大型文件,是因为无论当今的热点“大数据处理”,还是老牌的搜索引擎,都会涉及到大型文件的处理效率。

比如前一张我们生成了50000行的日志,那么如果是300万行,甚至更多的时候,我们前一张提到的函数是否还能达到我们能够接受的效率呢。

统计日志行数,分别用LinuxAPI, C标准库实现,可以看到直接调用API,耗时72秒,用C标准库用时20,因为标准库使用了缓存,所以速度上快了三倍。

当我们换成最后一种方法的时候,文件内存映射,速度再次提高了一个级别,不到1秒。相当惊艳的成绩。

 

头文件:<sys/mman.h>

void *mmap( void *addr, size_t length, int prot, int flag, int fd, off_t offset );

第一个参数addr,表示自己制定的映射后的内存地址,一般不指定,除非你想制定确定的内存地址。

第二个参数,内存映射的大小,这里传入的是文件的大小,因为文件不是很大,所以全部大小,如果文件太大,可以分次分批映射。

第三个参数代表权限,PROT_READ/ PROT_WRITE/ PROT_EXEC

第四个参数flag, 代表映射的方式,MAP_PRIVATE / MAP_SHARED 这个参数主要是多个进程映射时使用,主要是对写入进行保护。PRIVATE代表自己又自己的备份。

最后一个参数代表文件的便宜,和第二个参数搭配使用可以分阶段映射大文件。

 

文件映射之所以快,是因为采用了系统中的虚拟内存技术。我们的内存一般都比硬盘小,当内存不足的时候,系统会使用硬盘来做交换分区,对页进行换入换出。虚拟内存既然可以使用硬盘来作为内存的备份,这就为文件映射提供了很好的实现方式。只需要把文件本身当作我们的交换分区,那么随时通过交换分区的算法(页中断算法)靠硬件来完成对文件的读取操作。


 

#include <stdio.h> #include <time.h> #include <fcntl.h> /* [chenndao@localhost log]$ gcc -o logreadapi logreadapi.c [chenndao@localhost log]$ ./logreadapi 3000000.txt Finished, total 3000000 lines Cost time: 72 sec [chenndao@localhost log]$ */

int main( int argc, char *argv[] ) {   //Caculate how many lines in the log file.   if ( argc != 2 )     {       printf("usage: \n readlog log.txt\n");       return 0;     }   int lineNum = 0;   char buff[256] = { 0 };   time_t timeStart = time( NULL );   char *data = NULL;   int fd = open( argv[1], O_RDONLY );   if ( fd > 0 )     {       int len = 0;       do  {    len = read( fd, buff, 256 );    if ( len > 0 )      {        int i = 0;        for( ; i<len; i++)   {     if ( buff[i] == '\n' )       {         lineNum++;       }   }      }  }       while ( len > 0 );       printf("Finished, total %d lines\n", lineNum);       printf("Cost time: %d sec\n", time(NULL)-timeStart);     }   else     {       printf("Open file %s error\n", argv[1]);     }

  return 1; }

#include <stdio.h> #include <time.h> /* [chenndao@localhost log]$ ./logread 3000000.txt Finished, total 3000000 lines Cost time: 20 sec */

int main( int argc, char *argv[] ) {   //Caculate how many lines in the log file.   if ( argc != 2 )     {       printf("usage: \n readlog log.txt\n");       return 0;     }   int lineNum = 0;   char buff[256] = { 0 };   time_t timeStart = time( NULL );   char *data = NULL;   FILE *fd = fopen( argv[1], "r" );   if ( fd != NULL )     {       int len = 0;       do  {    data = fgets( buff, 256, fd );    if ( data != NULL )      {        int i = 0;        for( ; i<256; i++)   {     if ( buff[i] == '\n' )       {         lineNum++;         break;       }   }      }  }       while ( data );       printf("Finished, total %d lines\n", lineNum);       printf("Cost time: %d sec\n", time(NULL)-timeStart);     }   else     {       printf("Open file %s error\n", argv[1]);     }

  return 1; }

 

#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <sys/mman.h>

/*
[chenndao@localhost log]$ ./maplogread 3000000.txt
Finished, total lines is 3000000 
total costed time 0 sec
[chenndao@localhost log]$ ./maplogread 3000000.txt
Finished, total lines is 3000000 
total costed time 1 secs
*/

int main( int argc, char *argv[] )
{
  if ( argc != 2 )
    {
      printf("usage: \n maplogread 50000.txt\n");
      return 0;
    }
  
  char *memory = NULL;
  int file_length = 0;
  char *start_address = 0;
  int line_num = 0;
  int time_start = time(NULL);
  int fd = open( argv[1], O_RDONLY );
  if ( fd > 0 )
    {
      file_length = lseek(fd, 1, SEEK_END);
      memory = mmap( start_address, file_length, PROT_READ, MAP_SHARED, fd, 0 );

      int i=0;
      for ( ; i<file_length; i++ )
	{
	  if ( memory[i] == '\n' )
	    {
	      ++line_num;
	    }
	}
      close( fd );
      munmap( memory, file_length );
      printf("Finished, total lines is %d \n", line_num);
      printf("total costed time %d sec\n", time(NULL) - time_start);
    }
  return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值