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 }
- linux 处理大数据
- Blob处理大数据
- Clob处理大数据
- 大数据的处理
- java 处理大数据
- 处理大数据
- 单机处理大数据
- 大数据表格处理
- python 处理大数据
- 大数据排序处理
- 大数据算法处理
- 大数据的处理
- MATLAB 处理大数据
- python处理大数据
- 大数据的处理
- InternetReadFile 处理大数据
- 处理大对象数据-处理CLOB数据
- 处理大数据对象 -处理BLOB数据
- dma
- __cdecl
- 反编译APK获取java代码与图片,字符串资源,xml文件等资源
- 杭电2041
- coderforce 282A Bit++
- linux 处理大数据
- 嵌入式学习之旅
- java调用 COM接口基本配置操作
- Windows的进程间通信的方式
- struts1里怎么把值传到jsp里面?
- C++基本类型所占位数和取值范围
- 调用系统相机拍照,拍照后获取照片
- 目标检测中背景建模方法
- 最近业余研究arduino和树莓派