linux 处理大数据

来源:互联网 发布:王珂假富豪 知乎 编辑:程序博客网 时间:2024/05/16 01:06

Linux 系统编程 ---- 文件内存映射2010-10-12 11:03:25

 

分类: LINUX

 

Andrew Haung bluedrum@163.com 转载请注明作者及联络方式
 
 
 
 
 
 文件一般是用fopen/fwrite/fread/fclose或者 open/write/read/close 这种标准的文件I/O 来处理。但是有很多情况这种模式无法执行。
 
   考虑如下几种情况
 
      1.将一个文件的所有字符由小写变为大写
 
      2.打开一个超大文件,如视频,数据库。只是读写一下尾部。
 
      3.多线程同时下载一个文件
 
 
 
  这几种情况用普通的I/O操作均会有问题。这里得需要文件内存映射来操作了。
 
 
 
 一.Linux 文件I/O操作
 
---------------------------------------------------------------------------------
 
 
 
   首先我们回顾一下I/O操作,用于后面文件映射击的比对
 
    1.open 打开或创建一个文件
       打开一个只读文件
         int fd = open("myfile",O_RDONLY);  // FILE * file = fopen("myfile","r");
       打开一个可读可写文件
          int fd = open("myfile",O_RDWR); // FILE * file = fopen("myfile","w");
 
       新建可读可写的文件
           int fd = open("newfile",O_RDWR | O_CREAT);
                        // FILE * file = fopen("myfile","w+");
     

  2. close() 关闭文件
            int close(int fd);
 
  3.read() 从文件读入数据
        ssize_t read(int fd, void *buf, size_t count);
             //size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
 
  4.write()向文件写入数据
           ssize_t write(int fd, const void *buf, size_t count);
 
          //size_t   fwrite(const  void  *ptr,  size_t  size,  size_t  nmemb,  FILE
       *stream);
 

  5.lseek() 在文件移动。
        
        off_t lseek(int fildes, off_t offset, int whence);
          //whence 可以取 SEEK_SET(文件开始) SEEK_CUR(当前位置) SEEK_END(从尾部)
  
       文件大小.
            printf("file %d\n",lseek(fd,0,SEEK_END));

 
 6.调整文件尺寸到指定大小。
    int truncate(const char *path, off_t length);
    int ftruncate(int fd, off_t length);
 

  7.判断一个文件是否存在?
       access("myfile",F_OK); //判断一个文件存在
      
        access("myfile",F_OK|W_OK); //判断一个文件存在并且有可写的权限
 
 
 
   8. unlink /remove删除一个文件

 
二.Linux文件映射操作
 
---------------------------------------------------------------------------
 
 
 
使用Linux文件映射有如下固定步骤
 
 
 
    1.打开一个文件

        int open(const char *pathname, int flags);
       int open(const char *pathname, int flags, mode_t mode);

            name是文件名字,可以是绝对路径,也可是相对路径
 
            flag,文件属性.O_RDONLY,O_WRONLY ,O_RDWR,O_CREAT,O_EXCL
 
            mode 权限码
 
 
 
             

   2.取得文件长度
         int fstat(int filedes, struct stat *buf);

      fstat执行成功后,stat.st_size即是文件尺寸.
 
      
 
  3.用mmap 眏射到进程空间当中某一个地址上
 
       void *mmap(void *start, size_t length, int prot, int flags,
                  int fd, off_t offset);
 
 
 
          start是表示开始映射的物理地址,如果为NULL表示由内核自行选择合适空间来分配。
          length是内存的大小,一般是和第二步的同一大小。
 
          prot 是共享内存属性。它有如下值
 
                 PROT_EXEC 分配空间可执行
 
                 PROT_READ 分配空间可读
 
                 PROT_WRITE 可写
 
                 PROT_NONE 禁止访问,一般为省事,都设为  PROT_READ|PROT_WRITE
 
          flags 是共享内存的标志位,它有如下取值
 
                 MAP_FIXED ,内存固定大小,不能超过一页。如果超过将mmap失败.
 
                  MAP_SHARED ,在多个进程间共享这一内存
 
                  MAP_PRIVATE, 只供本进程使用。
 
 
 
          fd 是shm_open或open创建文件的描述符.
 
          offset 是在共享内存或文件中的偏移量。一般是0
 
 
 

         如果映射成功,将会返回一个进程内部地址。对这个地址访问即是对内核共享内存的访问。这个地址位于堆和栈的空闲区。
 
 
 
        如果失败,将返回MAP_FAILED (它等于 (void *)-1)
 
        

 
 
    到mmap后,对共享内存的操作就跟与普通内存没有什么区别了。如使用memcpy/memset等操作.
 
 
 
 
 
   如果结束的对共享内存使用,即可采用接下两步.
 
 
 
 
 
4.munmap解除当前进程对这块共享映射。
     int munmap(void *start, size_t length);
 
   

  start是映射的进程内地址,lenght是内存的长度
 
   如果映射是文件,它还有存盘功能。
 
 
 
 
 
 5.关闭文件     

   int close(int fd);

   
6.在内存映射中存盘
 
    int msync(void *start, size_t length, int flags);
     msync是不用解除内存映射的情况下,把内存文件保存到磁盘上。
 
 
 
二.文件映射实例,大写转小写
 
-------------------------------------------------------------------------------
 
这个例子里,test1()是用普通io函数来实现,必须使用一个中间文件。而test2()则直接采用内存映射处理
 
 
 
 
 
 
 
 
 

 

/*
   Author: Andrew Huang <bluedrum@163.com>
   
file upper to loweri
 */

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <signal.h>


#include <sys/mman.h> /* for mmap,shm_open */
#include <string.h>

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#include <unistd.h>

 


int file_to_lower(char * filename)
{
   int fd,tmp_fd;
   char ch;

    //创建一个中间文件做为缓存

    char * tmp_name ;

   printf("filename %s\n",filename);
  

   fd = open(filename,O_RDONLY);

   
    if(fd == -1)
      {
         perror("open");
       return -1;
      }

 
   // tmp_fd = mkstemp("/tmp/tfile.XXXXXX");


#if 1
    //tmp_name = mktemp("/tmp/tfile.XXXXXXXXXX");

    tmp_name = "/tmp/tfile.123456";

    printf("tmp file %s\n",tmp_name);
 
     tmp_fd = open(tmp_name,O_CREAT|O_RDWR|O_TRUNC);
#endif
     if(tmp_fd == -1)
        {
         perror("open2");
         return -2;
      }
 
  

    while(read(fd,&ch,sizeof(ch))>0)
     {
         ch = tolower(ch);
         write(tmp_fd,&ch,sizeof(ch));
     }

     close(fd);
   
    fd = open(filename,O_CREAT|O_RDWR);
     if(fd == -1)
      {
         perror("open3");
          return -3;
      }

     lseek(tmp_fd,0,SEEK_SET);
     while(read(tmp_fd,&ch,sizeof(ch))>0)
      {
         write(fd,&ch,sizeof(ch));
      }
    
    close(fd);
    close(tmp_fd);


    return 0;
}


void test1()
{
   file_to_lower("./1.txt");
}


typedef struct mmap_fd{
   int fd; /* 打开后的文件描述符*/
   void * base; /* 在进程内部的地址*/
   char * name; /* 共享内存名字 */
    int len ; /* 共享内存长度 */
    int offset; /* fd中的偏移量*/
}MMAP_FD;


#define MMAP_ADDR(p) (p)->base


//如果len =0 ,表示取文件本身

MMAP_FD * file_map_create(char * name,int len,int offset)
{
   MMAP_FD * p_fd;
   void * base;

   int fd;
   
  //第一步:打开一个文件

    fd = open(name,O_RDWR);
     if(fd == -1)
      {
            perror("open");
          return NULL;
      }

 
    //第二步:设置文件大小/取文件大小

       if(len == 0)
         {
             struct stat buf;
              if(fstat(fd,&buf) == -1)
                 {
                         perror("fstat");
                         close(fd);
                     return NULL;
                 }

              len = buf.st_size;
          }
       else if(ftruncate(fd,len) == -1)
          {
                 perror("ftruncate");
               close(fd);
               return NULL;
          }

    //第三步,将内核中共享内存映射到进程空间之上

  //void *mmap(void *start, size_t length, int prot, int flags,

// int fd, off_t offset);

      base = mmap(NULL,len,PROT_READ | PROT_WRITE, MAP_SHARED ,fd,offset);
       if(base == MAP_FAILED)
        {
             perror("mmap");
             close(fd);
              return NULL;
         }
      

        p_fd = malloc(sizeof(MMAP_FD));
         p_fd->len = len;
         p_fd->name = strdup(name);
         p_fd->fd = fd;
         p_fd->base = base;
         p_fd->offset = offset;

    return p_fd;
   
}


int file_map_close(MMAP_FD * p_fd)
{
   if(p_fd == NULL)
      return -1;
   //取消映射

   if(munmap(p_fd->base,p_fd->len) == -1)
      {
          perror("munmap");
           return -1;
      }

     close(p_fd->fd);

   free(p_fd->name);
   free(p_fd);

    return 0;

}

 

void test2()
{
   int i;
   char * p;
  MMAP_FD * p_fd = file_map_create("2.txt",0,0);

   p = (char *)p_fd->base;

  for(i=0 ; i < p_fd->len ; i++)
    {
        p[i] = tolower(p[i]);
    }

   file_map_close(p_fd);
}
int main()
{
  test2();
}
 

 

 


用C语言读取大文件的问题--内存映射

2011-08-24 10:58:16  www.hackbase.com  来源:互联网
Windows对文件的读写提供了很丰富的操作手段,如: 1. FILE *fp, fstearm...; (C/C++)2. CFile, CStdioFile...; (MFC)3. CreateFile, ReadFile...;(API)... 在处理一般的文件(文本/非文本),这些足够了。然而在 ...
Windows对文件的读写提供了很丰富的操作手段,如:
1. FILE *fp, fstearm...; (C/C++)
2. CFile, CStdioFile...; (MFC)
3. CreateFile, ReadFile...;(API)
...
在处理一般的文件(文本/非文本),这些足够了。然而在处理比较大的文件如
几十M, 几百M, 甚至上G的文件, 这时再用一般手段处理,系统就显的力不从心了
要把文件读出,再写进,耗费的是CPU利用率与内存以及IO的频繁操作。这显然是
令用户难以忍受的
为了解决这个吃内存,占CPU,以及IO瓶颈,windows核心编程提供了内存映射文件技术
(Maping File)
至于Maping File是什么原理,我不多说了,网上转载资源一箩筐,我只想从应用层
来考虑,怎样用这个技术,实现日常项目中的应用
举例来说:
可能项目中,会经常用到一些大量的常量,而这些大量常量用宏来替代写再源文件中
显然不可取,一般是写在文件中,给常量一些编号,通过编号来索引
一般文件比较小时候,常用做法也是先预读到内存中,毕竟从内存中读比从文件中读要快(IO操作的瓶颈)
比较好的做法,读到STL MAP 中去:
例如一个索引文件:
SEU07201213=一颗欲枯的草
FANG=方
SEU07201214=CSDN
............
打开文件,解析=号,在解析方面有CString操作,strtok,strstr, boost 正则表达式匹配等等,但我比较喜欢
sscanf(szIndex, "%[^=]=%[^=]", sName, sValue);
sscanf(szIndex, "%[^=]=%s", sName, sValue);
fscanf(stream, "%[^=]=%[^=]", sName, sValue);

之类,
然后再定义一个map:
map<string, string> m_Map;
m_Map[sName] = sValue;
但是文件比较大的时候,笔者做过测试,用上面方法处理一个15M, 25万行的文本文件,占用内存非常
的高,达70多M,处理的速度也非常的慢,这还不包括回写到文件
这时,Maping File就派上用场了,这里处理大文件就抛弃了map的应用(因为容器占用很多内存)
而是直接利用字符指针来操作,不用其他封装,不多说了,请看示例:
 
 
#pragma warning(disable: 4786) 
#include <windows.h>
#include <stdio.h>
#include <iostream>
#include <string>
 
using namespace std;
 
string GetValue(const TCHAR *, const TCHAR *);  //根据name得value
void main(int argc, char* argv[])
{
    // 创建文件对象(C: est.tsr)
    HANDLE hFile = CreateFile("C:/test.tsr", GENERIC_READ | GENERIC_WRITE,0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        printf("创建文件对象失败,错误代码:%d ", GetLastError());
        return;
    }
    // 创建文件映射对象
    HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
    if (hFileMap == NULL)
    {
        printf("创建文件映射对象失败,错误代码:%d ", GetLastError());
        return;
    }
    // 得到系统分配粒度
    SYSTEM_INFO SysInfo;
    GetSystemInfo(&SysInfo);
    DWORD dwGran = SysInfo.dwAllocationGranularity;
    // 得到文件尺寸
    DWORD dwFileSizeHigh;
    __int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
    qwFileSize |= (((__int64)dwFileSizeHigh) << 32);
    // 关闭文件对象
    CloseHandle(hFile);
    // 偏移地址 
    __int64 qwFileOffset = 0;
    // 块大小
    DWORD dwBlockBytes = 1000 * dwGran;
    if (qwFileSize < 1000 * dwGran)
        dwBlockBytes = (DWORD)qwFileSize;
    if (qwFileOffset >= 0)
    {
        // 映射视图
        TCHAR *lpbMapAddress = (TCHAR *)MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS, 
            0, 0,
            dwBlockBytes);
        if (lpbMapAddress == NULL)
        ...{
            printf("映射文件映射失败,错误代码:%d ", GetLastError());
            return;
        }
 
        
//-----------------------访问数据开始-------------------------
        cout<<GetValue(lpbMapAddress,"SEU07201213")<<endl;
        getchar();
//-----------------------访问数据结束-------------------------        
    
        // 撤销文件映像
        UnmapViewOfFile(lpbMapAddress);
    }
    // 关闭文件映射对象句柄
    CloseHandle(hFileMap);    
}
string GetValue(const TCHAR *lpbMapAddress, const TCHAR *sName)
{
   string sValue;  // 存放 = 后面的value值
  TCHAR *p1 = NULL, *p2 = NULL; // 字符指针
  if((p1 = strstr(lpbMapAddress,sName)) != NULL) // 查找sName出现位置
  {
   if(p2 = strstr(p1,"/r/n")) *p2 = '/0'; // 查找"/r/n"(换行)出现位置
   sValue = p1+strlen(sName)+strlen("="); // 指针移动"sName"+"="之后
   *p2 = '/r';  // 还原*p2值,因为不还原会改变原文件结构
  }
  return sValue;
}

 


LINUX
#include <unistd.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <string.h>
 5 #include <fcntl.h>
 6 #include <linux/fb.h>
 7 #include <sys/mman.h>
 8 #include <sys/ioctl.h>
 9
10 #define PAGE_SIZE 4096
11
12
13 int main(int argc , char *argv[])
14 {
15     int fd;
16     int i;
17     unsigned char *p_map;
18    
19     //打开设备
20     fd = open("/dev/mymap",O_RDWR);
21     if(fd < 0)
22     {
23         printf("open fail\n");
24         exit(1);
25     }
26
27     //内存映射
28     p_map = (unsigned char *)mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
29     if(p_map == MAP_FAILED)
30     {
31         printf("mmap fail\n");
32         goto here;
33     }
34
35     //打印映射后的内存中的前10个字节内容
36     for(i=0;i<10;i++)
37         printf("%d\n",p_map[i]);
38    
39
40 here:
41     munmap(p_map, PAGE_SIZE);
42     return 0;
43 }

原创粉丝点击