Linux에서 Poll 구현 #
--
김도집 2005-10-14 15:45:14
Contents
- 1 Linux Poll 구현 분석
- 1.1 개요
- 1.2 sys_poll
- 1.3 do_poll
- 1.4 do_pollfd
커널 소스상에서 따라 가 보도록 한다.
최초 응용 프로그램에서 poll 함수를 호출하면 리눅스 커널상의 sys_poll 이라는 시스템 콜이 호출이 될 것이다. 따라서 여기서 시작하는 것으로 하죠.
1.2 sys_poll #
fs/select.c
asmlinkage long sys_poll(struct pollfd __user * ufds, unsigned int nfds,
long timeout)
함수 원형을 보자면 위와 같다.
세개의 인자들은 응용 프로그램에서 사용하는 poll(2) 함수와 동일하므로 man 페이지를 참고하자.
- nfds와 timeout에 대한 값을 검사한다.
사실 이부분은 적당한 값이 들어왔는지 확인 하는 것이기에 그냥 넘어가자.
- poll_initwait(&table)
처음으로 무엇인가 한다. sys_poll 내부에서 strcut poll_wqueues 형의 tables를 선언했다. 이를 초기화 해 주는 것으로 보인다. 우선 poll_wqueue 자료가 어떤건지 보자.
<linux/poll.h>
struct poll_wqueues {
poll_table pt;
struct poll_table_page * table;
int error;
이제는 poll_initwait()를 보자
void poll_initwait(struct poll_wqueues *pwq)
{
init_poll_funcptr(&pwq->pt, __pollwait);
pwq->error = 0;
pwq->table = NULL;
}
함수는 간단하다. 그런데 또 함수를 호출한다. ㅡㅡ. init_poll_funcptr()를 따라가 보자.
<linux/poll.h>
static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc)
{
pt->proc = qproc;
}
결국 이는 poll를 위한 대기큐(waitqueue)인 pwq내의 필드 중 함수포인터인 qproc에 __pollwait()를 등록하는 것이다.
__pollwait()함수는 select.c에 정의되어 있다. 이는 이벤트를 위한 대기큐를 poll 대기큐의 테이블에 등록하는 역할을 한다.
- 기타 초기값을 설정하고 pollfd 형의 ufds를 모두 poll_list 구조체에 연결리스트로 등록한다.
- do_poll을 호출한다.
자 이제 중요한 것이다. 실질적인 poll 처리는 이 do_poll에서 처리한다.
fdcount = do_poll(nfds, head, &table, timeout);
- do_poll에서 받은 revents의 값을 사용자 영역으로 복사해 넘겨주는 것을 처리한다.
대충 sys_poll에서 처리하는 것을 설정하였다. sys_poll에서는 응용 프로그램에서 넘어온 인자를 커널에서 처리하기 위한 사전 작업을 하고 그 이후 처리를 do_poll()함수에게 넘긴다. 처리된 것을 응용 프로그램에 넘기기 위해 revents의 값을 사용자 영역으로 복사해 넘겨주게 되는 것이다.
자, 이제 do_poll에 대해 알아보자.
1.3 do_poll #
fs/select.c
static int do_poll(unsigned int nfds, struct poll_list *list,
struct poll_wqueues *wait, long timeout)
do_poll에서 실질적인 poll 처리를 한다. 이는 for(;;)문으로 무한 루프를 돈다. 우선 그 코드를 보면 다음과 같다.
for (;;) {
struct poll_list *walk;
set_current_state(TASK_INTERRUPTIBLE);
walk = list;
while(walk != NULL) {
do_pollfd( walk->len, walk->entries, &pt, &count);
walk = walk->next;
}
pt = NULL;
if (count || !timeout || signal_pending(current))
break;
count = wait->error;
if (count)
break;
timeout = schedule_timeout(timeout);
}
우선 현재 태스크를 TASK_INTERRUPTIBLE 상태로 변경하여 대기 상태로 만든다. 아직 실행 중으로 schedule_timeout()를 만나면 그때 실질적으로 대기 상태가 되어 컨텍스트(context) 스위칭이 일어난다.
poll_list 등록된 ufds를 하나씩 poll 대기큐에서 어떤 이벤트가 발생했는지 우선 체크를 한다. 이는 do_pollfd()를 호출하는데, 이는 디바이스 드라이버 내의 poll 함수를 호출하게 된다. 여기서 poll_wait를 통해 실질적인 이벤트에 대한 대기큐를 poll 대기큐에 등록하게 된다. 이때 사용하는 것이 앞서