Tag Archives: select

UNIX Socket Model & libevent

회사에서 libevent에 대한 스터디를 하기로 했습니다. 서버 프로그램 개발시 FreeBSD에서 KQueue와 Solaris에서 /dev/poll 개발 경험이 있습니다. 최근에는 리눅스 서버에서 libevent를 이용하여 서버 프로그램도 제작했습니다. OS별로 각기 API가 조금씩 틀려서 이식성이 떨어져서 이제는 libevent로 통일해서 개발하려고 합니다.

여기서 설명하는 소켓 모델은 여러 소켓을 한 프로세스에서 동시에 처리하기 위한 방식으로, 주로 단일 쓰레드 방식의 어플리케이션에서 여러 소켓 연결(세션)을 처리하기 위해서 사용합니다. 소켓 뿐 아니라 파일 IO 등을 같이 처리할수 있으며, File Descriptor를 통해서 액세스 합니다.

select와 poll이 유닉스 표준으로 정해져 있는 함수이며, 프로토타입은 다음과 같습니다.
[CODE type=cpp]
int select(int nfds, fd_set *r_fds, fd_set *w_fds, fd_set *exceptfds, struct timeval *timeout);
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
[/HTML][/CODE]

select와 poll 모두 file descriptor 리스트를 인자로 넘기고, timeout 동안 기다립니다. Timeout 시간 이전에 전달한 fd 중에 이벤트가 있으면 리턴합니다.

단일 쓰레드 소켓 서버 프로그램의 pseudo code는 대충 아래와 같습니다.
[CODE type=cpp]
while (1) {
  setup_fd_list();
  select_or_poll();
  handle_fd_events();
}
[/HTML][/CODE]

Select는 fd의 리스트를 비트 단위로 인코딩해서 보냅니다. FD_SETSIZE를 정의해서 bitset의 길이를 정해줄수 있지만, 시스템에 따라서 제약이 있는 경우도 있다고 합니다. Poll은 struct array로 fd 리스트를 전달하여 길이에는 제한이 없지만, array의 중간 fd가 지워지면 처리가 좀더 복잡해집니다. Select와 poll 모두 호출할때마다 fd 리스트 전체를 전달하기 때문에 연결수가 많아지면 비효율적이 됩니다. 유저스페이스에서 커널로 전달하는 데이타가 연결수에 비례하여 선형적으로 늘게됩니다.

아래는 libevent 홈페이지에 있는 소켓 모델간의 비교 그래프입니다. select와 poll 모두 연결수가 많아질수록 처리시간이 비례하여 증가하는것을 볼수 있습니다.

이러한 문제점을 해결하기 위해서 OS별로 다른 소켓 모델을 개발했습니다. BSD에서는 Kqueue, Solaris에서는 /dev/poll, Linux에서는 epoll 등이 있습니다. 이 모델들은 fd 리스트 전체를 항상 전달하지 않고, 한번 등록한 fd는 호출할때 다시 등록하지 않아도 등록된 상태로 되어 커널 쪽에서 관리가 됩니다. fd를 등록하고, 나중에 삭제할 때나 이벤트가 있을 때 까지 등록된 상태로 남아있어, 커널과 유저스페이스 간의 통신 오버헤드를 대폭 줄였습니다. 따라서 연결이 늘더라도 이를 커널쪽에서 효율적으로 처리하여 처리시간이 크게 늘지 않도록 구현되어 있습니다. 위에 그래프를 보면 Kqueue와 epoll은 연결수가 늘어나도 처리시간은 거의 일정한 것을 관찰할수 있습니다.

하지만 이러한 소켓 모델들은 표준화 되지 않고, 구현 범위도 조금씩 차이가 나기때문에 이식성이 좋은 소켓 서버를 구현하는 것은 쉽지 않습니다. Libevent는 이러한 소켓 모델들을 모두 지원하는 라이브러리로 시스템에 따라서 제일좋은 소켓 모델을 선택하여 동작하도록 되어 있습니다.

참고 URL: http://www.kegel.com/c10k.html

c10k 문제(10000 연결 문제)라고 하여 연결을 동시에 많이 유지하는 서버 프로그래밍에 대해서잘 정리되어 있는 페이지입니다.

스터디 주제가 이미 어느정도 아는 내용으로 정해졌고, 새로 오신 분들이 유닉스 경험이 많지 않아서 스터디하면서 나름대로 새로운 내용들을 공부해봐야겠습니다. 일단 개인 관심언어인 Ocaml로 ocaml-event를 이용해서 서버 프로그램을 작성해보고, Make대신 automake와 autoconf 공부도 좀 해야겠네요. 워낙 BSD Parallel Make에 익숙해져서 요즘 리눅스에서 GNU Make 쓰는데 당황스럽더군요. –;