您的位置:首页 > 运维架构 > Linux

linux 32位平台,文件大小受限于2G的解决方法

2013-08-23 19:24 239 查看
公司的asterisk系统已经发生了两次crash,检查日志,都是在日志文件写满到2G后自动执行转储时,日志还在写继续写入而导致的。google以后,发现了下面这边文章,赞!
解决了文件大小限于2G的问题,转帖到自己的空间保留。

突破Linux上面ftell函数2GB的文件大小限制
http://www.demix.cn/h?z=28507 在 32 位元的 Linux 上面写超过 2GB 的档案会发生错误,甚至导致程式终止执行。
这是因为 Linux 的系统内部处理档案时用的指标定义为 long,而 long 在 32 位元的系统上的大小为 32 位元,因此最大只能支援 2^31-1 = 2,147,483,647 bytes 等於是 2GB 扣掉 1 byte 的档案大小
64 位元的系统 (例如 AMD64 或 IA64) 则因为 long 定义成 64 位元,所以不会有问题..
# if __WORDSIZE == 64
typedef long int int64_t;
# endif
不过在 FreeBSD 上面,即使是 32 位元的系统,也不会有 2GB 档案大小的限制,这是因为 FreeBSD 内部处理档案时,本来就是使用 64 位元的数字当作指标,所以不会有问题
因此在 32 位元的 Linux 上面,程式需要作一些额外处理才能正确写超过 2GB 的档案
我们先写一个小程式来测试一下 (large.c)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
void sig_xfsz(int sig)
{
printf("ERROR: SIGXFSZ (%d) signal received!\n", sig);
}
int main()
{
int i, fd;
char dummy[4096];

signal( SIGXFSZ, sig_xfsz );

unlink("large.log");
fd = open("large.log", O_CREAT|O_WRONLY, 0644 );

bzero( dummy, 4096 );
/* 2GB = 4KB x 524288 */
for( i = 0 ; i < 524287 ; i++ )
write( fd, dummy, 4096 );
write( fd, dummy, 4095 );
printf("large.log: 2147483647 bytes\n");

if( write( fd, dummy, 1 ) < 0 )
printf("ERROR: %s [errno:%d]\n",strerror(errno),errno);
else
printf("large.log: 2147483648 bytes\n");

close(fd);
exit(0);
}
在 32 位元的 Linux 下面,以上程式编译后若没有特殊处理,执行结果如下:
# gcc -o large32 large.c
# ./large32
large.log: 2147483647 bytes
ERROR: SIGXFSZ (25) signal received!
ERROR: File too large [errno:27]
在写第 2147483648 byte 的时候,程式会收到 signal SIGXFSZ,同时 write() 会回传 -1 错误,errno 则为 27 (File too large)。更甚者,如果程式没有像上面一样去处理 SIGXFSZ 的话,内定的 signal handler 甚至会造成程式停止执行并产生 core dump
接下来,我们在编译同一个程式的时候加入 -D_FILE_OFFSET_BITS=64 再试看看:
# gcc -D_FILE_OFFSET_BITS=64 -o large64 large.c
# ./large64
large.log: 2147483647 bytes
large.log: 2147483648 bytes
果然顺利突破 2GB 的限制了!
而同样的程式在 32 位元的 FreeBSD 下面,不论有没有加这个定义,跑起来都是正确的
不过处理这些大档案的时候,除了编译程式时的参数不同外,有些函数的使用上也要作一些调整,例如 fseek() 与 ftell() 这两个原本使用到 long integer 当作 offset 的函数:
int fseek(FILE *stream, long offset, int whence);
long ftell(FILE *stream);
只要系统是 32 位元,即使是在 FreeBSD 下面,都需要改为使用 off_t 的版本:
int fseeko(FILE *stream, off_t offset, int whence);
off_t ftello(FILE *stream);
在 Linux 下面,如果 _FILE_OFFSET_BITS 定义为 64,则 off_t 这个型态会自动转成 64 位元的大小(在 FreeBSD 上面,off_t 本来就是 64 位元的大小)
每种系统支援大於 2GB 的档案读写所需要的编译选项都会有一些差异,即使是同样是 Linux 也会因为 32 位元或 64 位元而有不同。有一个简单的方法可以判断,就是利用 glibc 提供的 getconf 来取得编译(compile)以及连结(linking)时所需的参数:
# getconf LFS_CFLAGS
-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
# getconf LFS_LDFLAGS 

#
上面是在 32 位元的 Redhat Linux 上面跑出来的结果,代表的是在这个系统上,若要让程式支援 2GB 的档案读写,编译(compile)时需要加上 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 这两个参数,连结(linking)时则不用加任何参数
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: