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

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

2012-03-05 20:18 316 查看
公司的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)时则不用加任何参数
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: