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

时间:2017-11-25 00:01:23

标签: c 32bit-64bit large-files seek

我目前正在与seek进行一场小型战斗,并在32位(x86)盒子上运行C程序。

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

如果我这样做:

unsigned long long pos = 15032385535LLU;
int r = fseek(fd, pos, SEEK_SET);

然后我会得到

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

TL; DR 它有效。

但是,如果我通过 1 增加pos ...

unsigned long long pos = 15032385536LLU;
int r = fseek(fd, pos, SEEK_SET);

......然后一切都崩溃了:

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)

我完全迷失了原因。我做错了什么?

我能想到的唯一重要花絮是15032385535 37FFFFFFF似乎很有趣,以及数字seems to be related to time wraparound

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

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

1 个答案:

答案 0 :(得分:5)

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

15032385535 = 0x37fffffff
15032385536 = 0x380000000

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

long的第二个参数的类型为unsigned long long pos = …; int r = fseek(fd, pos, SEEK_SET); 。当你写

unsigned long long

将第二个参数的值转换为必需类型。将无符号整数类型转换为较小的整数类型 - 此处为unsigned longfseek(fd, pos & 0xffffffff, SEEK_SET) - 当值溢出较小的类型时具有未定义的行为,但在大多数平台上,包括您的平台,它会截断最重要的位价值。它相当于

0xffffffff

因为unsigned longpos = 0x37fffffff的最大值。当fseek时,结果值为0x7fffffff = 2147483647.请注意,对pos = 0x380000000的调用实际上不起作用!它没有寻求你认为你要求的职位。

_llseek时,会发生另一种现象:设置截断值中最重要的位,并将其用作符号位,因为您的计算机(与大多数计算机一样)使用two's complement表示形式为负数数字。因此得出的值是负的;它是-0x80000000 = -2147483648。然后将此负值传递给系统调用long,该调用采用64位值(即使在32位系统上)。它收到的值是-0x80000000,strace显示为二进制补码无符号对应的64位数字 - 18446744071562067968 = 0xffffffff80000000。

使用标准C,您无法使用超出fseeko范围的位置搜索文件。如果您愿意依赖POSIX函数,则fseekoff_t类似,但采用long类型的第二个参数而不是-D_FILE_OFFSET_BITS=64。在off_t下,ProgressDialog progress = new ProgressDialog(getApplicationContext()); progress.setTitle("In Progress"); progress.show(); AsyncTask.execute(new Runnable() { @Override public void run() { //Do things. runOnUiThread(new Runnable() { @Override public void run() { //Update UI. } }); //Do things. runOnUiThread(new Runnable() { @Override public void run() { progress.dismiss(); } }); } }); progress.setOnDismissListener(newDialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialogInterface) { //Called after your background task ended. } }); 是64位类型。