在32位系统上寻找非常大的文件

i33*_*36_ 4 c seek large-files 32bit-64bit

我目前正在seek与一个在32位(x86)盒子上运行的C程序进行小规模的斗争.

具体来说,我似乎无法超越看似相当随意的文件偏移.

如果我做:

unsigned long long pos = 15032385535LLU;
int r = fseek(fd, pos, SEEK_SET);
Run Code Online (Sandbox Code Playgroud)

然后我会的

fstat64(3, {st_mode=S_IFREG|0644, st_size=1000000000000, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77c3000
_llseek(3, 2147479552, [2147479552], SEEK_SET) = 0
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4095) = 4095
Run Code Online (Sandbox Code Playgroud)

TL; DR可行.

但是,如果我增加pos的只是1 ...

unsigned long long pos = 15032385536LLU;
int r = fseek(fd, pos, SEEK_SET);
Run Code Online (Sandbox Code Playgroud)

......然后一切都惊人地崩溃了:

fstat64(3, {st_mode=S_IFREG|0644, st_size=1000000000000, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb771e000
_llseek(3, 18446744071562067968, 0xbfd0f5f8, SEEK_SET) = -1 EINVAL (Invalid argument)
Run Code Online (Sandbox Code Playgroud)

我完全迷失了原因.我究竟做错了什么?

唯一的显著花絮我能想出的是,1503238553537FFFFFFF,似乎有趣十岁上下,与数量的事实一起似乎与时间环绕.

有问题的程序正在编译中-D_FILE_OFFSET_BITS=64,这对于实际获取我正在开放的大型文件实际上是有帮助的,但似乎并没有在这里产生有用的差异.我偶然发现-DLARGEFILES -D_LARGEFILE_SOURCE并尝试添加,但似乎没有任何明显的效果.

 

对于上下文(因为琐事很有趣):我创建了一个大的稀疏文件,truncate用于在一台单独的32位机器上完美地重现该问题(完美); 和有问题的程序是一个小型的Web服务器-我想将一些数据复制掀起了备用计算机,而且我发现,这是令人惊讶的,很难找到能够处理的小型Web服务器Range:的请求,并同时下载.nginx抛出Perl错误我不打算调查(Slackware打包问题 - 不行),Python的SimpleHTTPServer简单无用,并且thttpd溶解成一堆mmap错误.有趣的一天...

Gil*_*il' 5

如果用十六进制(或二进制)编写数字,则更容易理解.

15032385535 = 0x37fffffff
15032385536 = 0x380000000

在您的系统上,unsigned long是32位类型,unsigned long long是64位类型.

第二个参数fseek有类型long.当你写作

unsigned long long pos = …;
int r = fseek(fd, pos, SEEK_SET);
Run Code Online (Sandbox Code Playgroud)

第二个参数的值转换为必需的类型.将无符号整数类型转换为较小的整数类型(此处unsigned long longunsigned long- )在值溢出较小类型时具有未定义的行为,但在大多数平台(包括您的平台)上,它会截断该值的最高有效位.它相当于

fseek(fd, pos & 0xffffffff, SEEK_SET)
Run Code Online (Sandbox Code Playgroud)

因为0xffffffff是最大值unsigned long.当pos = 0x37fffffff,结果值为0x7fffffff = 2147483647.注意fseek实际调用不起作用!它没有寻求你认为你要求的职位.

何时pos = 0x380000000发生另一种现象:截断值中的最高位被设置,并且它被用作符号位,因为您的机器(像大多数机器一样)使用二进制补码表示负数.因此得出的值是负的; 它是-0x80000000 = -2147483648.然后将此负值传递给系统调用_llseek,该调用采用64位值(即使在32位系统上).它收到的值是-0x80000000,strace显示为二进制补码无符号对应的64位数 - 18446744071562067968 = 0xffffffff80000000.

使用标准C,您无法使用超出范围的位置搜索文件long.如果你愿意依赖POSIX函数,那就是fseeko,它是类似的,fseek但是采用类型的第二个参数off_t而不是long.在-D_FILE_OFFSET_BITS=64,off_t是64位类型.