fwrite()库函数为什么会吃内存?

ceasar1134 2008-08-19 09:29:42
以前没有注意这个问题,偶然发现自己的内存呼呼地往下降,最后定位在fwrite()函数上。

现象是这样的:
比如写一个300K的文件,内存就减少300K,直到减至一个稳定的值。(8M左右,我这里用的是128M的内存)
fwrite()调用成功返回后也不释放内存,除非我的程序停掉。

这是为什么呀,大家在写程序的时候,特别在写大文件的时候是像我这样
fwrite(buffer,1,size,pFILE);这样使用还是有其他技巧?
...全文
2985 27 打赏 收藏 转发到动态 举报
写回复
用AI写文章
27 条回复
切换为时间正序
请发表友善的回复…
发表回复
jiehui1008 2011-01-13
  • 打赏
  • 举报
回复
好贴......
zkylqh123 2008-08-21
  • 打赏
  • 举报
回复
学习了。。。
realdragon2 2008-08-21
  • 打赏
  • 举报
回复
mark~学习~
ceasar1134 2008-08-21
  • 打赏
  • 举报
回复
好了,谢谢大家参与。
封贴了...
fierygnu 2008-08-21
  • 打赏
  • 举报
回复
用错了工具。用valgrind、mtrace等来看是否有内存泄漏。如果用-g选项编译,可以给出具体的代码位置。

不能用sysinfo的返回信息判断是否有内存泄漏。freeram是free物理内存,所有的进程,包括内核都使用这些内存,所以它的数值变化不能说明你的进程的任何问题。fwrite的现象是内核为了提升disk访问性能,进行了page cache,不需要多虑。
快乐田伯光 2008-08-20
  • 打赏
  • 举报
回复
也不是很理解,但buffer跟cache不应该是一个东西, buffer是IO的范畴,cache的页面替换的范畴,两个不相干的概念
ceasar1134 2008-08-20
  • 打赏
  • 举报
回复 1
另外,下面是我的一点理解,大家帮我看看我理解的有没有错误:

(1)O_SYNC标志设定是体现write()操作的同步问题。如果设定了该标志,那么只有在写的内容的确从物理上写入存储设备,那么write()才返回;

(2)对我们经常看见的空间理解

用户空间(user-space) | 内核空间(incore-space) | Page cache | disk cache | disk | ram buffer
1.用户空间:fwrite()内申请的缓存内存空间。因为用户可以通过setbuffer()等函数对其进行设定;
2.内核空间:write()内申请的缓存内存空间。这个是OS为写操作申请的内存,对用户来说不可见。
3、4.Cache: 理解不是很深刻。不知道是物理cache还是可以占用内存的cache;
5.disk:这个就不说了。
6.buffer: 不是很理解,感觉它和Page cache是一个东西

(3)几个同步函数
1.fflush():The function fflush() forces a write of all user-space buffered data for the given output or update stream
via the stream's underlying write function. The open status of the stream is unaffected.
我的理解是该函数将fwrite()里面的缓存内容强行压入底层(内核空间?cache?disk?)。至于压倒哪里就不明白了,我猜是压入到了
write()的内核空间。
2.fsync(): fsync() transfers ("flushes") all modified in-core data of (i.e., modified buffer cache pages for) the file
referred to by the file descriptor fd to the disk device (or other permanent storage device) where that file
resides.
我的理解是该函数将write()里面内核空间(修改的部分)压入到磁盘设备。但是这里的buffer cache pages又不明白和内核空间的关系了
快乐田伯光 2008-08-20
  • 打赏
  • 举报
回复
按照15楼里引用的那段话, write跟fwrite的结果应该是一样的。是cache机制引起的,因为你频繁写硬盘,导致了大量的内存做为cache, 这个是操作系统的内部实现,我们应用程序基本可以不用担心,除非有什么其它的特殊要求或是限制
ceasar1134 2008-08-20
  • 打赏
  • 举报
回复
回楼上几位:
To:NC
write()和fwrite()的结果是一样的...

To:Walbing
谢谢你的测试。我试过你的O_SYNC的方法,诚然在开始阶段如你所说,但是过了一段时间后发生变化,可能是你监视的时间不够长。下面是按照你的方法的打印结果:
fuqd273 2008-08-20
  • 打赏
  • 举报
回复
向十五楼学习了
ceasar1134 2008-08-20
  • 打赏
  • 举报
回复
昨天晚上看女排了,没上论坛。今天我试试大家的方法...
ceasar1134 2008-08-20
  • 打赏
  • 举报
回复
看完15楼的连接终于明白了原因了。真是十分的感谢!!!!!
-------------------------------
重新整理一下我的思路:
(1)写操作的过程空间

用户空间(user-space) ¦ 内核空间(incore-space) ¦ buffer cache ¦ disk ¦
1.用户空间:fwrite()内申请的缓存内存空间。因为用户可以通过setbuffer()等函数对其进行设定;
2.内核空间:write()内申请的缓存内存空间。这个是OS为写操作申请的内存,对用户来说不可见。
3.buffer cache: 这个东东是造成我们内存减少的主要原因。不是我们的过错,是OS的特性;
5.disk:这个就不说了。

(2)同步问题
即使我们在fwrite()、write()后面使用了sync(),fsync()等同步操作,在内存中也会保留buffer cache的,所以内存不会减少...
-------------------------------
不过有个新问题,在存在这中情况下我们怎么查看我们程序的内存泄漏(本帖的出处就是我本来想看内存是否泄漏,才发现fwrite()的这个现象)?
莫非我们要将程序的所有“写”操作都屏蔽掉才能看出来是否内存泄漏了?
ceasar1134 2008-08-19
  • 打赏
  • 举报
回复
fflush()我也试过了,还是不可以
这是结果图片
ceasar1134 2008-08-19
  • 打赏
  • 举报
回复
第一种是块模式吧(block buffered)。
相关内容请见setbuffer的man手册(^_^,开个玩笑)
ceasar1134 2008-08-19
  • 打赏
  • 举报
回复
楼上
我在上面有一句屏蔽掉的:
//cOutFile.SetFwriteNoBuffer( );
在文件类中我是使用setvbuf(this->pfFile,(char*)NULL,_IONBF,0);来实现不实用缓存的。
但是测试下来对内存减少的情况还存在,几乎没有什么改变...

fflush()没有测试过,马上测试一下。
wlabing 2008-08-19
  • 打赏
  • 举报
回复
标准I/O有三种缓存形式:全局缓存,行缓存,无缓存.
fwrite函数应该属于全局缓存的,你可以将缓存设置小一些,缓存满了就会写到硬盘中了.
你也可以在fwrite之后,调用fflush函数,将缓存中的内容写到硬盘中.
相关内容请查看<<unix高级编程>>第5章的"标准I/O库""

#include <stdio.h>

void setbuf(FILE *restrict fp, char *restrict buf);

int setvbuf(FILE *restrict fp, char *restrict buf, int mode,size_t size);

int fflush(FILE *fp);

ceasar1134 2008-08-19
  • 打赏
  • 举报
回复
应该不是我没有释放内存,因为我写的下面的测试程序:

/************************************************
* test.cpp
************************************************/

#include "./FileClass.h"
#include "../type.h"
#include <unistd.h>
#include <sys/sysinfo.h>
#include <sys/types.h>

#define FWRITE_BUFFER_LENGTH 400*1024
int main(void)
{
CFile cInFile;
CFile cOutFile;
char* bfTemp = new char[400000];
char* bfFwrite = new char[FWRITE_BUFFER_LENGTH];
unsigned int uiFileLength=0;
unsigned int uiReadFileLength=0;
unsigned int uiOutFileIndex=1;
char szOutFileName[MAX_FILENAME_LENGTH];
struct sysinfo MySysinfo;
int iRet;

cInFile.SetFullFileName("./In.jpg");
cInFile.Open(BINARY_RWTYPE,POS_START);
uiFileLength = cInFile.GetFileLength();
uiFileLength = 314890;
iRet = cInFile.Read(bfTemp, uiFileLength, uiReadFileLength);
if(iRet==0 && uiFileLength==uiReadFileLength)
{
printf("== Read input file successfully\n");
}
else
{
printf("== Read input file failed\n== Read() return %d; File length is %d; Read number is %d\n",iRet,uiFileLength,uiReadFileLength);
return -1;
}
while(1)
{
snprintf(szOutFileName,MAX_FILENAME_LENGTH,"/EpData/OutFile%d.jpg",uiOutFileIndex++);
cOutFile.SetFullFileName(szOutFileName);
iRet = cOutFile.Open(BINARY_RWTYPE,POS_START);
//cOutFile.SetFwriteNoBuffer( );
if(iRet < 0)
{
printf("== cOutFile.Open() failed\n");
break;
}
iRet = cOutFile.Write(bfTemp,uiFileLength);
if(iRet < 0)
{
cOutFile.Close();
break;
}
cOutFile.Close();
sysinfo(&MySysinfo);
printf("== Free memory is %ldK\n",MySysinfo.freeram/1024);
sleep(1);
}

delete[] bfTemp;
delete[] bfFwrite;
cInFile.Close();

return 0;
}

cOutFile.Write()里面只做了一件事,就是fwrite(),没有申请任何内存,这样打印出来的内容还是内存不断减少
chenfeng2002 2008-08-19
  • 打赏
  • 举报
回复
没注意过这个问题,不过想象也是正常的,
相对来说写硬盘要慢,所以系统应该是等到写的总数达到一定数量时再写硬盘,比每fwrite()一次写一次硬盘然后释放内存效率高吧?
个人分析的,不知道对否,有待dx解答!~
快乐田伯光 2008-08-19
  • 打赏
  • 举报
回复
不会吧,你的buffer自己有没有去释放啊?
unilgr 2008-08-19
  • 打赏
  • 举报
回复
这应该是一个操作系统调度问题,而不是一个编程问题,我在Linux内核2.6.9(内存256MB)下做了测试:
按照LZ的那种写文件方式,确实有这个现象,不过top监控是进程本身的虚拟内存和物理内存消耗很少,基本很稳定,但是vmstat跟踪系统状态结果如下
[root@localhost ~]# vmstat 60
procs -----------memory---------- ---swap-- -----io---- --system-- ----cpu----
r b swpd free buff cache si so bi bo in cs us sy id wa
2 0 224 116320 4780 35520 0 0 59 125 537 216 3 5 91 1
0 0 224 94288 5096 57304 0 0 0 371 1072 486 5 6 88 1
0 0 224 70192 5432 81148 0 0 0 418 1057 351 2 3 95 0
0 0 224 46312 5764 104736 0 0 0 412 1058 326 1 3 96 0
0 0 224 21892 6284 128656 0 0 8 430 1071 395 2 3 94 1
0 0 224 18156 5264 133056 0 0 47 414 1087 531 3 4 91 1
0 0 224 17312 5284 133816 0 0 1 421 1132 574 3 4 92 0
0 0 224 18620 5252 132288 0 0 0 417 1058 372 2 3 95 0
可以看到memory下free和cache的变化趋势(间隔60秒),也就是系统空闲内存都用来做cache了,以提高写入磁盘的性能
从http://www.faqs.org/docs/linux_admin/buffer-cache.html摘录部分段落也说明了该问题
If the cache is of a fixed size, it is not very good to have it too big, either, because that might make the free memory too small and cause swapping (which is also slow). To make the most efficient use of real memory, Linux automatically uses all free RAM for buffer cache, but also automatically makes the cache smaller when programs need more memory.
Under Linux, you do not need to do anything to make use of the cache, it happens completely automatically. Except for following the proper procedures for shutdown and removing floppies, you do not need to worry about it.

加载更多回复(7)

23,124

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧