FSTWikiDiff for 1.10 LinuxPoll
Login:
Password:
Join
E D R S I H P RSS
FrontPage|FindPage|TitleIndex|RecentChanges

Difference between r1.10 and the current

@@ -1,5 +1,5 @@
= Linux Poll 구현 분석 =
-- [김도집] [[DateTime(2005-10-14T06:45:14)]]

[[TableOfContents]]
== 개요 ==
@@ -24,7 +24,7 @@
사실 이부분은 적당한 값이 들어왔는지 확인 하는 것이기에 그냥 넘어가자.

1. poll_initwait(&table)
처음으로 무엇인가 한다. sys_poll 내부에서 strcut struct poll_wqueues 형의 tables를 선언했다. 이를 초기화 해 주는 것으로 보인다. 우선 poll_wqueue 자료가 어떤건지 보자.
{{{
<linux/poll.h>

@@ -73,7 +73,7 @@
struct poll_wqueues *wait, long timeout)
}}}

do_poll에서 실질적인 poll 처리를 한다. 이는 for(;;)문으로 무한 루프를 돈다. 그렇다고 polling을 하는 것은 아니고 이벤트가 생길때까지 대기토록 한다. 우선 그 코드를 보면 다음과 같다.
{{{
for (;;) {
struct poll_list *walk;
@@ -95,13 +95,13 @@

우선 현재 태스크를 TASK_INTERRUPTIBLE 상태로 변경하여 대기 상태로 만든다. 아직 실행 중으로 schedule_timeout()를 만나면 그때 실질적으로 대기 상태가 되어 컨텍스트(context) 스위칭이 일어난다.

poll_list 등록된 ufds를 하나씩 따라가면서 poll 대기큐에서 어떤 이벤트가 발생했는지 우선 체크를 한다. 이는 do_pollfd()를 호출하는데, 이는 디바이스 드라이버 내의 poll 함수를 호출하게 된다.

do_pollfd는 poll_wait를 통해 실질적인 이벤트에 대한 대기큐를 poll 대기큐에 등록하게 된다. 이때 사용하는 것이 앞서 poll 대기큐 내의 poll_table형인 pt에 등록된 qproc이 실행되게 된다(pt->proc = qproc로 sys_poll 내용 참조). 디바이스 드라이버내의 poll 함수 내에는 어떤 조건을 만족하면 mask 값을 반환하게 되는데 이 값을 pollfd형의 revents 필드에 mask값을 넣게 된다. 또한 mask 값이 0이 아니면 count를 하나씩 증가시켜 그값을 count 값에 저장하게 된다.

즉 count 값이 0이 아니거나 timeout이 발생하거나 signal_pending이 존재한다면 무한 루프를 빠져나오게 된다. 또는 error가 있을 경우에도 무한 루프를 빠져나오게 된다.

만약 그렇지 않다면 schedule_timeout()를 통해 timeout이 되거나 wake_up이 될 때까지 현재 프로세스는 대기 상태로 있게 된다. 대기 상태에서 빠져나오면 다시 for문의 처음으로 가서 앞서 설명한 것을 반하게 되고 do_pollfd를 통해 mask 값과 count 값을 가져오게 되는 것이다.

최종적으로 for문을 빠져나오면 현재 프로세스의 상태를 TASK_RUNNING 상태로 변경하여 정상적으로 스케줄링이 가능토록 한다.

@@ -134,7 +134,6 @@
}}}

mask = file->f_op->poll(file, *pwait) 를 통해 디바이스 드라이버의 poll 함수를 호출하고 mask 값을 반환 받는다. 만약 mask가 0이 아니라면 count 값을 증가 시키고 mask는 revents에 저장하게 된다.





Linux Poll 구현 분석 #

-- 김도집 2005-10-14

Contents

1 Linux Poll 구현 분석
1.1 개요
1.2 sys_poll
1.3 do_poll
1.4 do_pollfd

1.1 개요 #

커널 소스상에서 따라 가 보도록 한다. ^^

최초 응용 프로그램에서 poll 함수를 호출하면 리눅스 커널상의 sys_poll 이라는 시스템 콜이 호출이 될 것이다. 따라서 여기서 시작하는 것으로 한다.

참고가 된 커널 소스의 버전은 2.6.11이다.

1.2 sys_poll #

fs/select.c

asmlinkage long sys_poll(struct pollfd __user * ufds, unsigned int nfds, 
                         long timeout)

함수 원형을 보자면 위와 같다.

세개의 인자들은 응용 프로그램에서 사용하는 poll(2) 함수와 동일하므로 man 페이지를 참고하자.

  1. nfds와 timeout에 대한 값을 검사한다.
    사실 이부분은 적당한 값이 들어왔는지 확인 하는 것이기에 그냥 넘어가자.

  2. poll_initwait(&table)
    처음으로 무엇인가 한다. sys_poll 내부에서 struct poll_wqueues 형의 tables를 선언했다. 이를 초기화 해 주는 것으로 보인다. 우선 poll_wqueue 자료가 어떤건지 보자.
  3. <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 대기큐의 테이블에 등록하는 역할을 한다.

  4. 기타 초기값을 설정하고 pollfd 형의 ufds를 모두 poll_list 구조체에 연결리스트로 등록한다.
  5. do_poll을 호출한다. 자 이제 중요한 것이다. 실질적인 poll 처리는 이 do_poll에서 처리한다.
    fdcount = do_poll(nfds, head, &table, timeout);
    
  6. 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(;;)문으로 무한 루프를 돈다. 그렇다고 polling을 하는 것은 아니고 이벤트가 생길때까지 대기토록 한다. 우선 그 코드를 보면 다음과 같다.
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 함수를 호출하게 된다.

do_pollfd는 poll_wait를 통해 실질적인 이벤트에 대한 대기큐를 poll 대기큐에 등록하게 된다. 이때 사용하는 것이 앞서 poll 대기큐 내의 poll_table형인 pt에 등록된 qproc이 실행되게 된다(pt->proc = qproc로 sys_poll 내용 참조). 디바이스 드라이버내의 poll 함수 내에는 어떤 조건을 만족하면 mask 값을 반환하게 되는데 이 값을 pollfd형의 revents 필드에 mask값을 넣게 된다. 또한 mask 값이 0이 아니면 count를 하나씩 증가시켜 그값을 count 값에 저장하게 된다.

즉 count 값이 0이 아니거나 timeout이 발생하거나 signal_pending이 존재한다면 무한 루프를 빠져나오게 된다. 또는 error가 있을 경우에도 무한 루프를 빠져나오게 된다.

만약 그렇지 않다면 schedule_timeout()를 통해 timeout이 되거나 wake_up이 될 때까지 현재 프로세스는 대기 상태로 있게 된다. 대기 상태에서 빠져나오면 다시 for문의 처음으로 가서 앞서 설명한 것을 반복하게 되고 do_pollfd를 통해 mask 값과 count 값을 가져오게 되는 것이다.

최종적으로 for문을 빠져나오면 현재 프로세스의 상태를 TASK_RUNNING 상태로 변경하여 정상적으로 스케줄링이 가능토록 한다.

1.4 do_pollfd #

fs/select.c

static void do_pollfd(unsigned int num, struct pollfd * fdpage,
                      poll_table ** pwait, int *count)

do_pollfd에 대한 설명은 do_poll에서 했다. 중요한 코드만 본다면 다음과 같다.
if (fd >= 0) {
        struct file * file = fget(fd);
        mask = POLLNVAL;
        if (file != NULL) {
                mask = DEFAULT_POLLMASK;
                if (file->f_op && file->f_op->poll)
                        mask = file->f_op->poll(file, *pwait);
                mask &= fdp->events | POLLERR | POLLHUP;
                fput(file);
        }
        if (mask) {
                *pwait = NULL;
                (*count)++;
        }
}
fdp->revents = mask;

mask = file->f_op->poll(file, *pwait) 를 통해 디바이스 드라이버의 poll 함수를 호출하고 mask 값을 반환 받는다. 만약 mask가 0이 아니라면 count 값을 증가 시키고 mask는 revents에 저장하게 된다.

last modified 2005-10-14 16:54:22
ShowPage|FindPage|DeletePage|LikePages Valid XHTML 1.0! Valid CSS! powered by MoniWiki
0.0492 sec