`select` — I/O 완료 대기하기

select — I/O 완료 대기하기

select 모듈은 운영체제에서 제공하는 select()poll() 함수에 접근할 수 있게 해 줘요. Solaris 계열에서는 devpoll(), Linux 2.5+에서는 epoll(), 대부분의 BSD에서는 kqueue()를 쓸 수 있어요. Windows에서는 소켓에만 동작하고, 다른 운영체제에서는 다른 파일 종류(특히 Unix에서는 파이프)에도 동작해요. 다만 일반 파일에 대해서는 "마지막으로 읽은 이후 파일이 자라났는지"를 판별하는 용도로는 쓸 수 없어요.

출처: Python 표준 라이브러리

본문

참고: selectors 모듈은 select 모듈의 원시 연산 위에 구축된 고수준이고 효율적인 I/O 멀티플렉싱을 제공해요. OS 수준 원시 연산을 세밀하게 제어하고 싶지 않다면 selectors 모듈을 사용하는 걸 권장해요.

가용성(availability): WASI가 아님. 이 모듈은 WebAssembly 플랫폼에서는 동작하지 않거나 사용할 수 없어요.

이 모듈은 다음 항목들을 정의해요.

예외

select.error

OSError 의 더 이상 쓰이지 않는(비권장) 별칭이에요.

3.3 버전 변경: PEP 3151 에 따라 이 클래스가 OSError 의 별칭이 되었어요.

함수

select.devpoll()

/dev/poll 폴링 객체를 반환해요. devpoll 객체가 지원하는 메서드는 아래 /dev/poll polling objects 절을 참고하세요.

devpoll() 객체는 인스턴스를 만들 당시 허용된 파일 디스크립터 수에 연결돼 있어요. 프로그램이 이 값을 줄이면 devpoll() 은 실패하고, 늘리면 devpoll() 이 활성 파일 디스크립터의 불완전한 목록을 반환할 수 있어요.

새로 만든 파일 디스크립터는 상속 불가(non-inheritable)예요.

3.3 버전에서 추가.

3.4 버전 변경: 이제 새 파일 디스크립터가 non-inheritable 해졌어요.

가용성: Solaris.

select.epoll(*sizehint=-1, *flags=0)

가장자리(Edge) 또는 수준(Level) 트리거 방식의 I/O 이벤트 인터페이스로 쓸 수 있는 가자리 폴링 객체를 반환해요.

  • sizehint 는 등록될 것으로 예상되는 이벤트 수를 epoll에 알려줘요. 양수여야 하고, 기본값을 쓰려면 -1이에요. epoll_create1() 을 쓸 수 없는 오래된 시스템에서만 쓰이고, 그 외에는 효과가 없어요(값은 여전히 검사되지만요).
  • flags 는 비권장되고 완전히 무시돼요. 다만 제공할 때는 값이 0 또는 select.EPOLL_CLOEXEC여야 하고, 그렇지 않으면 OSError 가 발생해요.

epoll 객체가 지원하는 메서드는 아래 Edge and level trigger polling (epoll) objects 절을 참고하세요.

epoll 객체는 컨텍스트 관리 프로토콜(context management protocol)을 지원해요. with 문에 쓰면 블록이 끝날 때 새 파일 디스크립터가 자동으로 닫혀요.

새 파일 디스크립터는 non-inheritable이에요.

3.3 버전 변경: flags 매개변수가 추가됐어요.

3.4 버전 변경: with 문 지원이 추가됐고, 새 파일 디스크립터가 non-inheritable 해졌어요.

3.4 이후 비권장: flags 매개변수. 이제 기본으로 select.EPOLL_CLOEXEC가 쓰여요. 파일 디스크립터를 상속 가능하게 하려면 os.set_inheritable()을 쓰세요.

가용성: Linux >= 2.5.44.

select.poll()

폴링 객체를 반환해요. 파일 디스크립터를 등록·해제하고 I/O 이벤트를 폴링할 수 있어요. 폴링 객체가 지원하는 메서드는 아래 Polling objects 절을 참고하세요.

가용성: Unix.

select.kqueue()

커널 큐(kernel queue) 객체를 반환해요. kqueue 객체가 지원하는 메서드는 아래 Kqueue objects 절을 참고하세요.

새 파일 디스크립터는 non-inheritable이에요.

3.4 버전 변경: 이제 새 파일 디스크립터가 non-inheritable 해졌어요.

가용성: BSD, macOS.

select.kevent(*ident, *filter=KQ_FILTER_READ, *flags=KQ_EV_ADD, *fflags=0, *data=0, *udata=0)

커널 이벤트 객체를 반환해요. kevent 객체가 지원하는 메서드는 아래 Kevent objects 절을 참고하세요.

가용성: BSD, macOS.

select.select(*rlist, *wlist, *xlist, *timeout=None)

Unix select() 시스템 호출에 대한 직관적인 인터페이스예요. 처음 세 인자는 '대기 가능한 객체(waitable objects)'의 이터러블이에요. 즉 파일 디스크립터를 나타내는 정수이거나, 그런 정수를 반환하는 매개변수 없는 fileno() 메서드를 가진 객체예요.

  • rlist : 읽을 준비가 될 때까지 대기
  • wlist : 쓸 준비가 될 때까지 대기
  • xlist : "예외 조건(exceptional condition)"을 대기 (어떤 조건을 그렇게 보는지는 시스템 매뉴얼 참고)

빈 이터러블은 허용되지만, 세 개 모두 빈 이터러블을 받는지는 플랫폼에 따라 달라요(Unix에서는 동작하는 것으로 알려져 있지만 Windows에서는 아님). 선택적인 timeout 인자는 초 단위 부동소수점 숫자로 제한 시간을 지정해요. timeout을 생략하거나 None으로 주면 함수는 파일 디스크립터가 최소 하나 준비될 때까지 블로킹해요. 타임아웃 값이 0이면 폴링(poll)을 의미하고 절대 블로킹하지 않아요.

반환 값은 준비된 객체들의 리스트 세 개의 튜플로, 각각 처음 세 인자의 부분집합이에요. 타임아웃이 됐는데도 준비된 파일 디스크립터가 없으면 빈 리스트 세 개를 반환해요.

이터러블에 허용되는 객체 종류에는 Python 파일 객체(예: sys.stdin 이나 open() 또는 os.popen()이 반환한 객체), socket.socket()이 반환한 소켓 객체가 있어요. 적절한 fileno() 메서드(진짜 파일 디스크립터를 반환하는, 단순한 임의 정수가 아닌)만 있으면 wrapper 클래스를 직접 정의해도 돼요.

참고: Windows에서는 파일 객체는 허용되지 않고 소켓만 허용돼요. Windows에서 내부적으로 select() 함수는 WinSock 라이브러리가 제공하는데, WinSock에서 비롯되지 않은 파일 디스크립터는 다루지 않아요.

3.5 버전 변경: 이제 신호에 의해 중단되면 InterruptedError 를 발생시키는 대신 다시 계산된 타임아웃으로 재시도돼요(신호 핸들러가 예외를 일으키는 경우 제외, 근거는 PEP 475 참고).

select.PIPE_BUF

파이프가 select(), poll() 또는 이 모듈의 다른 인터페이스로 쓰기 준비가 되었다고 보고됐을 때 블로킹 없이 파이프에 쓸 수 있는 최소 바이트 수예요. 소켓 같은 다른 종류의 파일류 객체에는 적용되지 않아요.

POSIX는 이 값이 최소 512라고 보장해요.

가용성: Unix.

3.2 버전에서 추가.

/dev/poll polling objects

Solaris와 그 파생 제품에는 /dev/poll이 있어요. select()O(가장 높은 파일 디스크립터), poll()O(파일 디스크립터 수)인 반면, /dev/pollO(활성 파일 디스크립터)예요.

/dev/poll 의 동작은 표준 poll() 객체와 매우 비슷해요.

devpoll.close()

폴링 객체의 파일 디스크립터를 닫아요.

3.4 버전에서 추가.

devpoll.closed

폴링 객체가 닫혔으면 True.

3.4 버전에서 추가.

devpoll.fileno()

폴링 객체의 파일 디스크립터 번호를 반환해요.

3.4 버전에서 추가.

devpoll.register(*fd*[, *eventmask*])

파일 디스크립터를 폴링 객체에 등록해요. 이후 poll() 메서드 호출에서 그 파일 디스크립터에 보류 중인 I/O 이벤트가 있는지 확인하게 돼요. fd 는 정수이거나 정수를 반환하는 fileno() 메서드를 가진 객체일 수 있어요. 파일 객체는 fileno() 를 구현하므로 인자로 쓸 수 있어요.

eventmask 는 확인하고 싶은 이벤트 종류를 설명하는 선택적 비트마스크예요. 상수는 poll() 객체와 같아요. 기본값은 POLLIN, POLLPRI, POLLOUT 상수의 조합이에요.

경고: 이미 등록된 파일 디스크립터를 다시 등록하는 것은 오류가 아니지만 결과는 정의되지 않아요. 적절한 방법은 먼저 해제(unregister)하거나 수정(modify)하는 것이에요. 이것은 poll() 과 비교했을 때 중요한 차이점이에요.

devpoll.modify(*fd*[, *eventmask*])

이 메서드는 unregister() 다음에 register() 를 수행해요. 명시적으로 두 번 하는 것보다 (약간) 더 효율적이에요.

devpoll.unregister(*fd*)

폴링 객체가 추적하는 파일 디스크립터를 제거해요. register() 메서드처럼 fd 는 정수이거나 정수를 반환하는 fileno() 메서드를 가진 객체일 수 있어요.

등록된 적 없는 파일 디스크립터를 제거하려는 시도는 안전하게 무시돼요.

devpoll.poll([*timeout*])

등록된 파일 디스크립터들의 집합을 폴링하고, 이벤트나 오류를 보고할 디스크립터에 대한 (fd, event) 2-튜플을 담은 (비어 있을 수도 있는) 리스트를 반환해요. fd 는 파일 디스크립터이고, event 는 그 디스크립터에 대해 보고된 이벤트들의 비트가 설정된 비트마스크예요. 입력 대기(POLLIN), 디스크립터에 쓸 수 있음(POLLOUT) 등을 나타내요. 빈 리스트는 호출이 타임아웃되어 이벤트를 보고할 파일 디스크립터가 없었음을 의미해요. timeout 이 주어지면 시스템이 이벤트를 기다릴 시간을 밀리초 단위로 지정해요. timeout 을 생략하거나, -1 이거나 None이면 호출은 이 poll 객체에 이벤트가 있을 때까지 블로킹해요.

3.5 버전 변경: 이제 신호에 의해 중단되면 InterruptedError 대신 다시 계산된 타임아웃으로 재시도돼요(신호 핸들러가 예외를 일으키는 경우 제외, 근거는 PEP 475 참고).

Edge and level trigger polling (epoll) objects

https://linux.die.net/man/4/epoll

eventmask 는 다음 상수들을 쓰는 비트 마스크예요.

Constant Meaning
EPOLLIN 읽을 수 있음(available for read).
EPOLLOUT 쓸 수 있음(available for write).
EPOLLPRI 긴급 데이터를 읽을 수 있음.
EPOLLERR 연결된 fd에서 오류 조건이 발생했음.
EPOLLHUP 연결된 fd에서 hang up이 발생했음.
EPOLLET 가장자리 트리거(Edge Trigger) 동작을 설정. 기본은 수준 트리거(Level Trigger) 동작.
EPOLLONESHOT 원샷(one-shot) 동작 설정. 이벤트 하나가 꺼내지면 fd가 내부적으로 비활성화됨.
EPOLLEXCLUSIVE 연결된 fd에 이벤트가 있을 때 하나의 epoll 객체만 깨움. 기본(이 플래그가 설정되지 않은 경우)은 fd에 폴링하는 모든 epoll 객체를 깨움.
EPOLLRDHUP 스트림 소켓 피어가 연결을 닫거나 연결의 쓰기 절반을 종료함.
EPOLLRDNORM EPOLLIN 과 동등.
EPOLLRDBAND 우선순위 데이터 대역을 읽을 수 있음.
EPOLLWRNORM EPOLLOUT 과 동등.
EPOLLWRBAND 우선순위 데이터를 쓸 수 있음.
EPOLLMSG 무시됨.
EPOLLWAKEUP 이벤트 대기 중 sleep 방지.

3.6 버전에서 추가: EPOLLEXCLUSIVE 가 추가됐어요. Linux 커널 4.5 이상에서만 지원돼요.

3.14 버전에서 추가: EPOLLWAKEUP 이 추가됐어요. Linux 커널 3.5 이상에서만 지원돼요.

epoll.close()

epoll 객체의 제어 파일 디스크립터를 닫아요.

epoll.closed

epoll 객체가 닫혔으면 True.

epoll.fileno()

제어 fd의 파일 디스크립터 번호를 반환해요.

epoll.fromfd(*fd*)

주어진 파일 디스크립터로부터 epoll 객체를 만들어요.

epoll.register(*fd*[, *eventmask*])

파일 디스크립터 fd 를 epoll 객체에 등록해요.

epoll.modify(*fd*, *eventmask*)

등록된 파일 디스크립터 fd 를 수정해요.

epoll.unregister(*fd*)

등록된 파일 디스크립터를 epoll 객체에서 제거해요.

3.9 버전 변경: 이 메서드는 더 이상 EBADF 오류를 무시하지 않아요.

epoll.poll(*timeout=None, *maxevents=-1*)

이벤트를 기다려요. timeout은 초 단위(부동소수점)예요.

3.5 버전 변경: 이제 신호에 의해 중단되면 InterruptedError 대신 다시 계산된 타임아웃으로 재시도돼요(신호 핸들러가 예외를 일으키는 경우 제외, 근거는 PEP 475 참고).

Polling objects

대부분의 Unix 시스템에서 지원되는 poll() 시스템 호출은 동시에 매우 많은 클라이언트를 서비스하는 네트워크 서버에 더 나은 확장성을 제공해요. poll() 은 관심 있는 파일 디스크립터만 나열하면 되지만 select() 는 비트맵을 만들고 관심 있는 fd에 비트를 켠 다음 나중에 전체 비트맵을 다시 선형적으로 스캔해야 하기 때문에 더 잘 확장돼요. select()O(가장 높은 파일 디스크립터)이고 poll()O(파일 디스크립터 수)예요.

poll.register(*fd*[, *eventmask*])

파일 디스크립터를 폴링 객체에 등록해요. 이후 poll() 메서드 호출에서 그 파일 디스크립터에 보류 중인 I/O 이벤트가 있는지 확인하게 돼요. fd 는 정수이거나 정수를 반환하는 fileno() 메서드를 가진 객체일 수 있어요. 파일 객체는 fileno() 를 구현하므로 인자로 쓸 수 있어요.

eventmask 는 확인하고 싶은 이벤트 종류를 설명하는 선택적 비트마스크로, 아래 표에 설명된 POLLIN, POLLPRI, POLLOUT 상수의 조합일 수 있어요. 지정하지 않으면 기본값으로 세 종류의 이벤트를 모두 확인해요.

Constant Meaning
POLLIN 읽을 데이터가 있음.
POLLPRI 긴급하게 읽을 데이터가 있음.
POLLOUT 출력 준비 완료: 쓰기가 블로킹되지 않음.
POLLERR 어떤 종류의 오류 조건.
POLLHUP 연결 끊김(hung up).
POLLRDHUP 스트림 소켓 피어가 연결을 닫거나 연결의 쓰기 절반을 종료함.
POLLNVAL 잘못된 요청: 디스크립터가 열려 있지 않음.

이미 등록된 파일 디스크립터를 다시 등록하는 것은 오류가 아니며, 디스크립터를 정확히 한 번 등록한 것과 같은 효과를 가져요.

poll.modify(*fd*, *eventmask*)

이미 등록된 fd를 수정해요. register(fd, eventmask) 와 같은 효과를 가져요. 등록된 적 없는 파일 디스크립터를 수정하려 하면 errno ENOENT 를 가진 OSError 예외가 발생해요.

poll.unregister(*fd*)

폴링 객체가 추적하는 파일 디스크립터를 제거해요. register() 메서드처럼 fd 는 정수이거나 정수를 반환하는 fileno() 메서드를 가진 객체일 수 있어요.

등록된 적 없는 파일 디스크립터를 제거하려 하면 KeyError 예외가 발생해요.

poll.poll([*timeout*])

등록된 파일 디스크립터들의 집합을 폴링하고, 이벤트나 오류를 보고할 디스크립터에 대한 (fd, event) 2-튜플을 담은 (비어 있을 수도 있는) 리스트를 반환해요. fd 는 파일 디스크립터이고, event 는 그 디스크립터에 대해 보고된 이벤트들의 비트가 설정된 비트마스크예요(입력 대기 POLLIN, 디스크립터에 쓸 수 있음 POLLOUT 등). 빈 리스트는 호출이 타임아웃되어 이벤트를 보고할 파일 디스크립터가 없었음을 의미해요. timeout 이 주어지면 시스템이 이벤트를 기다릴 시간을 밀리초 단위로 지정해요. timeout 을 생략하거나 음수이거나 None이면 호출은 이 poll 객체에 이벤트가 있을 때까지 블로킹해요.

3.5 버전 변경: 이제 신호에 의해 중단되면 InterruptedError 대신 다시 계산된 타임아웃으로 재시도돼요(신호 핸들러가 예외를 일으키는 경우 제외, 근거는 PEP 475 참고).

Kqueue objects

kqueue.close()

kqueue 객체의 제어 파일 디스크립터를 닫아요.

kqueue.closed

kqueue 객체가 닫혔으면 True.

kqueue.fileno()

제어 fd의 파일 디스크립터 번호를 반환해요.

kqueue.fromfd(*fd*)

주어진 파일 디스크립터로부터 kqueue 객체를 만들어요.

kqueue.control(*changelist*, *max_events*[, *timeout*]) → eventlist

kevent에 대한 저수준 인터페이스예요.

  • changelist는 kevent 객체들의 이터러블 또는 None 이어야 해요.
  • max_events는 0 또는 양의 정수여야 해요.
  • timeout은 초 단위(부동소수점 가능)이고 기본은 None으로 영원히 기다려요.

3.5 버전 변경: 이제 신호에 의해 중단되면 InterruptedError 대신 다시 계산된 타임아웃으로 재시도돼요(신호 핸들러가 예외를 일으키는 경우 제외, 근거는 PEP 475 참고).

Kevent objects

https://man.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2

kevent.ident

이벤트를 식별하는 데 쓰는 값이에요. 해석은 필터에 따라 다르지만 보통 파일 디스크립터예요. 생성자에서 ident는 int이거나 fileno() 메서드를 가진 객체일 수 있어요. kevent는 내부적으로 정수를 저장해요.

kevent.filter

커널 필터의 이름이에요.

Constant Meaning
KQ_FILTER_READ 디스크립터를 받고 읽을 수 있는 데이터가 있을 때마다 반환.
KQ_FILTER_WRITE 디스크립터를 받고 쓸 수 있는 데이터가 있을 때마다 반환.
KQ_FILTER_AIO AIO 요청.
KQ_FILTER_VNODE fflag 에서 감시 중인 요청된 이벤트 중 하나 이상이 발생하면 반환.
KQ_FILTER_PROC 프로세스 ID에 대한 이벤트를 감시.
KQ_FILTER_NETDEV 네트워크 장치에 대한 이벤트를 감시(macOS에서는 사용 불가).
KQ_FILTER_SIGNAL 감시 중인 신호가 프로세스에 전달될 때마다 반환.
KQ_FILTER_TIMER 임의의 타이머를 설정.

kevent.flags

필터 동작을 나타내요.

Constant Meaning
KQ_EV_ADD 이벤트를 추가하거나 수정.
KQ_EV_DELETE 큐에서 이벤트를 제거.
KQ_EV_ENABLE control()이 이벤트를 반환하도록 허용.
KQ_EV_DISABLE 이벤트를 비활성화.
KQ_EV_ONESHOT 첫 발생 후 이벤트를 제거.
KQ_EV_CLEAR 이벤트가 검색된 후 상태를 리셋.
KQ_EV_SYSFLAGS 내부 이벤트.
KQ_EV_FLAG1 내부 이벤트.
KQ_EV_EOF 필터별 EOF 조건.
KQ_EV_ERROR 반환 값을 참고.

kevent.fflags

필터별 플래그예요.

KQ_FILTER_READKQ_FILTER_WRITE 필터 플래그:

Constant Meaning
KQ_NOTE_LOWAT 소켓 버퍼의 low water mark.

KQ_FILTER_VNODE 필터 플래그:

Constant Meaning
KQ_NOTE_DELETE unlink() 가 호출됨.
KQ_NOTE_WRITE 쓰기 발생.
KQ_NOTE_EXTEND 파일이 확장됨.
KQ_NOTE_ATTRIB 속성이 변경됨.
KQ_NOTE_LINK 링크 수가 변경됨.
KQ_NOTE_RENAME 파일이 이름 변경됨.
KQ_NOTE_REVOKE 파일 접근이 회수됨.

KQ_FILTER_PROC 필터 플래그:

Constant Meaning
KQ_NOTE_EXIT 프로세스가 종료됨.
KQ_NOTE_FORK 프로세스가 fork() 를 호출함.
KQ_NOTE_EXEC 프로세스가 새 프로세스를 실행함.
KQ_NOTE_PCTRLMASK 내부 필터 플래그.
KQ_NOTE_PDATAMASK 내부 필터 플래그.
KQ_NOTE_TRACK fork() 를 따라 프로세스를 추적.
KQ_NOTE_CHILD NOTE_TRACK 에 대해 자식 프로세스에서 반환됨.
KQ_NOTE_TRACKERR 자식에 붙을 수 없음.

KQ_FILTER_NETDEV 필터 플래그(macOS에서 사용 불가):

Constant Meaning
KQ_NOTE_LINKUP 링크가 올라감.
KQ_NOTE_LINKDOWN 링크가 내려감.
KQ_NOTE_LINKINV 링크 상태가 유효하지 않음.

kevent.data

필터별 데이터예요.

kevent.udata

사용자 정의 값이에요.

더 알아보기

  • selectorsselect 위에 구축된 고수준 I/O 멀티플렉싱 모듈로, 일반적인 용도로는 이쪽을 권장해요.