사전 EAGAIN
에러코드

EAGAIN

gabury1

EAGAIN 은 시스템 호출이 실패했을 때 errno 에 담기는 값 하나입니다. 지금은 자원이 없으니 다시 해보라는 뜻입니다. 무엇이 망가졌다는 신호가 아니라 잠시 뒤에 다시 부르라는 신호입니다.

상세

errno 는 무엇이 잘못됐는지를 알리는 정수 변수입니다. 리눅스 errno(3) 은 <errno.h> 헤더가 이 변수를 정의한다고 적습니다. 시스템 호출과 일부 라이브러리 함수가 오류가 났을 때 이 변수를 설정합니다. EAGAIN 은 그 변수에 담기는 이름 가운데 하나입니다.

풀어 쓰는 약어가 아닙니다. POSIX(Portable Operating System Interface)가 규정한 오류 이름 가운데 하나입니다. 리눅스 커널 헤더 include/uapi/asm-generic/errno-base.h 는 이 값 옆에 /* Try again */ 이라는 주석을 답니다.

이름이 말하는 것은 일시적이라는 것입니다. errno(3) 의 오류 이름 목록은 EAGAIN 을 자원을 일시적으로 쓸 수 없다는 뜻으로 적습니다. 그래서 이 값은 실패를 알리는 자리뿐 아니라 정상 흐름의 신호로도 쓰입니다.

epoll(7) 이 그 자리를 보여줍니다. EPOLLET 플래그를 쓰는 edge-triggered 인터페이스에서 논블로킹 I/O(Input/Output, 입출력)를 어떻게 다루는지 적는 대목입니다. 파일 디스크립터를 논블로킹으로 두라고 적습니다. 그리고 read(2) 나 write(2) 가 EAGAIN 을 돌려준 뒤에야 이벤트를 기다리라고 적습니다. 같은 문서의 예제에서 do_use_fd() 는 read(2) 나 write(2) 가 EAGAIN 을 돌려줄 때까지 그 파일 디스크립터를 계속 씁니다. 이벤트 구동 상태 기계 애플리케이션은 EAGAIN 을 받은 뒤 그 시점의 상태를 기록해 둬야 합니다. 다음번 do_use_fd() 호출에서 멈춘 자리부터 이어 읽거나 쓰기 위해서입니다. 그 파일 디스크립터는 다음 논블로킹 읽기나 쓰기가 EAGAIN 을 낼 때까지 준비된 것으로 봐야 합니다.

명세상 뜻

Open Group Base Specifications 의 <errno.h> 항목이 이 이름을 정합니다. 정의 문장은 [EAGAIN] Resource unavailable, try again 입니다. 괄호 안에 [EWOULDBLOCK] 과 같은 값이어도 된다고 적혀 있습니다. 같은 목록의 [EWOULDBLOCK] 은 Operation would block 입니다. 여기에도 [EAGAIN] 과 같은 값이어도 된다는 말이 붙습니다.

리눅스 errno(3) 의 오류 이름 목록은 이렇게 적습니다.

EAGAIN          Resource temporarily unavailable (may be the same value as
                EWOULDBLOCK) (POSIX.1-2001).

함께 정해진 것이 하나 있습니다. errno(3) 은 POSIX.1 이 규정한 오류 이름이 전부 서로 다른 값을 가져야 한다고 적습니다. 예외는 EAGAIN 과 EWOULDBLOCK 둘뿐입니다. 이 둘은 같은 값이어도 됩니다. 리눅스에서는 이 둘이 모든 아키텍처에서 같은 값이라고 errno(3) 이 적습니다. 커널 헤더 include/uapi/asm-generic/errno.h 가 #define EWOULDBLOCK EAGAIN 한 줄로 둘을 잇습니다.

숫자값은 플랫폼이 정합니다. 리눅스 커널 헤더 include/uapi/asm-generic/errno-base.h 는 #define EAGAIN 11 로 적습니다. 이 값은 아키텍처를 안 가리는 asm-generic 헤더 기준입니다.

실제 원인

같은 EAGAIN 이 서로 다른 경로에서 옵니다. 어느 호출이 냈는지가 원인을 가릅니다.

flowchart TD
    A["EAGAIN 을 받음"] --> B{"어느 호출인가"}
    B -- "read · write" --> C["논블로킹 파일 디스크립터라 지금 블록될 상황"]
    B -- "accept" --> D["논블로킹 소켓인데 기다리는 연결이 없음"]
    B -- "fork · pthread_create" --> E["프로세스와 스레드 수 한도에 닿음"]
    B -- "connect" --> F["논블로킹 유닉스 도메인 소켓이거나 라우팅 캐시가 모자람"]

논블로킹 읽기와 쓰기

read(2) 는 파일 디스크립터가 소켓이 아닌 파일을 가리키는 경우를 따로 적습니다. 그 파일 디스크립터에 O_NONBLOCK 이 걸린 상태에서 읽기가 블록될 상황이면 EAGAIN 을 냅니다. write(2) 도 같은 조건에서 같은 값을 냅니다. 파일 디스크립터가 소켓이면 두 문서 모두 같은 상황을 EAGAIN or EWOULDBLOCK 으로 적습니다.

가리는 자리는 그 파일 디스크립터에 O_NONBLOCK 이 걸려 있는지입니다. 이 플래그의 자세한 것은 open(2) 이 갖는다고 두 문서가 함께 가리킵니다.

기다리는 연결이 없는 accept

accept(2) 는 큐에 대기 중인 연결이 없으면 호출자를 블록합니다. 소켓이 논블로킹으로 표시돼 있지 않은 경우입니다. 연결이 하나 올 때까지 기다립니다. 소켓이 논블로킹으로 표시돼 있으면 다릅니다. 대기 중인 연결이 없으면 EAGAIN 또는 EWOULDBLOCK 으로 실패합니다. 받을 것이 아직 없다는 뜻입니다. 소켓이 못 쓰게 됐다는 뜻이 아닙니다.

프로세스와 스레드 수 한도

이 값이 디스크나 메모리가 아니라 한도를 가리키는 자리입니다. fork(2) 의 EAGAIN 은 시스템이 부과한 스레드 수 한도에 닿았다는 뜻입니다. 이 오류를 촉발할 수 있는 한도가 여럿입니다.

한도 무엇을 세나
RLIMIT_NPROC 소프트 자원 한도 실제 사용자 ID 하나의 프로세스와 스레드 수. setrlimit(2) 으로 설정
/proc/sys/kernel/threads-max 커널이 시스템 전체에 거는 프로세스와 스레드 수 상한
/proc/sys/kernel/pid_max PID(Process ID, 프로세스 식별자)의 최대 개수
cgroup pids.max cgroup 의 프로세스 수 컨트롤러가 거는 한도

fork(2) 은 EAGAIN 항목을 하나 더 둡니다. 호출자가 SCHED_DEADLINE 스케줄링 정책 아래에서 도는 경우입니다. reset-on-fork 플래그가 설정돼 있지 않아야 합니다.

pthread_create(3) 도 같은 값을 냅니다. 스레드를 하나 더 만들 자원이 모자란 경우가 하나입니다. 시스템이 부과한 스레드 수 한도에 닿은 경우가 다른 하나입니다. 여기서 한도는 RLIMIT_NPROC · threads-max · pid_max 를 가리킵니다.

유닉스 도메인 소켓의 connect

connect(2) 은 논블로킹 유닉스 도메인 소켓에서 연결을 즉시 끝낼 수 없을 때 EAGAIN 을 냅니다. 다른 소켓 계열에서는 라우팅 캐시에 항목이 모자랄 때 이 값이 옵니다. 앞의 세 자리와 원인이 다릅니다. 한도도 논블로킹 읽기도 아닌 커널 안의 캐시입니다.

경계

EWOULDBLOCK

같은 값인가. 리눅스에서는 같은 값입니다. 다만 이식성을 따지는 코드에서 같다고 두면 안 됩니다. read(2) 는 소켓이 논블로킹이라 읽기가 블록될 상황을 EAGAIN or EWOULDBLOCK 으로 적습니다. 그리고 근거를 답니다. POSIX.1-2001 은 이 경우 둘 중 어느 오류든 돌려주는 것을 허용합니다. 두 상수가 같은 값을 가지도록 요구하지도 않습니다. 그래서 이식성 있는 애플리케이션은 두 가능성을 모두 검사해야 한다고 적습니다. write(2) 와 accept(2) 도 같은 문구를 답니다.

EINTR

시그널에 끊긴 read(2) 도 이건가. 아닙니다. read(2) 은 데이터를 하나도 읽기 전에 시그널에 호출이 끊긴 경우를 EINTR 로 따로 둡니다. 자원이 지금 없어서 돌아온 것이 아닙니다. 시그널이 끼어들어 돌아온 것이라 가리키는 사건이 다릅니다.

EINPROGRESS

논블로킹 connect(2) 이 내는 것도 이건가. 소켓 계열이 가릅니다. connect(2) 은 소켓이 논블로킹이라 연결을 즉시 끝낼 수 없는 경우를 EINPROGRESS 로 둡니다. 유닉스 도메인 소켓은 그 대신 EAGAIN 으로 실패한다고 괄호로 적습니다. 논블로킹 connect 가 언제나 EAGAIN 이라는 말도 반만 맞습니다. 언제나 EINPROGRESS 라는 말도 반만 맞습니다.

EAI_AGAIN

이름이 닮은 이 값도 EAGAIN 인가. 아닙니다. EAI_AGAIN 은 getaddrinfo(3) 이 이름 서버로부터 일시적 실패 표시를 받았을 때 쓰는 별개의 오류 이름입니다. 나중에 다시 해보라는 결이 겹쳐서 헷갈립니다. 그래도 EAGAIN 이 담기는 자리는 errno 입니다. 이쪽은 이름 해석 함수가 돌려주는 오류 이름입니다.

관련 항목

이 값을 돌려주는 호출

read(2) · write(2) · accept(2) · connect(2) · fork(2) · pthread_create(3) · getaddrinfo(3) · 시스템 호출

이 값이 놓이거나 저장되는 저장소

파일 디스크립터 · 소켓 · 유닉스 도메인 소켓 · errno

이 값의 발생을 좌우하는 논블로킹 설정과 호출

O_NONBLOCK · EPOLLET · 논블로킹 I/O · open(2) · epoll · epoll_wait(2)

이 값을 정의하는 표준·문서

POSIX · 커널

이 값을 내는 자원 부족의 종류

프로세스 · 스레드 · RLIMIT_NPROC · setrlimit(2) · threads-max · pid_max · cgroup · proc(5) · 라우팅

이 값이 나는 스케줄링 조건

SCHED_DEADLINE · reset-on-fork · 스케줄링 · sched(7)

헷갈리는 이웃

EWOULDBLOCK · EINTR · EINPROGRESS · EAI_AGAIN · 시그널 · signal(7) · 이름 해석

이 값과 원인이 다른 read 오류

EBADF · EFAULT

논블로킹 connect 완료를 확인하는 호출

select(2) · poll(2) · getsockopt(2) · SO_ERROR · SOL_SOCKET

다른 이름: Resource temporarily unavailable