Linux #
구현 분석-- 김도집 2005-10-14
1.1 개요 #
커널 소스상에서 따라 가 보도록 한다.
최초 응용 프로그램에서
함수를 호출하면 리눅스 커널상의 sys_ 이라는 시스템 콜이 호출이 될 것이다. 따라서 여기서 시작하는 것으로 한다. 참고가 된 커널 소스의 버전은 2.6.11이다.
1.2 sys_ #
fs/select.c
asmlinkage long sys_ (struct fd __user * ufds, unsigned int nfds,
long timeout)
함수 원형을 보자면 위와 같다.
세개의 인자들은 응용 프로그램에서 사용하는
(2) 함수와 동일하므로 man 페이지를 참고하자. - nfds와 timeout에 대한 값을 검사한다. 사실 이부분은 적당한 값이 들어왔는지 확인 하는 것이기에 그냥 넘어가자.
- 처음으로 무엇인가 한다. sys_내부에서 struct _wqueues 형의 tables를 선언했다. 이를 초기화 해 주는 것으로 보인다. 우선 _wqueue 자료가 어떤건지 보자.
<linux/
} .h>
struct _wqueues {
_table pt;
struct _table_page * table;
int error; _initwait(&table) - 이제는
void
함수는 간단하다. 그런데 또 함수를 호출한다. ㅡㅡ. init_ _initwait(struct _wqueues *pwq){ _funcptr()를 따라가 보자.
init_ _funcptr(&pwq->pt, __ wait);
pwq->error = 0;
pwq->table = NULL;
}<linux/
결국 이는 .h> 를 위한 대기큐(waitqueue)인 pwq내의 필드 중 함수포인터인 qproc에 __ wait()를 등록하는 것이다.
static inline void init_ _funcptr( _table *pt, _queue_proc qproc){
pt->proc = qproc;
}
__ wait()함수는 select.c에 정의되어 있다. 이는 이벤트를 위한 대기큐를 대기큐의 테이블에 등록하는 역할을 한다. _initwait()를 보자 - 기타 초기값을 설정하고 fd 형의 ufds를 모두 _list 구조체에 연결리스트로 등록한다.
- do_
fdcount = do_
(nfds, head, &table, timeout); 을 호출한다. 자 이제 중요한 것이다. 실질적인 처리는 이 do_ 에서 처리한다. - do_
대충 sys_
에서 처리하는 것을 설정하였다. sys_ 에서는 응용 프로그램에서 넘어온 인자를 커널에서 처리하기 위한 사전 작업을 하고 그 이후 처리를 do_ ()함수에게 넘긴다. 처리된 것을 응용 프로그램에 넘기기 위해 revents의 값을 사용자 영역으로 복사해 넘겨주게 되는 것이다. 자, 이제 do_
에 대해 알아보자. [edit]
1.3 do_ #
fs/select.c
static int do_(unsigned int nfds, struct _list *list,
struct _wqueues *wait, long timeout)
do_
에서 실질적인 처리를 한다. 이는 for(;;)문으로 무한 루프를 돈다. 그렇다고 ing을 하는 것은 아니고 이벤트가 생길때까지 대기토록 한다. 우선 그 코드를 보면 다음과 같다. for (;;) {
struct _list *walk;
set_current_state(TASK_INTERRUPTIBLE);
walk = list;
while(walk != NULL) {
do_ fd( 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) 스위칭이 일어난다.
_list에 등록된 ufds를 하나씩 따라가면서 대기큐에서 어떤 이벤트가 발생했는지 우선 체크를 한다. 이는 do_ fd()를 호출하는데, 이는 디바이스 드라이버 내의 함수를 호출하게 된다.
do_
fd는 _wait를 통해 실질적인 이벤트에 대한 대기큐를 대기큐에 등록하게 된다. 이때 사용하는 것이 앞서 대기큐 내의 _table형인 pt에 등록된 qproc이 실행되게 된다(pt->proc = qproc로 sys_ 내용 참조). 디바이스 드라이버내의 함수 내에는 어떤 조건을 만족하면 mask 값을 반환하게 되는데 이 값을 fd형의 revents 필드에 mask값을 넣게 된다. 또한 mask 값이 0이 아니면 count를 하나씩 증가시켜 그값을 count 값에 저장하게 된다. 즉 count 값이 0이 아니거나 timeout이 발생하거나 signal_pending이 존재한다면 무한 루프를 빠져나오게 된다. 또는 error가 있을 경우에도 무한 루프를 빠져나오게 된다.
만
약 그렇지 않다면 schedule_timeout()를 통해 timeout이 되거나 wake_up이 될 때까지 현재 프로세스는
대기 상태로 있게 된다. 대기 상태에서 빠져나오면 다시 for문의 처음으로 가서 앞서 설명한 것을 반복하게 되고 do_
fd를 통해 mask 값과 count 값을 가져오게 되는 것이다. 최종적으로 for문을 빠져나오면 현재 프로세스의 상태를 TASK_RUNNING 상태로 변경하여 정상적으로 스케줄링이 가능토록 한다.
[edit]
1.4 do_ fd #
fs/select.c
static void do_fd(unsigned int num, struct fd * fdpage,
_table ** pwait, int *count)
do_
fd에 대한 설명은 do_ 에서 했다. 중요한 코드만 본다면 다음과 같다. if (fd >= 0) {
struct file * file = fget(fd);
mask = NVAL;
if (file != NULL) {
mask = DEFAULT_ MASK;
if (file->f_op && file->f_op-> )
mask = file->f_op-> (file, *pwait);
mask &= fdp->events | ERR | HUP;
fput(file);
}
if (mask) {
*pwait = NULL;
(*count)++;
}
}
fdp->revents = mask;
mask = file->f_op->
(file, *pwait) 를 통해 디바이스 드라이버의 함수를 호출하고 mask 값을 반환 받는다. 만약 mask가 0이 아니라면 count 값을 증가 시키고 mask는 revents에 저장하게 된다.이 글과 관련있는 글을 자동검색한 결과입니다 [?]
- 리눅스 커널 소스에서 표류 중... by 지아
- 미친감자~!리눅스란놈의 커널구조를 설명해보게 by 미친감자
- E by 샘이 * == *
'Linux' 카테고리의 다른 글
[Linux]명령 (Command) (0) | 2008.07.15 |
---|---|
[Linux] valgrind 메모리 릭 체크 (0) | 2008.07.14 |
[Linux]리눅스 관리자가 알아두어야 할 50가지 (0) | 2008.07.14 |
[Linux] iptables (0) | 2008.07.14 |
[Linux]GDB 사용법 (0) | 2008.07.14 |