사전 EOVERFLOW
에러코드

EOVERFLOW

gabury1고친 사람 github-actions[bot]

EOVERFLOW 는 운영체제가 값을 건네주지 못했을 때 알려 주는 오류 값입니다. 값은 멀쩡합니다. 그 값을 받을 프로그램 쪽 변수가 너무 작습니다. 운영체제는 숫자를 잘라 틀린 값을 주는 대신 호출을 실패로 끝냅니다. 아주 큰 파일의 크기나 2038년 이후의 시각을 읽을 때 주로 만납니다.

쉽고 빠른 이해

EOVERFLOW 는 「값이 너무 커서 받으실 변수에 안 들어갑니다」라는 답입니다. 오래된 32비트 프로그램이 3기가바이트짜리 파일의 크기를 물으면 이 답이 옵니다. 32비트 변수로 받는 파일 크기는 약 2기가바이트가 끝이기 때문입니다.

이 오류 값이 없으면 운영체제는 넘치는 숫자를 잘라서 건네야 합니다. 그러면 프로그램은 파일 크기를 엉뚱한 값으로 받고도 모릅니다. 실패로 알려 주면 프로그램이 틀린 값을 쓰기 전에 멈춥니다.

어떻게 도는가:

  1. 프로그램이 파일 크기나 현재 시각을 묻습니다
  2. 운영체제가 그 값이 프로그램 쪽 변수에 들어가는지 봅니다
  3. 안 들어가면 값을 건네지 않고 실패(-1)를 돌려줍니다
  4. 실패한 까닭으로 이 오류 값을 남깁니다

대가가 있습니다. 다시 시도해도 풀리지 않습니다. 값이 저절로 줄어들 일이 없어서입니다. 받는 변수의 타입을 넓혀 다시 빌드해야 풀립니다.

상세

이 절은 EOVERFLOW 가 어떤 값이고 왜 생기는지를 봅니다. 큰 파일의 크기를 읽는 장면과 2038년 이후의 시각을 읽는 장면, 두 가지로 보입니다.

errno 와 오류 값

커널은 운영체제의 핵심입니다. 파일과 메모리 같은 자원을 쥐고 관리합니다. 프로그램은 이 자원이 필요하면 커널에 부탁해야 합니다.

시스템 콜은 그 부탁을 보내는 호출입니다. 파일 정보를 묻는 stat() 과 현재 시각을 묻는 time() 이 그런 호출입니다.

이런 호출은 실패하면 대개 -1 을 돌려줍니다. -1 만으로는 무엇이 잘못됐는지 알 수 없습니다. 그래서 errno 라는 정수 변수에 실패한 까닭을 따로 담아 둡니다.

errno 에 담기는 이 값이 오류 값입니다. 오류 값마다 이름이 붙어 있습니다. EOVERFLOW 는 그 이름 가운데 하나입니다. 뜻은 「Value too large to be stored in data type」, 곧 값이 너무 커서 받는 변수에 담을 수 없다는 것입니다.

오류 이름은 POSIX(Portable Operating System Interface)가 정합니다. POSIX 는 유닉스 계열 운영체제가 함께 따르는 표준입니다. 그래서 EOVERFLOW 는 유닉스 계열 어디서나 같은 뜻입니다.

다만 이름에 붙는 숫자 값은 운영체제마다 다릅니다. 리눅스 안에서도 아키텍처마다 다를 수 있습니다. 아키텍처는 중앙 처리 장치(CPU, Central Processing Unit)의 종류입니다. 그래서 코드는 숫자 대신 EOVERFLOW 라는 이름으로 비교합니다.

정수 오버플로

프로그램은 값을 변수로 받습니다. 그 변수가 얼마나 큰 값을 담을 수 있는지는 변수의 데이터 타입이 정합니다. 정수 타입은 정해진 비트 수만큼만 담습니다. 부호 있는 32비트 정수가 담을 수 있는 가장 큰 수는 2,147,483,647 입니다.

그보다 큰 수를 억지로 담으면 넘친 부분이 잘려 나가 엉뚱한 수가 됩니다. 이것을 정수 오버플로라고 부릅니다. EOVERFLOW 라는 이름의 OVERFLOW 가 이 넘침입니다.

받는 변수의 타입

EOVERFLOW 가 알리는 넘침은 계산하다 생긴 넘침이 아닙니다. 운영체제는 값을 넉넉한 크기로 들고 있습니다. 모자란 쪽은 그 값을 받는 변수의 타입입니다.

프로그램은 결과를 담을 변수를 미리 마련해 호출에 넘깁니다. 그 변수의 타입이 작으면 운영체제는 값을 옮겨 담을 수 없습니다. 이때 값을 잘라 넣지 않습니다. 호출을 -1 로 끝내고 errno 에 EOVERFLOW 를 담습니다.

잘라 넣지 않는 데는 까닭이 있습니다. 3기가바이트 파일의 크기를 32비트 변수에 잘라 넣으면 엉뚱한 값이 됩니다. 프로그램은 그 값을 믿고 파일을 덜 읽거나 계산을 틀립니다. 실패로 알리면 틀린 값이 퍼지기 전에 멈춥니다.

넘치기 쉬운 세 값

이 소절은 EOVERFLOW 를 낳기 쉬운 값 셋을 봅니다. 백엔드 개발자가 만나기 쉬운 것은 파일 정보와 시각입니다.

값마다 담는 타입이 따로 있습니다. 타입 이름 끝의 _t 는 type 의 머리글자입니다. 운영체제가 용도별로 이름을 붙여 둔 정수 타입이라는 표시입니다.

표에 나올 inode 번호는 파일 시스템이 파일마다 붙이는 번호입니다. 파일 이름이 바뀌어도 이 번호로 같은 파일을 가립니다.

값 담는 타입 이 값을 묻는 호출
파일 크기 off_t stat()
inode 번호 ino_t stat()
1970년 1월 1일 이후 흐른 초 time_t time()

파일 크기와 시각은 아래 두 장면에서 봅니다. inode 번호도 같은 식으로 넘칩니다. 파일 시스템이 32비트로 못 담을 만큼 큰 번호를 붙인 파일이 있다고 합시다. ino_t 가 32비트인 프로그램이 그 파일에 stat() 을 부르면 EOVERFLOW 로 실패합니다.

32비트로 빌드한 오래된 프로그램에서는 이 타입들이 32비트일 수 있습니다. 64비트로 빌드한 프로그램에서는 대개 64비트라서 넘칠 일이 거의 없습니다.

다만 프로그램의 비트 수와 타입의 폭은 따로 정해집니다. 32비트로 빌드한 프로그램이어도 이 타입들만 64비트로 넓힐 수 있습니다. 아래 두 장면의 처방이 이것입니다.

32비트 프로그램과 큰 파일

첫 장면은 큰 파일입니다. 32비트로 빌드한 프로그램은 파일 크기를 32비트 off_t 로 받을 수 있습니다. 부호가 있으니 담을 수 있는 가장 큰 크기는 2,147,483,647 바이트, 약 2기가바이트입니다.

이 프로그램이 그보다 큰 파일에 stat() 을 부르면 EOVERFLOW 로 실패합니다. 아래는 3기가바이트 파일 big.tar 의 정보를 물은 경우입니다.

C
struct stat st;
int r = stat("big.tar", &st);  // -1
int e = errno;           // EOVERFLOW

r 은 실패를 뜻하는 -1 입니다. 그 까닭이 e 에 담긴 EOVERFLOW 입니다. 파일이 없어서 실패한 것이 아닙니다.

고치는 길은 off_t 를 64비트로 넓혀 다시 빌드하는 것입니다. 리눅스에서 32비트로 빌드할 때는 컴파일러에 -D_FILE_OFFSET_BITS=64 를 넘겨 이렇게 합니다. -D 는 컴파일러에 설정값 하나를 넘기는 옵션입니다. 프로그램은 여전히 32비트입니다. off_t 만 64비트로 넓어집니다.

2038년 이후의 시각

둘째 장면은 시각입니다. 시각은 협정 세계시를 기준으로 셉니다. 협정 세계시는 세계가 함께 쓰는 기준 시각입니다. 나라마다 시간대가 달라도 이 기준 하나로 맞춥니다.

유닉스 계열 운영체제는 1970년 1월 1일 0시 협정 세계시부터 흐른 초의 수로 시각을 셉니다. 이 수를 유닉스 시간이라고 부릅니다. 시각을 숫자 하나로 들고 있으면 비교와 계산이 쉽습니다.

time_t 가 부호 있는 32비트면 2,147,483,647 초까지만 셉니다. 그 끝이 2038년 1월 19일 03:14:07 협정 세계시입니다. 이 끝에 부딪히는 일을 2038 문제라고 부릅니다.

32비트 time_t 로 빌드한 프로그램을 64비트 커널에서 돌린다고 해 봅시다. 64비트 커널은 2038년을 넘긴 시각도 바르게 셉니다. 그래서 그 시각을 32비트 time_t 에 건넬 수 없다는 것을 알아챕니다. 시각이 2038년 1월 19일 03:14:08 협정 세계시를 지나면 time() 이 EOVERFLOW 로 실패할 수 있습니다.

이 조합이 아니면 이야기가 다릅니다. 시각이 time_t 범위를 벗어날 때 어떻게 될지 정해져 있지 않습니다. 언제나 EOVERFLOW 로 알려 준다고 기대할 수는 없습니다.

시각도 처방이 같습니다. time_t 를 64비트로 넓혀 다시 빌드합니다.

다시 해도 안 풀리는 실패

EOVERFLOW 는 기다린다고 풀리지 않습니다. 잠시 뒤 다시 부르라는 EAGAIN(Resource temporarily unavailable, 자원을 잠시 쓸 수 없음)과 다릅니다. 파일 크기도 시각도 저절로 줄지 않기 때문입니다.

그래서 이 값을 보면 재시도 대신 프로그램의 빌드를 봅니다. 32비트로 빌드됐는지, 크기와 시각을 담는 타입이 몇 비트인지를 확인합니다.

「너무 크다」를 말하는 이웃 오류 값

errno 에는 「너무 크다」를 말하는 값이 몇 더 있습니다. 무엇이 너무 큰지로 가릅니다.

값 뜻 무엇이 너무 큰가
EOVERFLOW Value too large to be stored in data type 운영체제가 건넬 값이 받는 변수의 타입보다 큼
EFBIG File too large 파일이 허용 크기를 넘어 자라려 함
ERANGE Result too large 함수가 계산하거나 변환한 결과가 돌려줄 타입의 범위를 넘음
E2BIG Argument list too long 프로그램을 띄울 때 넘긴 인자 목록이 김

가장 헷갈리는 짝은 EFBIG 입니다. EFBIG 는 파일을 더 키우려다 막힌 것입니다. EOVERFLOW 에서는 파일이 이미 그 크기로 있습니다. 그 크기를 읽어 가는 쪽이 문제입니다.

ERANGE 도 EOVERFLOW 와 비슷해 보입니다. 갈리는 곳은 넘친 값이 어디서 왔느냐입니다. ERANGE 는 수학 함수나 문자열을 숫자로 바꾸는 strtol() 이 결과를 만들다 넘친 것입니다. EOVERFLOW 는 운영체제가 이미 들고 있는 값을 건네다 넘친 것입니다.

관련 항목

EOVERFLOW 가 속하는 오류 체계

errno · errno.h · POSIX · 시스템 콜 · 커널

EOVERFLOW 를 낳는 넘침과 타입

정수 오버플로 · 데이터 타입 · off_t · ino_t · time_t · blkcnt_t · time64_t

EOVERFLOW 를 돌려주는 호출

stat · fstat · lseek · open · time(2) · clock_gettime

EOVERFLOW 를 피하려고 타입을 넓히는 수단

_FILE_OFFSET_BITS · _TIME_BITS · 대용량 파일 지원 · 64비트 시간 · ABI

EOVERFLOW 가 드러나는 시각과 파일의 상한

2038 문제 · 유닉스 시간 · 에포크 · inode · 파일 시스템

EOVERFLOW 와 헷갈리는 이웃 오류 값

EFBIG · ERANGE · E2BIG · EAGAIN · EINVAL · ENOSPC

다른 이름: Value too large to be stored in data type