https://blog.csdn.net/bingqingsuimeng/article/details/8133529
本人做流媒体开发的,工作中需要将mpg文件转化成一定格式发送到网络,由于文件太大,程序用mmap()来实现文件的读取,但在实测过程中发现一个大问题,如下:
服务器配置:
CPU:两颗至强3.0
内存:2G
磁盘:3个SATA硬盘组成磁盘阵列
测试时同时运行30个播放程序读取30个不同的mpg文件,程序起初运行画面播放非常流畅,几分钟过后,内存剩下15MB左右时,mmap()就开始不停 的进行页面置换,将新的数据读入内存,老的数据置换出去,这时的磁盘利用率不到1%,但CPU耗在iowait上的时间却有90%多
各位大侠我该怎么办,如果不用内存映射还有没有其他的办法处理大文件???
开个4G的swap分区挂上去试试看
或者,把文件分段mmap(),如100M的文件做10次mmap(),并且要求在播放1段完前做好下段准备工作
你挂mmap不释放怎么行.
你mmap一个大文件, 要在这个大文件播放完后才能释放.
要把大文件分小
你可以分段映射试试看,比如一次映射2M,并跟踪程序在这个映射内的使用情况,如果这此映射的数据快用完时,就提前映射下一段.在上一段用完后,就释放掉其映射.
今天又做了一下测试,先映射整个文件,在使用过程中一段一段释放,还是阻塞在IOWait,另外挂载4G交换分区的方法也试了,效果更差
在国外论坛上看了些IOWait的东东,有很多都说红帽企业版+Xeons处理器+磁盘阵列容易发生IOWait,和我现在的配置一模一样,用单独的大文件拷贝就能测得出来,明天下个新内核编译一下试试看
问题已解决,好高兴^_^
问题原因:
调用mmap()时内核只是建立了逻辑地址到物理地址的映射表,并没有映射任何数据到内存。
在你要访问数据时内核会检查数据所在分页是否在内存,如果不在,则发出一次缺页中断,linux默认分页为4K,可以想象读一个将近2G的电影文件要发生多少次中断,I can't bear it!!!
解决办法:
将madvise()和mmap()搭配起来使用,在使用数据前告诉内核这一段数据我要用,将其一次读入内存,现在程序可以并发150个数据流了,每秒最高可读70MB数据
感谢大家的支持,另外还要特别感谢老兵哥在“timer frequency”问题上的支持,那个问题还有待研究,周末了,祝大家愉快。
++++++++++++++++++++++++++++++++++++++++++++++++++++++
今天在封装一个"内存映射文件"读取的类。编写WINDOWS版本的时候发现CreateFile()函数中,可以在参数dwFlagsAndAttributes中指定FILE_FLAG_SEQUENTIAL_SCAN来优化顺序读的情况。那么,在LINUX中如何优化顺序读呢?
查了一下资料,发现madvise()这个函数可以对映射的内存提出使用建议,从而提高内存。
以下是我进行对比测试的代码,可以明显的发现:使用MADV_SEQUENTIAL标志比使用MADV_RANDOM标志更快。
以下是代码:
=============================================================
//test_advise.cpp
//测试madvise()函数的差异
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifndef P
#define P(format, ...) do \
{ \
fprintf(stdout, "%s %s %d " format "\n", __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
fflush(stdout); \
} \
while (0);
#endif
static int operator-(struct timeval& lsh, struct timeval& rsh)
{
if (lsh.tv_sec==rsh.tv_sec)
{
return lsh.tv_usec - rsh.tv_usec;
}
else
{
return (lsh.tv_sec-rsh.tv_sec)*1000000 + (lsh.tv_usec - rsh.tv_usec);
}
}
#define FILE_LEGNTH (200*1024*1024) /*200MB的文件*/
/// 创建一个大的空文件
void CreateFile()
{
int fd = open("test.dat", O_CREAT | O_RDWR);
if (fd<1)
{
P("open error");
return;
}
lseek(fd, FILE_LEGNTH-1, SEEK_SET);
write(fd, "\0", 1);
close(fd);
fd = -1;
}
/// 顺序读
void SequenceRead()
{
int fd = open("test.dat", O_RDWR | O_EXCL);
if (-1==fd)
{
P("open error");
return;
}
void* p = mmap(NULL, FILE_LEGNTH, PROT_READ, MAP_SHARED, fd, 0);
if (MAP_FAILED==p)
{
P("map error");
return;
}
close(fd);
fd = -1;
//内存使用建议
if (-1==madvise(p, FILE_LEGNTH, MADV_WILLNEED | MADV_SEQUENTIAL))
{
P("madvise error");
return;
}
//下面开始逐个字符读取
struct timeval start;
struct timeval end;
int i;
unsigned char* pTemp = (unsigned char*)p;
int nSum = 0;
gettimeofday(&start, NULL);
for (i=0; i
{
nSum += *pTemp;
pTemp++;
}
gettimeofday(&end, NULL);
//
if (-1==munmap(p, FILE_LEGNTH))
{
P("unmap error");
return;
}
//显示占用时间
struct tm stTime;
localtime_r(&start.tv_sec, &stTime);
char strTemp[40];
strftime(strTemp, sizeof(strTemp)-1, "%Y-%m-%d %H:%M:%S", &stTime);
printf("start=%s.%07d\n", strTemp, (int)start.tv_usec);
//
localtime_r(&end.tv_sec, &stTime);
strftime(strTemp, sizeof(strTemp)-1, "%Y-%m-%d %H:%M:%S", &stTime);
printf("end =%s.%07d\n", strTemp, (int)end.tv_usec);
printf("spend=%d 微秒\n", end-start);
}
/// 随机读
void RandomRead()
{
int fd = open("test.dat", O_RDWR | O_EXCL);
if (-1==fd)
{
P("open error");
return;
}
void* p = mmap(NULL, FILE_LEGNTH, PROT_READ, MAP_SHARED, fd, 0);
if (MAP_FAILED==p)
{
P("map error");
return;
}
close(fd);
fd = -1;
//内存使用建议
if (-1==madvise(p, FILE_LEGNTH, MADV_RANDOM))
{
P("madvise error");
return;
}
//下面开始逐个字符读取
struct timeval start;
struct timeval end;
int i;
unsigned char* pTemp = (unsigned char*)p;
int nSum = 0;
gettimeofday(&start, NULL);
for (i=0; i
{
nSum += *pTemp;
pTemp++;
}
gettimeofday(&end, NULL);
//
if (-1==munmap(p, FILE_LEGNTH))
{
P("unmap error");
return;
}
//显示占用时间
struct tm stTime;
localtime_r(&start.tv_sec, &stTime);
char strTemp[40];
strftime(strTemp, sizeof(strTemp)-1, "%Y-%m-%d %H:%M:%S", &stTime);
printf("start=%s.%07d\n", strTemp, (int)start.tv_usec);
//
localtime_r(&end.tv_sec, &stTime);
strftime(strTemp, sizeof(strTemp)-1, "%Y-%m-%d %H:%M:%S", &stTime);
printf("end =%s.%07d\n", strTemp, (int)end.tv_usec);
printf("spend=%d 微秒\n", end-start);
}
int main()
{
CreateFile();
RandomRead();
SequenceRead();
return 1;
}
/*
g++ -o test_advise.o -c test_advise.cpp -g -Wall -Werror
g++ -o test_advise test_advise.o
*/
=============================================================
需要注意的是,当没有使用madvise的时候,相当于使用madvise(... MADV_NORMAL),这种情况下仍然会进行预读,性能和使用madvise(... MADV_SEQUENTIAL)差不多。我猜想,使用MADV_SEQUENTIAL标志的时候,对于实际的物理内存的占用将是最小的,关于这点,希望内核高手能够给我些指点,谢谢!