본문 바로가기

UNIX_LINUX_C_C++

polling 개념잡기 - Polling Made Efficient (select vs poll vs /dev/poll)

[출처] http://www.ezdoum.com/stories.php?story=02/07/18/0795779

리눅스 fd를 감시하는 방법들을 보면 select, poll, kqueue, /dev/poll등 여러 가지 방법이 있습니다. 제가 주로 사용하는 것은 select인데, 나머지 방법론들은 왜 만들어지게 되었는지 호기심이 들어서 자료를 좀 정리해 봤습니다..

select는 전통적인 fd를 감시하는 방법으로, 3개의 비트 배열을 사용합니다. 이것은 fd가 늘어가면 선형적으로 비용이 증가할 뿐만 아니라, 감시하고자 하는 이벤트 설정을 변형하기 때문에 매번 이벤트 비트를 새로 설정해주어야 하는 불편함이 있습니다. (뭐 트릭이 없는 것은 아니죠. select호출전에 원본은 복사를 해놓고 사본으로 이벤트를 통지 받고 원본을 가지고 다시 select를 호출하는 것이죠. 그리고 fd가 많아졌을때 비용이 선형적으로 증가하는 문제도 어느정도 트릭으로 해결이 가능한데 1000개를 한 select로 관리하면 리턴후에 1000개를 검사해야 하지만 이것을 쓰레드 10개를 만든 다음 select를 각각 10개씩 걸어준다면 fd검사하는 비용을 획기적으로 줄일 수 있습니다.)

select의 불편함 점을 보완한 것이 poll인데, poll 인터페이스의 장점은 두가지를 들수 있습니다. 우선 프로그래머가 관심있어 하는 fd만 넘겨줄수 있고, 감시하는 이벤트도 보전이 된다는 것이죠. 그러나 poll역시 함수 리턴 후에 감시하는 모든 fd에 대해서 루핑을 돌면서 체크를 해야 한다는 단점은 해결하지 못했죠, 또 poll은 하나의 fd의 이벤트를 전송하기 위해서 64개의 비트를 전송해야 하는 단점이 있습니다. select의 경우엔 고작 3개의 비트만 사용하면 되는 것과 비교했을때 상당히 큰 메모리를 차지함을 알 수 있습니다.
(A scalable and explicit event delivery mechanism for UNIX
http://www.ezdoum.com/stories.php?story=02/07/18/3465692
를 보면 자세한 정보를 얻을 수 있습니다.)

그래서 솔라리스에서는 새로운 /dev/poll이란 것을 도입했습니다. poll과 비슷하기는 한데, 가장 큰 차이점은 커널에서 사용자 영역에 있는 pollfd 버퍼로 이벤트에 해당하는 fd만 전송해주기 때문에, 감시하는 fd만큼 루핑을 돌 필요가 없이 정확하게 이벤트 받은 fd에 대해서만 작업을 해주면 되게되는 것이죠. 커널과 사용자영역을 넘나들어야 하기 때문에 하나의 장치드라이버 형태로 만들어지게 된것이구요.

아래의 첨부화일은 이 /dev/poll에 대한 문서입니다. Polling Made Efficient와 /dev/poll의 man page를 pdf문서로 만든 것입니다.

Polling Made Efficient문서 중에 나오는 예제 소스가 있는데, 이 소스를 보시면 poll과 /dev/poll의 구현상의 차이를 알 수 있습니다.
http://www.ezdoum.com/upload/polling_efficient_devpollperf.c

ps.
kqueue의 경우엔 제가 freebsd 장비가 없어서 테스트를 못해봐서 아직 뭐라 언급하기는 힘들지만 우선 이지도움에 문서는 등록해두었습니다.

Kqueue: A generic and scalable event notification facility
http://www.ezdoum.com/stories.php?story=02/07/18/2605778

급하신 분들은 libevent 소스를 분석해 보시면 같은 기능을 하는 select와 kqueue의 구현을 보실 수 있습니다.

첨부 파일: polling_efficient.pdf polling_efficient.pdf (83 KiB(84,543 Bytes))