2017-11-25 2 views
4

현재 32 비트 (x86) 상자에서 실행되는 C 프로그램을 사용하는 seek과 관련된 소규모 전투가 있습니다.32 비트 시스템에서 매우 큰 파일 찾기

특히 겉으로보기에는 오히려 임의의 파일 오프셋을 넘어서서 탐색 할 수없는 것 같습니다.

내가 할 경우

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가 작동합니다. 난 그냥 ... 에 의해 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) 

내가 완전에로 잃었어요 왜. 내가 도대체 ​​뭘 잘못하고있는 겁니까?

내가 말할 수있는 유일한 중요한 것들은 seems to be related to time wraparound이라는 사실과 함께 재미있는 것 같다 - 입니다. 15032385535입니다.

문제의 프로그램이 -D_FILE_OFFSET_BITS=64으로 컴파일되고 있는데, 실제로 처음에 열어 본 작업에 큰 파일을 가져 오는 데 도움이되었지만 여기서는 유용한 차이점을 보이지 않는 것 같습니다. 나는 -DLARGEFILES -D_LARGEFILE_SOURCE을 우연히 만났고, 그것을 추가하려고 시도했지만 어떤 눈에 띄는 효과가 없었던 것 같습니다. 컨텍스트에 대한

 

(퀴즈는 재미 있기 때문에) : 나는 별도의 32 비트 컴퓨터 (완벽)과의 문제를 재현하는 truncate있는 대형 스파 스 파일을 만든; 문제의 프로그램은 작은 웹 서버입니다. 여분의 컴퓨터에서 데이터를 복사하려고하는데, 놀랍게도Range: 요청 및 동시 다운로드를 처리 할 수있는 컴팩트 한 웹 서버를 찾기가 어렵습니다. nginx가 Perl 오류를 던지고 있습니다 (슬랙웨어 패키징 문제 - nope). Python의 SimpleHTTPServer는 쓸데없이 간단하며, thttpd는 mmap 오류의 웅덩이에 녹아 있습니다. 재미있는 날 ...

+0

1 바이트가 14GB 미만인 것은 완전히 임의적 인 것이 아닙니다. 그건 그렇다. 이것이 일어날 이유가 확실하지 않습니다. – hnefatl

+1

'fseek()'의 두번째 인수는'long int'입니다. 이것은 전달 될 수있는 크기에 대한 특정 (구현에 따라 정의 된) 제한을 의미합니다. 간단히'unsigned long long '을 전달하는 것은 더 큰 값을 받아들이는 방법이 아닙니다. – Peter

+0

'unsigned long long'을'size_t' 또는'off_t'로 바꾸면'Invalid argument'도 생성됩니다. –

답변

5

숫자를 16 진수 (또는 2 진수)로 쓰면 더 쉽습니다.

15,032,385,535 = 0x37fffffff
15,032,385,536 시스템에 0x380000000 =

unsigned long 32 비트 타입이고 unsigned long long 64 비트 타입이다.

fseek의 두 번째 인수는 long입니다. 작성시

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

두 번째 인수의 값은 필수 유형으로 변환됩니다.부호없는 정수 유형을 더 작은 정수 유형 (여기 unsigned long long ~ unsigned long)으로 변환하면 값이 더 작은 유형을 오버 플로우 할 때 정의되지 않은 동작이 발생하지만 사용자를 포함하여 대부분의 플랫폼에서는 값의 최상위 비트가 잘립니다. 0xffffffffunsigned long의 최대 값이기 때문에 그것은

fseek(fd, pos & 0xffffffff, SEEK_SET) 

에 해당합니다. pos = 0x37fffffff 일 때 결과 값은 0x7fffffff = 2147483647입니다. fseek에 대한 호출이 실제로 작동하지 않습니다. 그것은 당신이 요청한 위치로 추구하지 않습니다.

pos = 0x380000000 일 때 다른 현상이 발생합니다. 자르기 값의 최상위 비트가 설정되고 부호 비트로 사용됩니다. 대부분의 컴퓨터와 마찬가지로 컴퓨터가 음수에 대해 two's complement 표현을 사용하기 때문입니다. 따라서 결과 값은 음수입니다. -0x80000000 = -2147483648입니다. 이 음수 값은 시스템 호출 _llseek으로 전달됩니다. 시스템 호출은 64 비트 값을 취합니다 (32 비트 시스템에서도). 수신하는 값은 -0x80000000이며, strace는 부호없는 2의 보수로 64 비트 숫자 (18446744071562067968 = 0xffffffff80000000)를 표시합니다.

표준 C를 사용하면 long 범위를 벗어나는 위치를 사용하여 파일을 검색 할 수 없습니다. POSIX 함수를 사용하고자한다면 이 있습니다. 이는 fseek과 유사하지만 long 대신 off_t 유형의 두 번째 인수를 취합니다. -D_FILE_OFFSET_BITS=64에서 off_t은 64 비트 유형입니다.

관련 문제