{{{#!jade
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V4.1//EN" "../docbook/dtd/docbook-xml-4.1.2/docbookx.dtd">

<article lang="ko">

 <articleinfo>

  <title>Kernel Analysis-HOWTO</title>

  <author>
     <firstname>Roberto Arcomano</firstname>
     <affiliation>
        <address>
           <email>berto (at) bertolinux.com</email>
        </address>
     </affiliation>
  </author>
 
  <othercredit role="translator">
     <firstname>김남형</firstname>
     <affiliation>
        <address>
           <email>pastime (at) ece.uos.ac.kr</email>
        </address>
     </affiliation>
  </othercredit>

   <revhistory>
      <revision>
         <revnumber>0.7</revnumber>
         <date>2003-03-26</date>
      </revision>
   </revhistory>

  <preface>
    <para>
	  이 문서에서는 리눅스 커널에 관한 내용 - 중요한 컴포넌트가 무엇이며, 그들이 어떻게 동작하는지 - 을 설명하고자 한다. 이 HOWTO 문서는 커널 소스에서 '특정 함수' 가 어떻게 선언되었고, 정의되었으며 다른 함수들과 연결되는지를 찾고자 하는 독자들에게 도움이 될 것이다. 이 문서의 최신 버전은 <ulink url="http://www.bertolinux.com">http://www.bertolinux.com</ulink> 에서 찾아볼 수 있다. 이 문서에 대한 개선점이나 제안 사항이 있다면 당신의 아이디어를 다음의 메일 주소를 통해 보내주기 바란다: <email>berto (at) bertolinux.com</email>
    </para>
  </preface>

 </articleinfo>



 <sect1 id="intro">
   <title>서문</title>

  <sect2 id="introduction">
   <title>서문</title>

   <para>
     이 문서에서는 리눅스 커널을 구성하는 각 부분을 정의하고 그 부분을 수행하는 주된 함수와 자료 구조, 그리고 이들이 어떻게 동작하는지 (how the "wheel spins") 를 설명하려고 한다. 이 문서의 최신 버전은 <ulink url="http://www.bertolinux.com">http://www.bertolinux.com</ulink> 에서 찾아볼 수 있다. 이 문서에 대한 개선점이나 제안 사항이 있다면 당신의 아이디어를 다음의 메일 주소를 통해 보내주기 바란다: <email>berto (at) bertolinux.com</email>. 이 문서에 사용된 코드들은 이 HOWTO 문서가 쓰여지고 있는 시점에서 최신 안정 버전인 리눅스 커널 버전 2.4.x 를 참조하였다.
   </para>

  </sect2>


  <sect2 id="copyright">
   <title>저작권 정보</title>

   <para>
    Copyright (C) 2000,2001,2002 Roberto Arcomano. 
    Copyright (C) 2003 김남형 (한국어판).
	본 문서는 여러분이 자유 소프트웨어 재단(Free Software Foundation)에서 발행한 GNU 라이브러리 일반 공개 사용권(GNU Library General Public License)의 규정(즉 사용권 2판이나 혹은 여러분의 선택에 따라 더 이후의 사용권)을 준수하는 한 재유포하거나 수정할 수 있는 자유 문서이다.  본 문서는 이것이 유용하게 사용되기를 바라는 마음으로 배포하는 것이지만, 그에 대한 어떠한 보증도 하지 않는다: 상업적인 용도나 특정한 목적에 맞게 사용 했을때 의무적으로 따라 붙는 보증도 없다. 더 자세한 내용은 GNU 라이브러리 일반 대중 사용권 (GPL) 에 대해 보기 바란다. GNU GPL 의 복사본은 <ulink url="http://www.gnu.org/copyleft/gpl.html">여기</ulink> 에서 얻을 수 있다.
   </para>

  </sect2>


  <sect2 id="translations">
   <title>번역에 관하여</title>

   <para>
     당신이 이 문서를 번역하는 것은 자유이다. 하지만 그전에 다음과 같은 사항들을 참고해야 할 필요가 있다:
	 <orderedlist>
	   <listitem>
	     <para>
		  당신이 번역해서 올리고자 하는 LDP 에 이미 이 문서의 번역본이 있는지 체크한다.
		 </para>
	   </listitem>
	   <listitem>
	     <para>
		  <xref linkend="intro">&nbsp;</xref> 절을 유지한다. (<xref linkend="introduction">&nbsp;</xref>, <xref linkend="copyright">&nbsp;</xref>, <xref linkend="translations">&nbsp;</xref>, <xref linkend="credits">&nbsp;</xref> 을 포함)
		 </para>
	   </listitem>
	 </orderedlist>
   </para>

   <para>
    <note>
	 <para>
      TXT 파일이나 HTML 파일을 직접 번역해서는 안되고 LYX 파일을 수정해야 한다. 그래야만 이를 이용해 다른 파일 형식 (TXT, HTML, RIFF 등) 으로 변환할 수 있다. "LyX" 프로그램을 사용하기 위해서 <ulink url="http://www.lyx.org">http://www.lyx.org</ulink> 에서 다운로드 받을 수 있다.
	 </para>
	</note>
   </para>

   <para>
     번역을 위해서 나에게 질문을 할 필요는 없다. 그저 당신의 번역물에 관해 (당신이 원한다면) 내가 알 수 있게만 해주면 된다.
   </para>

   <para>
     당신의 번역에 감사한다!
   </para>

  </sect2>


  <sect2 id="credits">
    <title>감사의 글</title>

    <para>
	  이 문서를 빨리 올려주고 발표해 준 <ulink url="http://tldp.org">The Linux Documentation Project</ulink> 에 감사한다.
	</para>

	<para>
	  Klaas de Waal 의 제안에 감사한다.
	</para>

  </sect2>

 </sect1>



 <sect1 id="syntax_used">
   <title>사용된 문법</title>

   <sect2 id="function_syntax">
     <title>함수의 문법</title>

	 <para>
	  함수에 대해서 설명할 때는 다음과 같은 형태로 표기한다:
	 </para>

     <para>
	  <synopsis id="function_synopsis">
	   "함수 이름 [ 파일 위치 . 확장자 ]"
	  </synopsis>
	 </para>

	 <para>
	  예를 들어:
	 </para>

     <para>
	  <example id="function_syntax_ex1">
	   <para>
	    "schedule [ kernel/sched.c ]"
	   </para>
	  </example>
	 </para>

     <para>
	  라는 표현이 있을 때 이것은 우리가 지금 
	 </para>

	 <para>
	  <example id="function_syntax_ex2">
	   <para>
	    "schedule"
	   </para>
	  </example>
	 </para>

	 <para>
	  이라는 함수에 관해서 설명하고 있음을 나타내며, 이 함수는 
	 </para>

	 <para>
	  <example id="function_syntax_ex3">
	   <para>
	    [ kernel/sched.c ]
	   </para>
	  </example>
	 </para>

	 <para>
	  라는 파일에서 찾아볼 수 있음을 뜻한다.
	 </para>

	 <para>
	  <note>
	   <para>
	    여기서는 시작 디렉토리가 <filename>/usr/src/linux</filename> 라고 가정하고 있는 것이다.
	   </para>
	  </note>
	 </para>

   </sect2>


   <sect2 id="indentation">
     <title>들여쓰기</title>

	 <para>
	  소스 코드 안에서의 들여쓰기에는 3개의 공백문자가 사용되었다.
	 </para>

   </sect2>


   <sect2 id="intercalling_analysis">
     <title>함수간 호출 분석 (InterCalling Analysis)</title>

	  <simplesect id="ica_overview">
	   <title>개요</title>

	   <para>
	    이 문서에서는 커널 함수들의 호출 관계를 표현하기 위해 (들여쓰기의 방식으로) "함수간 호출 분석 (InterCalling Analysis : ICA)" 을 사용한다.
	   </para>
	   
	   <para>
	    예를 들어 <function>sleep_on</function> 이라는 함수를 함수간 호출 분석을 통해 표현하면 아래와 같다:
	   </para>

	   <para>
	    <screen>
		  
|sleep_on
|init_waitqueue_entry      --
|__add_wait_queue            |   enqueuing request  
   |list_add                 |
      |__list_add          -- 
   |schedule              ---     waiting for request to be executed
      |__remove_wait_queue --   
      |list_del              |   dequeuing request
         |__list_del       -- 
 
                          sleep_on ICA
        </screen>
	   </para>

	   <para>
	    들여쓰기로 표현된 함수간 호출 분석 뒤에는 함수의 위치가 나온다:
	   </para>

	   <para>
	    <itemizedlist>

		 <listitem> 
		  <para> sleep_on [kernel/sched.c] </para>
		 </listitem>

		 <listitem> 
		  <para> init_waitqueue_entry [include/linux/wait.h] </para>
		 </listitem>

		 <listitem> 
		  <para> __add_wait_queue </para>
		 </listitem>

		 <listitem> 
		  <para> list_add [include/linux/list.h] </para>
		 </listitem>

		 <listitem> 
		  <para> __list_add </para>
		 </listitem>

		 <listitem> 
		  <para> schedule [kernel/sched.c] </para>
		 </listitem>

		 <listitem> 
		  <para> __remove_wait_queue [include/linux/wait.h] </para>
		 </listitem>

		 <listitem> 
		  <para> list_del [include/linux/list.h] </para>
		 </listitem>

		 <listitem> 
		  <para> __list_del </para>
		 </listitem>

		</itemizedlist>
	   </para>

	   <para>
	    <note>
		 <para>
		  함수의 위치가 바로 앞에 표시한 함수의 위치와 같은 경우에는 명시하지 않았다.
		 </para>
		</note>
	   </para>

	  </simplesect>

	  <simplesect id="ica_details">
	   <title>세부사항</title>

	   <para>
	    함수간 호출 분석에서 다음과 같은 형태가 있을 것이다:
	   </para>

	   <para>
	    <synopsis id="ica_details_synopsis1">
function1 -> function2
		</synopsis>
	   </para>

	   <para>
	    이것은 <function>function1</function> 은 다른 함수를 가리키는 일반적인 포인터라는 것을 뜻한다. 위의 경우에서 <function>function1</function> 은 <function>function2</function> 를 가리킨다. 
	   </para>

	   <para>
	    또 다음과 같은 형태에서는:
	   </para>

	   <para>
	    <synopsis id="ica_details_synopsis2">
function:
		</synopsis>
	   </para>

	   <para>
	    <function>function</function> 은 함수가 아님을 뜻한다. 이것은 레이블이다 (일반적으로 어셈블러 레이블일 것이다).
	   </para>

	   <para>
	    이 문서에서는 많은 절에서 <emphasis>C</emphasis> 코드의 형태나 <emphasis>슈도-코드</emphasis> 의 형태로 표현하였다. 실제의 소스 파일에는 이것들이 <emphasis>어셈블러</emphasis> 코드나 <emphasis>구조화 되지 않은</emphasis> 코드의 형태로 들어있을 수도 있다. 이러한 차이점은 학습을 위한 것이다.
	   </para>

	  </simplesect>

	  <simplesect id="pros_of_using_ica">
	   <title>함수간 호출 분석의 장점</title>

	   <para>
	    함수간 호출 분석 (ICA) 를 사용하는데 있어서 다음과 같은 많은 장점들이 있다:
	   </para>

	   <para>
	    <itemizedlist>

		 <listitem>
		  <para> 커널 함수를 호출하였을 때 어떤 일이 일어나는 가를 전체적으로 알 수 있다. </para>
		 </listitem>

		 <listitem>
		  <para> 함수의 뒤에 함수의 위치가 나오므로, 함수간 호출 분석을 하나의 <emphasis>함수의 레퍼런스</emphasis> 로도 사용할 수 있다. </para>
		 </listitem>

		 <listitem>
		  <para>
		   함수간 호출 분석은 sleep/awake 메카니즘에서 유용하다. 이를 이용해 프로세스가 sleep 되기 전에 어떤 일을 하는지와 (스케줄 이후에) wake up 이후에 무슨 일을 할 것인지를 볼 수 있다.
		  </para>
		 </listitem>

		</itemizedlist>
	   </para>

	  </simplesect>

	  <simplesect id="contros_of_using_ica">
	   <title>함수간 호출 분석의 단점</title>

	   <para>
	    함수간 호출 분석 (ICA) 를 사용하는데 있어서 다음과 같은 단점들이 있다:
	   </para>

	   <para>
	    <itemizedlist>

		 <listitem>
		  <para> 모든 이론적인 모델에 있어서, 실제 소스코드와 특정한 조건들과 같은 자세한 사항들을 제외하고 단순화 시켰다. </para>
		 </listitem>

		 <listitem>
		  <para> 스택의 상태, 데이타의 값 등을 표현하기 위해서는 추가적인 표현이 추가되어야 한다. </para>
		 </listitem>

		</itemizedlist>
	   </para>

	  </simplesect>

   </sect2>

 </sect1>



 <sect1 id="fundamentals">
   <title>기초적인 내용</title>

   <sect2 id="what_is_kernel">
    <title>커널이란 무엇인가?</title>

	<para>
	 커널이란 컴퓨터 시스템의 <emphasis>핵심(core)</emphasis> 부분을 말한다. 
	 즉, 컴퓨터의 자원들을 사용자가 공유할 수 있도록 해주는 <emphasis>소프트웨어</emphasis> 이다.
	</para>

	<para>
	 커널은 (그래픽을 이용한 관리 프로그램도 포함할 수 있는) 운영체제의 주요 소프트웨어라고 생각할 수 있을 것이다.
	</para>

	<para>
	 예를 들어, (다른 Unix 기반의 운영체제와 같이) 리눅스에서는 X 윈도우 환경은 커널에 속해있지 않다. 왜냐하면 X 윈도우 환경은 오직 그래픽 연산 만을 관리하기 때문이다. (비디오 카드 장치에 접근하기 위해서 사용자 모드의 I/O 를 사용한다.)
	</para>

	<para>
	 대조적으로 윈도우즈 환경 (Win9x, WinME, WinNT, Win2K, WinXP 등) 에서는 커널과 그래픽 환경이 혼합되어 있다.
	</para>

   </sect2>


   <sect2 id="diference_between_UM_and_KM">
    <title>사용자 모드와 커널 모드</title>

	<simplesect id="diference_overview">
	 <title>개요</title>

	 <para>
	  (컴퓨터가 방 한개를 차지할 만큼의 크기였던) 예전에는 사용자들이 응용 프로그램을 실행시키는 것도 무척 힘든 일이었고, 때때로 응용 프로그램이 컴퓨터를 망가뜨리기도 하였다.
	 </para>

	</simplesect>

	<simplesect id="operative_modes">
	 <title>동작 모드</title>

	 <para>
	  위와 같이 응용 프로그램이 시스템을 망가뜨리는 것을 방지하기 위해, 이후의 운영체제들은 2 가지의 다른 동작 모드를 갖도록 디자인되었다.
	 </para>

	 <para>
	  <orderedlist>

	   <listitem>
	    <para> 커널 모드: 중요한 자료구조나 직접적인 하드웨어 접근 (I/O 혹은 memory mapped), 직접적인 메모리 접근, IRQ, DMA 등의 처리 </para>
	   </listitem>

	   <listitem>
	    <para> 사용자 모드: 사용자가 응용 프로그램을 실행할 수 있음 </para>
	   </listitem>

	  </orderedlist>
	 </para>

	 <para>
	  <screen>
               |          Applications           /|\
               |         ______________           |
               |         | User Mode  |           |  
               |         ______________           | 
               |               |                  |  
Implementation |        _______ _______           |   Abstraction
    Detail     |        | Kernel Mode |           |
               |        _______________           |
               |               |                  |
               |               |                  | 
               |               |                  |
              \|/          Hardware               |
	  </screen>
	 </para>

	 <para>
	  커널 모드에서는 사용자 모드의 응용 프로그램이 시스템 자체나 시스템의 기능에 손상을 입히는 것을 <emphasis>방지</emphasis> 한다.
	 </para>

	 <para>
	  근래의 마이크로 프로세스들은 하드웨어 적으로 최소한 2 가지 이상의 상태를 구현하고 있다. 예를 들어 인텔에서는, 4 가지 상태로 PL (Privilege Level: 특권 레벨) 결정한다. 즉, 0, 1, 2, 3 의 4 가지 상태를 사용할 수 있으며 이 중에서 커널 모드는 0 을 사용한다. 
	 </para>

	 <para>
	  유닉스 기반의 운영체제는 오직 2 단계의 특권 레벨을 필요로 하며, 앞으로 이러한 개념이 코드 상에서 어떻게 이용되는지 보게 될 것이다.
	 </para>

	</simplesect>

   </sect2>


   <sect2 id="switching_from_UM_to_KM">
    <title>사용자 모드에서 커널 모드로 전환</title>

	<simplesect id="when_do_we_switch">
	 <title>언제 이러한 전환이 일어나는가?</title>

	 <para>
	  위의 2 가지 모드를 이해했다면, 다음으로 이러한 2 가지 모드 사이의 전환이 언제 일어나는지를 알아야 한다.
	 </para>

	 <para>
	  일반적으로 이러한 전환이 일어나는 경우는 다음의 2 가지 경우이다:
	 </para>

	 <para>
	  <orderedlist>

	   <listitem>
	    <para>
	     시스템 콜을 호출했을 때: 시스템 콜을 호출한 이후에, 프로세스는 자동적으로 커널 모드에 있는 코드를 호출한다.
		</para>
	   </listitem>

	   <listitem>
	    <para>
	     IRQ (혹은 예외) 가 발생했을 때: 인터럽트 요청 (InterRupt reQuest: <abbrev>IRQ</abbrev>) 이 발생한 후에, 인터럽트 핸들러 (혹은 예외 핸들러) 가 호출되고, (인터럽트 처리가 끝나면) 제어경로는 아무 일도 없었던 듯이 (사용자 모드의) 프로세스로 돌아간다.
		 </para>
	   </listitem>

	  </orderedlist>
	 </para>

	</simplesect>

	<simplesect id="system_calls">
	 <title>시스템 콜</title>

	 <para>
	  시스템 콜은 커널 모드 내에 있는 운영체제 루틴들을 관리하는 특별한 함수라고 할 수 있다.
	 </para>

	 <para>
	  시스템 콜은 다음과 같은 경우에 호출된다:
	 </para>

	 <para>
	  <itemizedlist>

	   <listitem>
	    <para> I/O 장치나 파일에 접근할 때 (read/write) </para>
	   </listitem>

	   <listitem>
	    <para> 특권이 있는 정보에 접근할 필요가 있을 때 (pid, 스케줄링 정책 등) </para>
	   </listitem>

	   <listitem>
	    <para> 실행 환경을 바꿔야 할 때 (<function>fork</function>, <function>exec</function>) </para>
	   </listitem>

	   <listitem>
	    <para> 특정한 명령을 수행해야 할 때 (<function>chdir</function>, <function>kill</function>, <function>brk</function>, <function>signal</function> 등) </para>
	   </listitem>

	  </itemizedlist>
	 </para>

	 <para>
	  <screen>
                                 |                |
                         ------->| System Call i  | (Accessing Devices)
|                |       |       |  [sys_read()]  |
| ...            |       |       |                |
| system_call(i) |--------       |                |
|   [read()]     |               |                |
| ...            |               |                |
| system_call(j) |--------       |                |  
|   [get_pid()]  |       |       |                |
| ...            |       ------->| System Call j  | (Accessing kernel data structures)
|                |               |  [sys_getpid()]|
                                 |                | 
 
    USER MODE                        KERNEL MODE
 
  
                        Unix System Calls Working 
	  </screen>
	 </para>

	 <para>
	  시스템 콜은 사용자 모드에서 로우 레벨의 자원 (하드웨어) 과 통신을 위해 사용할 수 있는 거의 유일한 인터페이스이다. 이에 대한 단 하나의 예외 사항으로는 프로세스에서 <function>ioperm</function> 을 호출한 경우이다. 이 경우에는 사용자 모드에서 장치에 직접 접근할 수 있다. (IRQ 는 사용되지 않는다.)
	 </para>

	 <para>
	  <note>
	   <para> 모든 <emphasis>C</emphasis> 언어 함수가 시스템 콜인 것이 아니라 그중 일부 만이 시스템 콜이다. </para>
	  </note>
	 </para>

	 <para>
	  다음의 리스트는 리눅스 커널 2.4.17 의 시스템 콜의 목록을 보여준다. [ arch/i386/kernel/entry.S ]
	  <footnote>
	   <para>
	    역자주: 현재 최신 커널 버전인 2.4.21 에는 이보다 더 많은 시스템 콜의 목록이 있지만 지금 단계에서는 그 차이점에 큰 의미가 없을 것 같아 그냥 원문에 있는 대로만 목록을 작성하였다. 관심이 있는 사람은 해당 파일을 직접 확인해 보기 바란다.
	   </para>
	  </footnote>
	 </para>

	 <para>
	  <screen>
.long SYMBOL_NAME(sys_ni_syscall)       /* 0  -  old "setup()" system call*/
        .long SYMBOL_NAME(sys_exit)
        .long SYMBOL_NAME(sys_fork)
        .long SYMBOL_NAME(sys_read)
        .long SYMBOL_NAME(sys_write)
        .long SYMBOL_NAME(sys_open)             /* 5 */
        .long SYMBOL_NAME(sys_close)
        .long SYMBOL_NAME(sys_waitpid)
        .long SYMBOL_NAME(sys_creat)
        .long SYMBOL_NAME(sys_link)
        .long SYMBOL_NAME(sys_unlink)           /* 10 */
        .long SYMBOL_NAME(sys_execve)
        .long SYMBOL_NAME(sys_chdir)
        .long SYMBOL_NAME(sys_time)
        .long SYMBOL_NAME(sys_mknod)
        .long SYMBOL_NAME(sys_chmod)            /* 15 */
        .long SYMBOL_NAME(sys_lchown16)
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old break syscall holder */
        .long SYMBOL_NAME(sys_stat)
        .long SYMBOL_NAME(sys_lseek)
        .long SYMBOL_NAME(sys_getpid)           /* 20 */
        .long SYMBOL_NAME(sys_mount)
        .long SYMBOL_NAME(sys_oldumount)
        .long SYMBOL_NAME(sys_setuid16)
        .long SYMBOL_NAME(sys_getuid16)
        .long SYMBOL_NAME(sys_stime)            /* 25 */
        .long SYMBOL_NAME(sys_ptrace)
        .long SYMBOL_NAME(sys_alarm)
        .long SYMBOL_NAME(sys_fstat)
        .long SYMBOL_NAME(sys_pause)
        .long SYMBOL_NAME(sys_utime)            /* 30 */
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old stty syscall holder */
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old gtty syscall holder */
        .long SYMBOL_NAME(sys_access)
        .long SYMBOL_NAME(sys_nice)
        .long SYMBOL_NAME(sys_ni_syscall)       /* 35 */                /* old ftime syscall holder */
        .long SYMBOL_NAME(sys_sync)
        .long SYMBOL_NAME(sys_kill)
        .long SYMBOL_NAME(sys_rename)
        .long SYMBOL_NAME(sys_mkdir)
        .long SYMBOL_NAME(sys_rmdir)            /* 40 */
        .long SYMBOL_NAME(sys_dup)
        .long SYMBOL_NAME(sys_pipe)
        .long SYMBOL_NAME(sys_times)
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old prof syscall holder */
        .long SYMBOL_NAME(sys_brk)              /* 45 */
        .long SYMBOL_NAME(sys_setgid16)
        .long SYMBOL_NAME(sys_getgid16)
        .long SYMBOL_NAME(sys_signal)
        .long SYMBOL_NAME(sys_geteuid16)
        .long SYMBOL_NAME(sys_getegid16)        /* 50 */
        .long SYMBOL_NAME(sys_acct)
        .long SYMBOL_NAME(sys_umount)                                   /* recycled never used phys() */
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old lock syscall holder */
        .long SYMBOL_NAME(sys_ioctl)
        .long SYMBOL_NAME(sys_fcntl)            /* 55 */
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old mpx syscall holder */
        .long SYMBOL_NAME(sys_setpgid)
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old ulimit syscall holder */
        .long SYMBOL_NAME(sys_olduname)
        .long SYMBOL_NAME(sys_umask)            /* 60 */
        .long SYMBOL_NAME(sys_chroot)
        .long SYMBOL_NAME(sys_ustat)
        .long SYMBOL_NAME(sys_dup2)
        .long SYMBOL_NAME(sys_getppid)
        .long SYMBOL_NAME(sys_getpgrp)          /* 65 */
        .long SYMBOL_NAME(sys_setsid)
        .long SYMBOL_NAME(sys_sigaction)
        .long SYMBOL_NAME(sys_sgetmask)
        .long SYMBOL_NAME(sys_ssetmask)
        .long SYMBOL_NAME(sys_setreuid16)       /* 70 */
        .long SYMBOL_NAME(sys_setregid16)
        .long SYMBOL_NAME(sys_sigsuspend)
        .long SYMBOL_NAME(sys_sigpending)
        .long SYMBOL_NAME(sys_sethostname)
        .long SYMBOL_NAME(sys_setrlimit)        /* 75 */
        .long SYMBOL_NAME(sys_old_getrlimit)
        .long SYMBOL_NAME(sys_getrusage)
        .long SYMBOL_NAME(sys_gettimeofday)
        .long SYMBOL_NAME(sys_settimeofday)
        .long SYMBOL_NAME(sys_getgroups16)      /* 80 */
        .long SYMBOL_NAME(sys_setgroups16)
        .long SYMBOL_NAME(old_select)
        .long SYMBOL_NAME(sys_symlink)
        .long SYMBOL_NAME(sys_lstat)
        .long SYMBOL_NAME(sys_readlink)         /* 85 */
        .long SYMBOL_NAME(sys_uselib)
        .long SYMBOL_NAME(sys_swapon)
        .long SYMBOL_NAME(sys_reboot)
        .long SYMBOL_NAME(old_readdir)
        .long SYMBOL_NAME(old_mmap)             /* 90 */
        .long SYMBOL_NAME(sys_munmap)
        .long SYMBOL_NAME(sys_truncate)
        .lng SYMBOL_NAME(sys_ftruncate)
        .long SYMBOL_NAME(sys_fchmod)
        .long SYMBOL_NAME(sys_fchown16)         /* 95 */
        .long SYMBOL_NAME(sys_getpriority)
        .long SYMBOL_NAME(sys_setpriority)
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old profil syscall holder */
        .long SYMBOL_NAME(sys_statfs)
        .long SYMBOL_NAME(sys_fstatfs)          /* 100 */
        .long SYMBOL_NAME(sys_ioperm)
        .long SYMBOL_NAME(sys_socketcall)
        .long SYMBOL_NAME(sys_syslog)
        .long SYMBOL_NAME(sys_setitimer)
        .long SYMBOL_NAME(sys_getitimer)        /* 105 */
        .long SYMBOL_NAME(sys_newstat)
        .long SYMBOL_NAME(sys_newlstat)
        .long SYMBOL_NAME(sys_newfstat)
        .long SYMBOL_NAME(sys_uname)
        .long SYMBOL_NAME(sys_iopl)             /* 110 */
        .long SYMBOL_NAME(sys_vhangup)
        .long SYMBOL_NAME(sys_ni_syscall)       /* old "idle" system call */
        .long SYMBOL_NAME(sys_vm86old)
        .long SYMBOL_NAME(sys_wait4)
        .long SYMBOL_NAME(sys_swapoff)          /* 115 */
        .long SYMBOL_NAME(sys_sysinfo)
        .long SYMBOL_NAME(sys_ipc)
        .long SYMBOL_NAME(sys_fsync)
        .long SYMBOL_NAME(sys_sigreturn)
        .long SYMBOL_NAME(sys_clone)            /* 120 */
        .long SYMBOL_NAME(sys_setdomainname)
        .long SYMBOL_NAME(sys_newuname)
        .long SYMBOL_NAME(sys_modify_ldt)
        .long SYMBOL_NAME(sys_adjtimex)
        .long SYMBOL_NAME(sys_mprotect)         /* 125 */
        .long SYMBOL_NAME(sys_sigprocmask)
        .long SYMBOL_NAME(sys_create_module)
        .long SYMBOL_NAME(sys_init_module)
        .long SYMBOL_NAME(sys_delete_module)
        .long SYMBOL_NAME(sys_get_kernel_syms)  /* 130 */
        .long SYMBOL_NAME(sys_quotactl)
        .long SYMBOL_NAME(sys_getpgid)
        .long SYMBOL_NAME(sys_fchdir)
        .long SYMBOL_NAME(sys_bdflush)
        .long SYMBOL_NAME(sys_sysfs)            /* 135 */
        .long SYMBOL_NAME(sys_personality)
        .long SYMBOL_NAME(sys_ni_syscall)       /* for afs_syscall */
        .long SYMBOL_NAME(sys_setfsuid16)
        .long SYMBOL_NAME(sys_setfsgid16)
        .long SYMBOL_NAME(sys_llseek)           /* 140 */
        .long SYMBOL_NAME(sys_getdents)
        .long SYMBOL_NAME(sys_select)
        .long SYMBOL_NAME(sys_flock)
        .long SYMBOL_NAME(sys_msync)
        .long SYMBOL_NAME(sys_readv)            /* 145 */
        .long SYMBOL_NAME(sys_writev)
        .long SYMBOL_NAME(sys_getsid)
        .long SYMBOL_NAME(sys_fdatasync)
        .long SYMBOL_NAME(sys_sysctl)
        .long SYMBOL_NAME(sys_mlock)            /* 150 */
        .long SYMBOL_NAME(sys_munlock)
        .long SYMBOL_NAME(sys_mlockall)
        .long SYMBOL_NAME(sys_munlockall)
        .long SYMBOL_NAME(sys_sched_setparam)
        .long SYMBOL_NAME(sys_sched_getparam)   /* 155 */
        .long SYMBOL_NAME(sys_sched_setscheduler)
        .long SYMBOL_NAME(sys_sched_getscheduler)
        .long SYMBOL_NAME(sys_sched_yield)
        .long SYMBOL_NAME(sys_sched_get_priority_max)
        .long SYMBOL_NAME(sys_sched_get_priority_min)  /* 160 */
        .long SYMBOL_NAME(sys_sched_rr_get_interval)
        .long SYMBOL_NAME(sys_nanosleep)
        .long SYMBOL_NAME(sys_mremap)
        .long SYMBOL_NAME(sys_setresuid16)
        .long SYMBOL_NAME(sys_getresuid16)      /* 165 */
        .long SYMBOL_NAME(sys_vm86)
        .long SYMBOL_NAME(sys_query_module)
        .long SYMBOL_NAME(sys_poll)
        .long SYMBOL_NAME(sys_nfsservctl)
        .long SYMBOL_NAME(sys_setresgid16)      /* 170 */
        .long SYMBOL_NAME(sys_getresgid16)
        .long SYMBOL_NAME(sys_prctl)
        .long SYMBOL_NAME(sys_rt_sigreturn)
        .long SYMBOL_NAME(sys_rt_sigaction)
        .long SYMBOL_NAME(sys_rt_sigprocmask)   /* 175 */
        .long SYMBOL_NAME(sys_rt_sigpending)
        .long SYMBOL_NAME(sys_rt_sigtimedwait)
        .long SYMBOL_NAME(sys_rt_sigqueueinfo)
        .long SYMBOL_NAME(sys_rt_sigsuspend)
       .long SYMBOL_NAME(sys_pread)            /* 180 */
        .long SYMBOL_NAME(sys_pwrite)
        .long SYMBOL_NAME(sys_chown16)
        .long SYMBOL_NAME(sys_getcwd)
        .long SYMBOL_NAME(sys_capget)
        .long SYMBOL_NAME(sys_capset)           /* 185 */
        .long SYMBOL_NAME(sys_sigaltstack)
        .long SYMBOL_NAME(sys_sendfile)
        .long SYMBOL_NAME(sys_ni_syscall)               /* streams1 */
        .long SYMBOL_NAME(sys_ni_syscall)               /* streams2 */
        .long SYMBOL_NAME(sys_vfork)            /* 190 */
        .long SYMBOL_NAME(sys_getrlimit)
        .long SYMBOL_NAME(sys_mmap2)
        .long SYMBOL_NAME(sys_truncate64)
        .long SYMBOL_NAME(sys_ftruncate64)
        .long SYMBOL_NAME(sys_stat64)           /* 195 */
        .long SYMBOL_NAME(sys_lstat64)
        .long SYMBOL_NAME(sys_fstat64)
        .long SYMBOL_NAME(sys_lchown)
        .long SYMBOL_NAME(sys_getuid)
        .long SYMBOL_NAME(sys_getgid)           /* 200 */
        .long SYMBOL_NAME(sys_geteuid)
        .long SYMBOL_NAME(sys_getegid)
        .long SYMBOL_NAME(sys_setreuid)
        .long SYMBOL_NAME(sys_setregid)
        .long SYMBOL_NAME(sys_getgroups)        /* 205 */
        .long SYMBOL_NAME(sys_setgroups)
        .long SYMBOL_NAME(sys_fchown)
        .long SYMBOL_NAME(sys_setresuid)
        .long SYMBOL_NAME(sys_getresuid)
        .long SYMBOL_NAME(sys_setresgid)        /* 210 */
        .long SYMBOL_NAME(sys_getresgid)
        .long SYMBOL_NAME(sys_chown)
        .long SYMBOL_NAME(sys_setuid)
        .long SYMBOL_NAME(sys_setgid)
        .long SYMBOL_NAME(sys_setfsuid)         /* 215 */
        .long SYMBOL_NAME(sys_setfsgid)
        .long SYMBOL_NAME(sys_pivot_root)
        .long SYMBOL_NAME(sys_mincore)
        .long SYMBOL_NAME(sys_madvise)
        .long SYMBOL_NAME(sys_getdents64)       /* 220 */
        .long SYMBOL_NAME(sys_fcntl64)
        .long SYMBOL_NAME(sys_ni_syscall)       /* reserved for TUX */
        .long SYMBOL_NAME(sys_ni_syscall)       /* Reserved for Security */
        .long SYMBOL_NAME(sys_gettid)
        .long SYMBOL_NAME(sys_readahead)        /* 225 */
	  </screen>
	 </para>

	</simplesect>

	<simplesect id="irq_event">
	 <title>IRQ 이벤트</title>

	 <para>
	  IRQ 가 발생하면 현재 실행중인 프로세스는 IRQ 핸들러를 실행하기 위해 인터럽트 된다.
	 </para>

	 <para>
	  IRQ 가 처리된 후, 제어 경로는 마치 아무 일도 없던 것처럼 인터럽트가 걸린 지점으로 되돌아 간다.
	 </para>

	 <para>
	  <screen>
              Running Task 
             |-----------|          (3)
NORMAL       |   |       | [break execution] IRQ Handler
EXECUTION (1)|   |       |     ------------->|---------| 
             |  \|/      |     |             |  does   |         
 IRQ (2)---->| ..        |----->             |  some   |      
             |   |       |&lt;-----             |  work   |       
BACK TO      |   |       |     |             |  ..(4). |
NORMAL    (6)|  \|/      |     &lt;-------------|_________|
EXECUTION    |___________|  [return to code]
                                    (5)
               USER MODE                     KERNEL MODE

         User->Kernel Mode Transition caused by IRQ event
	  </screen>
	 </para>

	 <para>
	  아래의 순서가 매겨진 단계는 위의 그림에서 나타나는 이벤트의 번호에 해당한다:
	 </para>

	 <para>
	  <orderedlist>
	   
	   <listitem>
	    <para> 프로세스가 실행되고 있음 </para>
	   </listitem>

	   <listitem>
	    <para> 실행 도중 IRQ 발생 </para>
	   </listitem>

	   <listitem>
	    <para> 프로세스는 인터럽트 되고 <emphasis>인터럽트 핸들러</emphasis> 호출 </para>
	   </listitem>

	   <listitem>
	    <para> <emphasis>인터럽트 핸들러</emphasis> 코드 수행 </para>
	   </listitem>

	   <listitem>
	    <para> 제어 경로는 (아무 일도 없던 것처럼) 사용자 모드의 프로세스로 돌아감 </para>
	   </listitem>

	   <listitem>
	    <para> 프로세스가 다시 실행 </para>
	   </listitem>

	  </orderedlist>
	 </para>

	 <para>
	  특히 관심있게 지켜보아야 할 것은 Timer IRQ 이다. Timer IRQ 는 타이머가 설정된 매 시간 간격 (ms 단위) 마다 발생하며 다음과 같은 것들을 관리한다:
	 </para>

	 <para>
	  <orderedlist>

	   <listitem>
	    <para> 알람 </para>
	   </listitem>

	   <listitem>
	    <para> (프로세스 스케줄링 이나 통계 등에서 사용되는) 시스템 카운터와 태스크 카운터 </para>
	   </listitem>

	   <listitem>
	    <para> 타임 슬라이스 <footnote> <para> 역자주: 프로세스가 한번에 연속적으로 수행될 수 있는 시간 간격을 말한다. </para> </footnote> 에 기반을 둔 멀티 태스킹 </para>
	   </listitem>

	  </orderedlist>
	 </para>

	</simplesect>

   </sect2>


   <sect2 id="multitasking">
    <title>멀티태스킹</title>

	<simplesect id="multitasking_mechanism">
	 <title>메카니즘</title>

	 <para>
	  최근의 운영체제에서의 핵심은 <emphasis>태스크 (task, 작업)</emphasis> <footnote> <para> 역자주: 번역과정에서 태스크 와 '프로세스' 가 같은 의미로 사용된 경우가 많다. </para> </footnote> 이다. 태스크는 메모리 상에서 동작하는 응용 프로그램으로서 다른 태스크 들과 모든 자원들을 (CPU 와 메모리 등을 포함) 공유한다.
	 </para>

	 <para>
	  이러한 <emphasis>자원의 공유</emphasis> 는 <emphasis>멀티태스킹 메카니즘</emphasis> 에 의해서 관리된다. 멀티태스킹 메카니즘은 <emphasis>타임 슬라이스</emphasis> 로 정해진 시간이 흐르면 현재의 태스크를 다른 태스크로 교체한다. 사용자는  모든 자원을 점유하고 있는 것 같은 <emphasis>환상</emphasis> 에 빠지게 된다. 또한 한명의 사용자가 시스템을 사용하고 있는 시나리오를 생각해 보면, 마찬가지로 사용자는 많은 태스크들이 동시에 수행되고 있는 것과 같은 <emphasis>환상</emphasis> 에 빠지게 된다.
	 </para>

	 <para>
	  이러한 멀티태스킹을 구현하기 위해서 태스크는 다음과 같은 <emphasis>상태</emphasis> 변수를 가진다:
	 </para>

	 <para>
	  <orderedlist>

	   <listitem>
	    <para> READY, 실행할 준비가 되어 있다. </para>
	   </listitem>

	   <listitem>
	    <para> BLOCKED, 자원을 기다리고 있다. </para>
	   </listitem>

	  </orderedlist>
	 </para>

	 <para>
	  태스크의 상태는 그에 관련된 리스트 (READY 리스트와 BLOCKED 리스트) 내에 태스크가 속해 있는지에 의해서 관리된다.
	 </para>

	</simplesect>

	<simplesect id="multitasking_task_switching">
	 <title>태스크 교환 (task switching)</title>

	 <para>
	  (실행중인) 한 태스크에서 다른 태스크로 이동하는 것을 <emphasis>태스크 교환</emphasis> (<emphasis>context switching (문맥 교환)</emphasis> 이라고 부르기도 한다) 이라고 한다. 많은 컴퓨터는 이러한 작업을 자동으로 수행해 주는 하드웨어적인 명령을 가지고 있다. 태스크 교환은 다음과 같은 경우에 일어난다:
	 </para>

	 <para>
	  <orderedlist>

	   <listitem>
	    <para> 타임 슬라이스 종료 후: <emphasis>실행할 준비가 된</emphasis> 태스크를 스케줄링하여 실행한다. </para>
	   </listitem>

	   <listitem>
	    <para> 태스크가 어떤 장치를 기다리는 경우: 새로운 태스크를 스케줄링하여 교환한다. <footnote> <para> 태스크가 다른 연산을 수행하지 않고 장치를 기다리는 경우 (Busy Form Waiting) 을 방지하기 위해 다른 태스크를 스케줄링한다. </para> </footnote> </para>
	   </listitem>

	  </orderedlist>
	 </para>

	 <para>
	  태스크 교환은 <emphasis>Schedule</emphasis> 개체에 의해서 관리된다. 
	 </para>

	 <para>
	  <screen>
Timer    |           |
 IRQ     |           |                            Schedule
  |      |           |                     ________________________
  |----->|   Task 1  |&lt;------------------>|(1)Chooses a Ready Task |
  |      |           |                    |(2)Task Switching       |
  |      |___________|                    |________________________|   
  |      |           |                               /|\
  |      |           |                                | 
  |      |           |                                |
  |      |           |                                |
  |      |           |                                |      
  |----->|   Task 2  |&lt;-------------------------------|
  |      |           |                                |
  |      |___________|                                |
  .      .     .     .                                .
  .      .     .     .                                .
  .      .     .     .                                .
  |      |           |                                |
  |      |           |                                |
  ------>|   Task N  |&lt;--------------------------------
         |           |
         |___________| 
    
            Task Switching based on TimeSlice
	  </screen>
	 </para>

	 <para>
	  리눅스에서 타임 슬라이스는 일반적으로 10 ms 이다.
	 </para>

	 <para>
	  <screen>
 |           |            
 |           | Resource    _____________________________
 |   Task 1  |----------->|(1) Enqueue Resource request |
 |           |  Access    |(2)  Mark Task as blocked    |
 |           |            |(3)  Choose a Ready Task     |
 |___________|            |(4)    Task Switching        |
                          |_____________________________|
                                       |
                                       |
 |           |                         |
 |           |                         |
 |   Task 2  |&lt;-------------------------
 |           |  
 |           |
 |___________|
 
     Task Switching based on Waiting for a Resource
	  </screen>
	 </para>

	</simplesect>

   </sect2>

   <sect2 id="microkernel_vs_monolithicos">
    <title>마이크로커널 OS 와 모놀리틱 OS</title>

	<simplesect id="micro_vs_monolithic_overview">
	 <title>개요</title>

	 <para>
	  지금까지 우리가 살펴본 것은 모놀리틱 OS 라 불리는 것들 이었지만, <emphasis>마이크로커널</emphasis> 
	  이라고 하는 방식의 운영체제도 있다.
	 </para>

	 <para>
	  마이크로커널 방식의 운영체제는 사용자 모드의 프로세스는 물론 플로피 디스크 관련 태스크, 
	  하드 디스크 관련 태스크, 네트워크 관련 작업 등 커널이 처리하는 작업 들도 태스크 방식으로 처리한다. 
	  이러한 방식의 운영체제로는 <application>Amoeba</application>, 
	  <application>Mach</application> 등이 있다.
	 </para>
	
	</simplesect>

	<simplesect id="micro_vs_monolithic_pros_cons">
	 <title>마이크로 커널 방식의 장단점</title>

	 <para>
	  장점:
	 </para>

	 <para>
	  <itemizedlist>

	   <listitem>
	    <para> 각각의 태스크는 한가지 종류의 연산 만을 수행하므로 운영체제의 입장에서는 이를 관리하기가
		 쉬워진다. 그래서 만약 네트워크 부분을 수정하고 싶다면 이를 담당하는 태스크 만을 수정하면 될 것이다.
		 (다른 부분의 구조적인 변화가 없는 이상적인 경우라면) </para>
	   </listitem>

	  </itemizedlist>
	 </para>

	 <para>
	  단점:
	 </para>

	 <para>
	  <itemizedlist>

	   <listitem>
	    <para> 특정한 태스크로 진입하는 시간과 그 태스크에서 복귀하는 시간만큼의 2 배의 
		<emphasis>태스크 전환</emphasis> 시간이 추가되어야 하므로 
		모놀리틱 방식의 운영체제보다 성능을 떨어지게 된다. </para>
	   </listitem>

	  </itemizedlist>
	 </para>

	 <para>
	  개인적인 의견으로는 마이크로커널 방식은 교육용 예제로는 적절하지만 (Minix 처럼) 실제로 적용될 수 있을 만큼
	  <emphasis>최적의</emphasis> 운영체제는 아니라고 생각한다. 리눅스에서는 마이크로커널의 구조를 약간 
	  구현하여 <emphasis>커널 스레드</emphasis> 라고 불리는 몇가지 태스크를 사용한다. (하드 디스크와 같은
	  저장 장치에서 (스왑아웃된) 페이지를 다시 받아오는 kswapd 와 같은 태스크가 있다.)
	  이 경우 스와핑은 매우 느린 작업이기 때문에 성능면에서 문제될 것은 없다.
	 </para>

	</simplesect>

   </sect2>

   <sect2 id="networking">
    <title>네트워킹</title>

    <simplesect id="iso_osi_levels">
	 <title>ISO 의 OSI 계층</title>

	  <para>
	   ISO-OSI 표준은 다음과 같은 계층들로 이루어진 네트워크 구조를 설명하고 있다:
	  </para>

	  <para>
	   <orderedlist>
	    
		<listitem>
		 <para> 물리적인 계층 (예: PPP, Ethernet) </para>
		</listitem>
		
		<listitem>
		 <para> 데이타-링크 계층 (예: PPP, Ethernet) </para>
		</listitem>
	
		<listitem>
		 <para> 네트워크 계층 (예: IP, X.25) </para>
		</listitem>
	
		<listitem>
		 <para> 전송 계층 (예: TCP, UDP) </para>
		</listitem>

		<listitem>
		 <para> 세션 계층 (예: SSL) </para>
		</listitem>

		<listitem>
		 <para> 표현 계층 (예: FTP 의 binary-ascii 코딩) </para>
		</listitem>

		<listitem>
		 응용 계층 (예: 네스케이프와 같은 응용 프로그램)
		</listitem>

	   </orderedlist>
	  </para>

	  <para>
	   위의 리스트 중에서 처음 2 개의 계층은 주로 하드웨어로 구현된다. 나머지 계층들은 소프트웨어에 속한다. (혹은 펌웨어나 라우터)
	  </para>

	  <para>
	   운영체제는 많은 프로토콜을 사용하고 있으며 그 중의 하나로는 TCP/IP 를 들 수 있다. (이것은 3-4 계층의 가장 주된 프로토콜이다.)
	  </para>

	</simplesect>

	<simplesect id="what_does_the_kernel">
	 <title>커널은 무슨 일을 하는가?</title>

	 <para>
	  ISO-OSI 계층의 처음 2 가지에 대해서는 커널은 (주소를 제외하고) 아무 것도 알지 못한다.
	 </para>

	 <para>
	  RX (수신) 시에 커널이 하는 일은:
	 </para>

	 <para>
	  <orderedlist>

	   <listitem>
	    <para> (이더넷 카드나 모뎀과 같은) 로우 레벨 장치와의 연결(handshake)을 관리하고 그 장치들로부터 <emphasis>프레임</emphasis> <footnote><para> 역자주: 실제로 네트워크 상으로 전해지는 비트열의 정보 </para></footnote> 을 받아온다. </para>
	   </listitem>

	   <listitem>
	    <para> 그 (이더넷이나 PPP 같은) <emphasis>프레임</emphasis> 으로부터 TCP/IP <emphasis>패킷</emphasis> 을 구성한다. </para>
	   </listitem>
	   
	   <listitem>
	    <para> <emphasis>패킷</emphasis> 을 <emphasis>소켓</emphasis> 으로 변환하여 (포트 번호에 맞는) 적절한 응용프로그램에게 넘겨주거나 </para>
	   </listitem>

	   <listitem>
	    <para> <emphasis>패킷</emphasis> 을 적절한 큐에 보내준다. </para>
	   </listitem> 

	  </orderedlist>
	 </para>

	 <para>
	  <screen>
frames         packets              sockets
NIC ---------> Kernel ----------> Application
                  |    packets
                  --------------> Forward
                        - RX - 
	  </screen>
	 </para>

	 <para>
	  TX (송신) 시에 커널이 하는 일은:
	 </para>

     <para>
	  <orderedlist>
	   
	   <listitem>
	    <para> <emphasis>소켓</emphasis> 을 <emphasis>패킷</emphasis> 으로 변환하거나 </para>
	   </listitem>

	   <listitem>
	    <para> 큐에 들어있는 정보를 가져와서 <emphasis>패킷</emphasis> 을 구성한다. </para>
	   </listitem>

	   <listitem>
	    <para> <emphasis>패킷</emphasis> 을 (이더넷이나 PPP 에서 사용되는) <emphasis>프레임</emphasis> 으로 분리한다. </para>
	   </listitem>

	   <listitem>
	    <para> 하드웨어 장치를 사용해서 <emphasis>프레임</emphasis> 을 전송한다. </para>
	   </listitem>

	  </orderedlist>
	 </para>

	 <para>
	  <screen>
sockets       packets                     frames
Application ---------> Kernel ----------> NIC
              packets     /|\    
Forward  -------------------
                        - TX -  
	  </screen>
	 </para>

	</simplesect>

   </sect2>

   <sect2 id="virtual_memory">
    <title>가상 메모리</title>

	<simplesect id="segmentation">
	 <title>세그먼테이션 (Segmentation)</title>

	 <para>
	  세그먼테이션은 메모리 할당 문제를 해결하는 첫번째 방법이다: 이를 이용하면 소스코드가 메모리 상의 어느 부분에 놓이게 될지를 신경쓰지 않고도 프로그램을 컴파일할 수 있다. 실제로 이러한 기능은 응용 프로그램 개발자에게 OS 와 하드웨어에 독립적인 프로그램을 개발할 수 있도록 해준다. 
	 </para>

	 <para>
	  <screen>
            |       Stack        |
            |          |         |
            |         \|/        |
            |        Free        | 
            |         /|\        |     Segment &lt;---> Process    
            |          |         |
            |        Heap        |
            | Data uninitialized |
            |  Data initialized  |
            |       Code         |
            |____________________|  
 
                   Segment
	  </screen>
	 </para>

	 <para>
	  세그먼트는 프로그램의 논리적인 개체, 혹은 메모리 상의 프로그램에 대한 이미지라고 말 할 수 있다. 
	 </para>

	 <para>
	  프로그래밍시에 우리는 데이타가 메모리 상의 어느 부분에 놓이게 될 지 전혀 고려하지 않는다. 오직 세그먼트 (프로그램) 내에서의 위치(offset)에 대해서만 생각할 뿐이다.
	 </para>

	 <para>
	  보통 각각의 프로세스에 대해서 하나의 세그먼트를 할당하거나 혹은 그 반대이지만 리눅스에서는 그렇게 하지 않는다.
	  리눅스에서는 커널과 다른 모든 프로세스들에 대해 오직 4 개의 세그먼트 만을 사용한다. 
	 </para>

	</simplesect>

	<simplesect id="problems_of_segmentation">
	 <title>세그먼테이션의 문제점</title>

	 <para>
	  <screen>
                                ____________________
                          ----->|                    |----->
                          | IN  |     Segment A      | OUT
 ____________________     |     |____________________|   
|                    |____|     |                    |   
|     Segment B      |          |     Segment B      |
|                    |____      |                    |   
|____________________|    |     |____________________|   
                          |     |     Segment C      |   
                          |     |____________________|
                          ----->|     Segment D      |-----> 
                            IN  |____________________| OUT 
 
                     Segmentation problem
	  </screen>
	 </para>

	 <para>
	  위의 그림에서 A 와 D 라는 프로세스(세그먼트)를 없애고 새로 B 라는 프로세스를 집어넣는 경우를 생각해보자. 	이 경우 B 가 들어갈 만한 충분한 메모리 공간이 있지만 프로세스를 2 개로 나눌 수 없으므로 결국 메모리로 <emphasis>로드하지 못한다</emphasis>. (메모리 부족)
	 </para>

	 <para>
	  이러한 문제가 발생하는 이유는 세그먼트는 (논리적인 공간이기 때문에) 연속된 공간이고 나누어 질 수 없기 때문이다.  
	 </para>

	</simplesect>

	<simplesect id="pagination">
	 <title>페이지네이션 (Pagination)</title>

	 <para>
	  <screen>
             ____________________
            |     Page 1         |
            |____________________|
            |     Page 2         |
            |____________________| 
            |      ..            |     Segment &lt;---> Process    
            |____________________|
            |     Page n         |
            |____________________|
            |                    |
            |____________________|
            |                    |
            |____________________|  
 
                   Segment
	  </screen>
	 </para>

	 <para>
	  페이지네이션 ('페이징' 이라고 하기도 한다) 이란 메모리를 고정된 길이의 n 개의 조각으로 나누는 것이다.
	 </para>

	 <para>
	  프로세스는 하나 이상의 페이지로 로드될 수 있다. 메모리가 해제되면 모든 페이지가 해제된다. (앞의 <xref linkend="problems_of_segmentation">&nbsp;</xref> 참고)
	 </para>

	 <para>
	  페이지네이션은 <emphasis>스와핑 (swapping)</emphasis> 이라고 하는 또하나의 중요한 목적을 위해서도 사용된다. 만약 페이지가 실제 메모리 상에 존재하지 않는다면 <emphasis>예외 (Exception)</emphasis> 를 발생시키고, 이는 커널이 메모리 상에 새로운 페이지를 찾게 한다. 이 메카니즘은 운영체제에서 실제 메모리가 허용하는 것보다 더 많은 응용 프로그램을 메모리 상에 로드할 수 있게 해준다.
	 </para>

	</simplesect>

	<simplesect id="pagination_problem">
	 <title>페이지네이션의 문제점</title>

     <para>
	  <screen>
             ____________________
   Page   X |     Process Y      |
            |____________________|
            |                    |
            |       WASTE        |
            |       SPACE        |
            |____________________|  
   
              Pagination Problem
	  </screen>
	 </para>
	 <para>
	  위의 그림을 보면 페이지네이션 정책의 문제점을 알 수 있을 것이다: 프로세스 Y 가 페이지 X 에 로드되는 경우, 하나의 페이지에 해당하는 전체 영역이 할당되므로 뒤쪽의 나머지 영역은 낭비된다.
	 </para>

	</simplesect>

	<simplesect id="segmentation_and_pagination">
	 <title>세그먼테이션과 페이지네이션</title>

	 <para>
	  세그먼테이션과 페이지네이션의 문제점을 해결하기 위해서 2 가지 정책을 혼합해서 사용한다.
	 </para>

	 <para>
	  <screen>
                                  |      ..            |
                                  |____________________|
                            ----->|      Page 1        |
                            |     |____________________|
                            |     |      ..            |
 ____________________       |     |____________________|
|                    |      |---->|      Page 2        |
|      Segment X     |  ----|     |____________________|
|                    |      |     |       ..           |
|____________________|      |     |____________________|
                            |     |       ..           |
                            |     |____________________|
                            |---->|      Page 3        |
                                  |____________________|
                                  |       ..           |
	  </screen>
	 </para>

	 <para>
	  (세그먼트 X 로 구분되는) 프로세스 X 는 3 부분으로 나누어지고 각각은 페이지로 로드된다.
	 </para>

	 <para>
	  이로 인해 다음과 같은 문제점이 해결된다:
	 </para>

	 <para>
	  <orderedlist>

	   <listitem>
	    <para> 세그먼테이션의 문제점: 메모리를 페이지 단위로 할당하고 해제하기 때문에 메모리 관리를 최적화된 방법으로 할 수 있다. </para>
	   </listitem>

	   <listitem>
	    <para> 페이지네이션의 문제점: 오직 마지막 페이지 만이 낭비된다. 하지만 매우 작은 크기의 페이지를 사용하도록 결정할 수 있고 - 예를 들어 4096 바이트를 한 페이지로 설정한다면 최대 4096 * 태스크의 수 만큼의 바이트가 낭비될 것이다 - 페이지를 계층적으로 (2-3 단계로) 관리할 수 있다. </para>
	   </listitem>

	  </orderedlist>
	 </para>

	 <para>
	  <screen>
                          |         |           |         |
                          |         |   Offset2 |  Value  |
                          |         |        /|\|         |
                  Offset1 |         |-----    | |         |
                      /|\ |         |    |    | |         |
                       |  |         |    |   \|/|         | 
                       |  |         |    ------>|         |
                      \|/ |         |           |         |
 Base Paging Address ---->|         |           |         |
                          | ....... |           | ....... |
                          |         |           |         |    
 
                     Hierarchical Paging
	  </screen>
	 </para>

	</simplesect>

   </sect2>

 </sect1>



 <sect1 id="linux_startup">
  <title>리눅스의 시작</title>

  <para>
   여기서는 <function>startup_32:</function> 라고 하는 레이블에서 시작되는 C 코드부터 살펴본다.
  </para>

  <para>
   <screen>
|startup_32:
   |start_kernel
      |lock_kernel
      |trap_init
      |init_IRQ
      |sched_init
      |softirq_init
      |time_init
      |console_init 
      |#ifdef CONFIG_MODULES 
         |init_modules 
      |#endif 
      |kmem_cache_init 
      |sti 
      |calibrate_delay 
      |mem_init
      |kmem_cache_sizes_init
      |pgtable_cache_init
      |fork_init
      |proc_caches_init 
      |vfs_caches_init
      |buffer_init
      |page_cache_init
      |signals_init 
      |#ifdef CONFIG_PROC_FS 
        |proc_root_init 
      |#endif 
      |#if defined(CONFIG_SYSVIPC) 
         |ipc_init
      |#endif 
      |check_bugs      
      |smp_init
      |rest_init
         |kernel_thread
         |unlock_kernel
         |cpu_idle

   </screen>
  </para>

  <para>
   <itemizedlist>

    <listitem>
	 <para> startup_32 [arch/i386/kernel/head.S] </para>
    </listitem>

    <listitem>
	 <para> start_kernel [init/main.c] </para>
    </listitem>

    <listitem>
	 <para> lock_kernel [include/asm/smplock.h] </para>
    </listitem>

    <listitem>
	 <para> trap_init [arch/i386/kernel/traps.c] </para>
    </listitem>

    <listitem>
	 <para> init_IRQ [arch/i386/kernel/i8259.c] </para>
    </listitem>

    <listitem>
	 <para> sched_init [kernel/sched.c] </para>
    </listitem>

    <listitem>
	 <para> softirq_init [kernel/softirq.c] </para>
    </listitem>

    <listitem>
	 <para> time_init [arch/i386/kernel/time.c] </para>
    </listitem>

    <listitem>
	 <para> console_init [drivers/char/tty_io.c] </para>
    </listitem>

    <listitem>
	 <para> init_modules [kernel/module.c] </para>
    </listitem>

    <listitem>
	 <para> kmem_cache_init [mm/slab.c] </para>
    </listitem>

    <listitem>
	 <para> sti [include/asm/system.h] </para>
    </listitem>

    <listitem>
	 <para> calibrate_delay [init/main.c] </para>
    </listitem>

    <listitem>
	 <para> mem_init [arch/i386/mm/init.c] </para>
    </listitem>

    <listitem>
	 <para> kmem_cache_sizes_init [mm/slab.c] </para>
    </listitem>

    <listitem>
	 <para> pgtable_cache_init [arch/i386/mm/init.c] </para>
    </listitem>

    <listitem>
	 <para> fork_init [kernel/fork.c] </para>
    </listitem>

    <listitem>
	 <para> proc_caches_init </para>
    </listitem>

    <listitem>
	 <para> vfs_caches_init [fs/dcache.c] </para>
    </listitem>

    <listitem>
	 <para> buffer_init [fs/buffer.c] </para>
    </listitem>

    <listitem>
	 <para> page_cache_init [mm/filemap.c] </para>
    </listitem>

    <listitem>
	 <para> signals_init [kernel/signal.c] </para>
    </listitem>

    <listitem>
	 <para> proc_root_init [fs/proc/root.c] </para>
    </listitem>

    <listitem>
	 <para> ipc_init [ipc/util.c] </para>
    </listitem>

    <listitem>
	 <para> check_bugs [include/asm/bugs.h] </para>
    </listitem>

    <listitem>
	 <para> smp_init [init/main.c] </para>
    </listitem>

    <listitem>
	 <para> rest_init </para>
    </listitem>

    <listitem>
	 <para> kernel_thread [arch/i386/kernel/process.c] </para>
    </listitem>

    <listitem>
	 <para> unlock_kernel [include/asm/smplock.h] </para>
    </listitem>

    <listitem>
	 <para> cpu_idle [arch/i386/kernel/process.c] </para>
    </listitem>

   </itemizedlist>
  </para>

  <para>
   마지막 부분의 <function>rest_init</function> 라는 함수는 다음과 같은 일을 한다:
  </para>

  <para>
   <orderedlist>

    <listitem>
	 <para> <function>init</function> 라는 커널 쓰레드를 시작시킨다. </para>
	</listitem>

	<listitem>
	 <para> <function>kernel_unlock</function> 을 호출한다. </para>
	</listitem>

	<listitem>
	 <para> 실행할 프로세스가 아무 것도 없는 경우에 실행될 부분인 <function>cpu_idle</function> 루틴을 실행한다. </para>
	</listitem>

   </orderedlist>
  </para>

  <para>
   사실 <function>start_kernel</function> 함수는 절대 끝나지 않는다. 이 함수는 끝없이 <function>cpu_idle</function> 루틴을 실행시킨다. 
  </para>

  <para>
   다음은 첫번째 커널 쓰레드인 <function>init</function> 함수에 대한 설명이다:
  </para>

  <para>
   <screen>
|init
   |lock_kernel
   |do_basic_setup
      |mtrr_init
      |sysctl_init
      |pci_init
      |sock_init
      |start_context_thread
      |do_init_calls
         |(*call())-> kswapd_init
   |prepare_namespace
   |free_initmem
   |unlock_kernel
   |execve
   </screen>
  </para>

 </sect1>

 

 <sect1 id="linux_peculiarities">
   <title>리눅스의 특징</title>

   <sect2 id="linux_peculiarities_overview">
    <title>개요</title>

    <para>
	 리눅스는 다른 운영체제들과 구분되는 다음과 같은 특징들을 가지고 있다: 
	</para>

	<para>
	 <orderedlist>

	  <listitem>
	   <para> 페이지네이션만을 사용 </para>
	  </listitem>

	  <listitem>
	   <para> 소프트웨어 인터럽트 (softirq) </para>
	  </listitem>

	  <listitem>
	   <para> 커널 쓰레드 </para>
	  </listitem>

	  <listitem>
	   <para> 커널 모듈 </para>
	  </listitem>

	  <listitem>
	   <para> <emphasis>proc</emphasis> 디렉토리 </para>
	  </listitem>

	 </orderedlist>
	</para>

	<simplesect id="flexibility_elements">
	 <title>유연성을 위한 요소들</title>

	 <para>
	  위에서 열거한 4 번째와 5 번째 특징은 치명적인 커널 버그나 특정한 문제가 발생하였을 때 시스템을 리부팅하지 않고 (시스템 관리자가) 사용자 모드에서 문제를 해결하거나 시스템을 관리할 수 있는 막대한 유연성을 제공한다. 예를 들어 대형 서버에서 특정 부분을 수정할 필요가 있을 때 리부팅을 하지 않고 해결하기 위해서 커널 모듈을 이용할 수 있다.
	 </para>

	</simplesect>

   </sect2>

   <sect2 id="pagination_only">
    <title>페이지네이션만을 사용</title>

	<para>
	 리눅스에서는 각각의 태스크를 구분하기 위해 세그먼테이션을 사용하지 않고 페이지네이션을 사용한다. (모든 태스크는 오직 2 개의 세그먼트 (코드 세그먼트와 데이타 (스택) 세그먼트) 를 사용할 뿐이다.)
	</para>

	<para>
	 각각의 태스크는 자신만의 페이지 테이블을 사용하므로 서로 다른 태스크 간의 페이지 관련 오류는 절대 일어나지 않는다고 말할 수 있다. 어떤 경우에 있어서는 여러 태스크들이 하나의 페이지 테이블을 참조하기도 하는데, 공유 라이브러리와 같은 경우 (메모리를 절약하기 위해) 코드 부분을 공유하고 데이타는 각 태스크의 스택에 저장하는 방식을 사용한다. 
	</para>

	<simplesect id="linux_segments">
	 <title>리눅스 세그먼트</title>

	 <para>
	  리눅스 커널에서는 다음과 같은 오직 4 개의 세그먼트 만이 존재한다:
	 </para>

     <para>
	  <orderedlist>

	   <listitem>
	    <para> 커널 코드 세그먼트 [0x10] </para>
	   </listitem>
	  
	   <listitem>
	    <para> 커널 데이타/스택 세그먼트 [0x18] </para>
	   </listitem>
	  
	   <listitem>
	    <para> 사용자 코드 세그먼트 [0x23] </para>
	   </listitem>
	  
	   <listitem>
	    <para> 사용자 데이타/스택 세그먼트 [0x2b] </para>
	   </listitem>
	  
	  </orderedlist>
	 </para>

	 <para>
	  <command>목적 [세그먼트]</command> 의 형태로 표현하였다.
	 </para>

	 <para>
	  인텔 CPU 의 경우, 다음과 같은 세그먼트 레지스터들이 사용된다:
	 </para>

	 <para>
	  <itemizedlist>

	   <listitem>
	    <para> CS - 코드 세그먼트 </para>
	   </listitem>

	   <listitem>
	    <para> DS - 데이타 세그먼트 </para>
	   </listitem>

	   <listitem>
	    <para> SS - 스택 세그먼트 </para>
	   </listitem>

	   <listitem>
	    <para> ES - 예비 세그먼트 (서로 다른 세그먼트에 속한 메모리 영역을 복사하는 일 등을 수행할 때 필요하다) </para>
	   </listitem>

	  </itemizedlist>
	 </para>

	 <para>
	  그래서 모든 태스크는 코드 세그먼트로 0x23 을, 데이타/스택 세그먼트로 0x2b 를 사용한다.
	 </para>

	</simplesect>

	<simplesect id="linux_pagination">
	 <title>리눅스 페이지네이션</title>

	 <para>
	  리눅스에서는 하드웨어의 구조에 따라 3 단계의 페이지가 사용된다. 인텔 CPU 에서는 단지 2 단계의 페이지 만이 지원된다. 리눅스는 또한 <emphasis>Copy on Write</emphasis> 메카니즘을 지원한다. (더 자세한 정보는 <xref linkend="copy_on_write">&nbsp;</xref> 을 참고)
	 </para>

	</simplesect>

	<simplesect id="why_dont_intertasks_address_conflicts_exist">
	 <title>왜 태스크들 간에 주소 충돌이 일어나지 않는가?</title>

	 <para>
	  대답은 매우 간단하다: 태스크들 간에 주소 충돌이 일어나지 않는 이유는 그것이 불가능하기 때문이다. 선형 주소를 물리 주소로 매핑하는 것은 <emphasis>페이지네이션</emphasis> 에 의해서 행해지므로 물리적인 페이지를 단일한 (univocal) 방법으로 할당할 필요가 없다.
	 </para>

	</simplesect>

	<simplesect id="do_we_need_to_defragment_memory">
	 <title>메모리를 페이지 단위로 분리해야 하는가?</title>

	 <para>
	  그럴 필요가 없다. 페이지의 할당은 동적으로 이루어진다. 페이지는 오직 태스크가 페이지를 요청한 경우에만 필요하다. 그래서 크기 별로 정리된 이용 가능한 메모리 (페이지) 영역에서 원하는 크기에 해당하는 페이지를 선택한다. 페이지가 해제되면 단지 이용 가능한 페이지 리스트에 추가하면 된다.
	 </para>

	</simplesect>

	<simplesect id="what_about_kernel_pages">
	 <title>커널 페이지에 대하여</title>

	 <para>
	  커널 페이지에는 문제점이 하나 있다: 커널 페이지는 동적으로 할당되지만 할당되는 영역이 연속된 영역이라는 것을 보장할 수는 없다는 것이다. 왜냐하면 선형 커널 공간은 물리적인 커널 공간과 동일하기 때문이다..??
	 </para>

	 <para>
	  코드 세그먼트는 문제가 없다. 부트 코드는 부팅 시에 할당되고 (따라서 고정된 크기의 메모리 영역 만을 가진다) 모듈에서는 모듈 코드를 포함하는 메모리 영역 만을 할당한다.
	 </para>

	 <para>
	  실제적인 문제는 스택 세그먼트에서 나타나는데, 각각의 태스크들이 커널 스택 페이지를 사용하기 때문이다. 스택 세그먼트는 반드시 연속적인 영역에 할당되어야 하므로 (스택의 정의에 따라) 태스크의 스택 영역의 최대 크기를 정하게 되었다. 만약 이 크기를 넘어가게 되면 커널 모드 프로세스의 자료 구조를 덮어쓰게 되는 일이 벌어질 것이다.
	 </para>

	 <para>
	  커널의 구조는 이러한 문제점을 피하기 위해 다음과 같은 방식으로 함수를 사용한다:
	 </para>

	 <para>
	  <itemizedlist>
	   
	   <listitem>
	    <para> 재귀적인 함수 호출이 없다. </para>
	   </listitem>

	   <listitem>
	    <para> N 번 이상의 함수간 호출이 없다. </para>
	   </listitem>

	  </itemizedlist>
	 </para>

	 <para>
	  N 을 알고, 모든 커널 함수들의 정적 변수의 평균치를 알면 스택의 크기 제한을 추정할 수 있다.
	 </para>

	 <para>
	  이 문제점을 실제로 확인해 보고 싶다면, 내부적으로 자신을 재귀 호출하는 함수를 가지고 있는 모듈을 생성해 보면 된다. 이 모듈은 몇차례 수행된 후에 페이지 폴트 예외 핸들러에 의해서 중지될 것이다. (보통은 읽기 전용 페이지에 쓰기 접근 등의 이유로)
	 </para>

	</simplesect>

   </sect2>

   <sect2 id="softirq">
    <title>소프트웨어 인터럽트 (SoftIRQ)</title>

	<para>
	 IRQ 가 발생하였을 때, 성능 향상을 위해 태스크 교환은 나중으로 미루어 진다. 몇몇가지 태스크 들은 (IRQ 를 처리한 후에 실행되며, TCP/IP 패킷을 구성하는 것 처럼 CPU 자원을 많이 필요로 하는 작업들) 큐에 넣어지며 스케줄링이 일어날 때 실행된다. (타임 슬라이스가 끝날 때)
	</para>

	<para>
	 최근 버전의 커널 (2.4.x) 에서는 소프트웨어 인터럽트 메카니즘은 <function>kdoftirqd_CPUn</function> 이라는 커널 쓰레드로 구현한다. <function>n</function> 은 커널 쓰레드가 실행되는 CPU 의 번호를 의미한다 (단일 CPU 시스템의 경우 <function>ksoftirqd_CPU0</function> 은 PID 3 번을 사용한다).
	</para>

	<simplesect id="preparing_softirq">
	 <title>소프트웨어 인터럽트의 준비</title>

	</simplesect>

	<simplesect id="enabling_softirq">
	 <title>소프트웨어 인터럽트의 설정</title>

	 <para>
	  <function>cpu_raise_softirq</function> 은 큐에 들어있는 작업들을 관리하는 <function>ksoftirqd_CPU0</function> 커널 쓰레드를 깨우는 루틴이다.
	 </para>

	 <para>
	  <screen>
|cpu_raise_softirq
   |__cpu_raise_softirq
   |wakeup_softirqd
      |wake_up_process
	  </screen>
	 </para>

	 <para>
	  <itemizedlist>
	   
	   <listitem>
	    <para> cpu_raise_softirq [kernel/softirq.c] </para> 
	   </listitem>
	   
	   <listitem>
	    <para> __cpu_raise_softirq [include/linux/interrupt.h] </para>
	   </listitem>
	   
	   <listitem>
	    <para> wakeup_softirq [kernel/softirq.c] </para>
	   </listitem>
	   
	   <listitem>
	    <para> wake_up_process [kernel/sched.c] </para>
	   </listitem>
	   
	  </itemizedlist>
	 </para>

	 <para>
	  <function>__cpu_raise_softirq</function> 루틴은 해당하는 소프트웨어 인터럽트가 발생하였다고 벡터 내의 비트를 설정한다.
	 </para>

	 <para>
	  <function>wakeup_softirq</function> 루틴은 <function>ksoftirqd_CPU0</function> 커널 쓰레드를 깨우기 위해 <function>wakeup_process</function> 을 사용한다.
	 </para>

	</simplesect>

	<simplesect id="executing_softirq">
	 <title>소프트웨어 인터럽트의 실행</title>

	 <para>
	  TODO: 소프트웨어 인터럽트 메카니즘에 포함된 자료 구조를 설명
	 </para>

	 <para>
	  <function>ksoftirqd_CPU0</function> 커널 쓰레드가 깨어나면 큐에 들어있는 작업들을 수행할 것이다.
	 </para>

	 <para>
	  <function>ksoftirqd_CPU0</function> 의 코드는 다음과 같다 (부분 발췌):
	 </para>

	 <para>
	  <programlisting>
for ( ; ; ) {
   if (!softirq_pending(cpu)) 
      schedule();
   __set_current_state(TASK_RUNNING);
   while (softirq_pending(cpu)) { 
      do_softirq(); 
      if (current->need_resched) 
         schedule 
   }
   __set_current_state(TASK_INTERRUPTIBLE)
}
	  </programlisting>
	 </para>

	 <para>
	  <itemizedlist>
	   
	   <listitem>
	    <para> ksoftirqd [kernel/softirq.c] </para>
	   </listitem>

	  </itemizedlist>
	 </para>

	</simplesect>

   </sect2>

   <sect2 id="kernel_threads">
    <title>커널 쓰레드</title>

	<para>
	 리눅스가 모놀리틱 운영체제이지만, 시스템의 관리 (housekeeping) 작업을 위한 몇개의 <emphasis>커널 쓰레드</emphasis> 가 존재한다.
	</para>

	<para>
	 이러한 태스크들은 사용자 영역의 메모리를 사용하지 않고, 커널 영역의 메모리를 공유한다. 또한 다른 커널 영역의 코드와 비슷하게 높은 우선 순위를 가지고 실행된다. (i386 환경에서 RING0)
	</para>

	<para>
	 커널 쓰레드는 <function>kernel_thread [arch/i386/kernel/process]</function> 함수를 통해 생성된다. 이 함수는 어셈블리 부분에서 (<function>fork</function> 와 비슷한 시스템 콜인) <function>clone [arch/i386/kernel/process.c]</function> 시스템 콜을 호출한다:
	</para>

	<para>
	 <programlisting>
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
        long retval, d0;
 
        __asm__ __volatile__(
                "movl %%esp,%%esi\n\t"
                "int $0x80\n\t"         /* Linux/i386 system call */
                "cmpl %%esp,%%esi\n\t"  /* child or parent? */
                "je 1f\n\t"             /* parent - jump */
                /* Load the argument into eax, and push it.  That way, it does
                 * not matter whether the called function is compiled with
                 * -mregparm or not.  */
                "movl %4,%%eax\n\t"
                "pushl %%eax\n\t"               
                "call *%5\n\t"          /* call fn */
                "movl %3,%0\n\t"        /* exit */
                "int $0x80\n"
                "1:\t"
                :"=&amp;a" (retval), "=&amp;S" (d0)
                :"0" (__NR_clone), "i" (__NR_exit),
                 "r" (arg), "r" (fn),
                 "b" (flags | CLONE_VM)
                : "memory");
        return retval;
}
	 </programlisting>
	</para>

	<para>
	 이 함수가 한번 호출되면 매우 느린 자원 (스왑 혹은 USB 이벤트) 을 기다리고 있는 새로운 태스크 (보통 2, 3 처럼 매우 낮은 PID 를 가진다) 를 생성한 것이다. 이렇게 매우 느린 자원을 사용하는 이유는 태스크 교환에 따르는 오버헤드를 줄이기 위해서 이다. 
	</para>

	<para>
	 아래는 (<command>ps x</command> 명령으로 알아본) 일반적인 커널 쓰레드의 리스트이다.
	</para>

	<para>
	 <screen>
PID      COMMAND
 1        init
 2        keventd
 3        kswapd
 4        kreclaimd
 5        bdflush
 6        kupdated
 7        kacpid
67        khubd
	 </screen>
	</para>

	<para>
	 <function>init</function> 커널 쓰레드는 부팅시에 최초로 생성되는 태스크이다. 이 태스크는 (<filename>/etc/inittab</filename> 을 참조하여) 다른 모든 사용자 모드 태스크 - 콘솔 데몬, tty 데몬, 네트워크 데몬 (<filename>rc</filename> 스크립트 참고) 등 - 을 호출한다.
	</para>

	<simplesect id="example_of_kernel_threads">
	 <title>커널 쓰레드 예제: kswapd [mm/vmscan.c]</title>

	 <para>
	  <function>kswapd</function> 는 <function>clone() [arch/i386/kernel/process.c]</function> 에 의해 생성된다.
	 </para>

	 <para>
	  초기화 루틴은 다음과 같다:
	 </para>

	 <para>
	  <screen>
|do_initcalls
   |kswapd_init
      |kernel_thread
         |syscall fork (in assembler)
	  </screen>
	 </para>

	 <para>
	  <itemizedlist>

	   <listitem>
	    <para> do_initcalls [init/main.c] </para>
	   </listitem>

	   <listitem>
	    <para> kswapd_init [mm/vmscan.c] </para>
	   </listitem>

	   <listitem>
	    <para> kernel_thread [arch/i386/kernel/process.c] </para>
	   </listitem>

	  </itemizedlist>
	 </para>

	</simplesect>

   </sect2>

   <sect2 id="kernel_modules">
    <title>커널 모듈</title>

	<simplesect id="kernel_modules_overview">
	 <title>개요</title>

	 <para>
	  리눅스 커널 모듈은 실행 중에 추가할 수 있는 커널 모드에서 실행되는 코드의 일부분 (예를 들면, FS (파일 시스템), 네트워크, 하드웨어 드라이버 등) 을 말한다.
	 </para>

	 <para>
	  리눅스의 핵심 부분 - 프로세스 스케줄링, 인터럽트 관리, 네트워크의 핵심적인 부분 - 들은 모듈화될 수 없다.
	 </para> 

	 <para>
	  <filename>/lib/modules/커널버전/</filename> 디렉토리에 시스템에 설치된 모듈들을 찾아볼 수 있다.
	 </para>

	</simplesect>

	<simplesect id="module_loading_and_unloading">
	 <title>모듈의 로딩과 언로딩</title>

	 <para>
	  모듈을 로드하기 위해서는 다음과 같은 명령어를 사용한다:
	 </para>

	 <para>
	  <command>insmod 모듈이름 parameters</command>
	 </para>

	 <para>
	  <example id="module_loading_ex">
	   <para> insmod ne io=0x300 irq=9 </para>
	  </example>
	 </para>

	 <para>
	  <note>
	   <para>
	    커널 몇가지 파라미터들을 자동으로 찾아주기를 원한다면 (예를 들면 PCI 드라이버를 사용하는 경우나, <filename>/etc/conf.modules</filename> 안에 파라미터를 적어둔 경우) <command>insmod</command> 대신에 <command>modprobe</command> 를 사용할 수 있다. 
	   </para>
	  </note>
	 </para>

	 <para>
	  모듈은 언로딩하기 위해서는 다음과 같은 명령어를 사용한다:
	 </para>

	 <para>
	  <command>rmmod 모듈이름</command>
	 </para>

	</simplesect>

	<simplesect id="module_definition">
	 <title>모듈의 정의</title>

	 <para>
	  모듈은 항상 다음과 같은 내용을 포함한다:
	 </para>

	 <para>
	  <orderedlist>

	   <listitem>
	    <para> <command>insmod</command> (혹은 <command>modprobe</command>) 시에 실행되는 <function>init_module</function> 함수 </para>
	   </listitem>

	   <listitem>
	    <para> <command>rmmod</command> 시에 실행되는 <function>cleanup_module</function> 함수 </para>
	   </listitem>

	  </orderedlist>
	 </para>

	 <para>
	  만약 위의 두 함수가 모듈내에 존재하지 않는다면, 어떤 함수가 이러한 역할 (init, exit) 을 할 것인지를 나타내기 위해 다음과 같은 2 개의 매크로를 추가해야 한다.
	 </para>

	 <para>
	  <orderedlist>
	   
	   <listitem>
	    <para> <function>module_init(함수이름) </function> </para>
	   </listitem>

	   <listitem>
	    <para> <function>module_exit(함수이름) </function> </para>
	   </listitem>

	  </orderedlist>
	 </para>

	 <para>
	  <note>
	   <para>
	    모듈에서는 오직 (<function>EXPORT_SYMBOL</function> 매크로를 이용하여) 외부로 공개된 커널 변수만을 <emphasis>볼 수 있다</emphasis>.
	   </para>
	  </note>
	 </para>

	</simplesect>

	<simplesect id="useful_trick_for_adding_flexibility">
	 <title>커널에 유연성을 추가하는 유용한 팁</title>

	 <para>
	  <programlisting>
// kernel sources side
void (*foo_function_pointer)(void *);
 
if (foo_function_pointer)
  (foo_function_pointer)(parameter);
	  </programlisting>
	 </para>

	 <para>
	  <programlisting>
// module side
extern void (*foo_function_pointer)(void *);

void my_function(void *parameter) {
  //My code
}
 
int init_module() {
  foo_function_pointer = &amp;my_function;
}

int cleanup_module() {
  foo_function_pointer = NULL;
}
	  </programlisting>
	 </para>

	 <para>
	  이 간단한 팁은 오직 모듈이 로드되었을 때만 <function>my_function</function> 루틴을 실행하도록 하기 때문에 커널에 높은 유연성을 주게 된다. 이 함수 내에서는 원하는 어떤 행동이라도 수행할 수 있다: 예를 들어 <function>rshape</function> 모듈은 네트워크로부터 들어오는 입력 트래픽의 BandWidth 를 조절하는 것과 같은 역할을 한다.
	 </para>

	 <para>
	  전체 모듈 메카니즘은 고맙게도 head list (원하는 만큼 리스트를 확장할 수 있게 허용) 와 같은 전역 변수를 모듈에 공개 (export) 하는 것이 가능하다. 전형적인 예로는 파일 시스템, Generic devices (문자, 블럭, 네크워크, 전화 연결) 등이 있다. 그리고 커널이 모듈을 사용할 수 있도록 준비해야 한다: 특정한 경우에 있어서는 가능한 표준에 맞게 다른 구조 (infrastructure) 들을 생성해야 한다. (최근에 생성된 전화연결 장치와 같이)
	 </para>

	</simplesect>

   </sect2>

   <sect2 id="proc directory">
    <title>proc 디렉토리</title>

	<para>
	 <filename>proc</filename> 파일 시스템은 <filename>/proc</filename> 디렉토리에 있으며, 커널과 직접 대화하는 것이 허용된 특수한 디렉토리이다.
	</para>

	<para>
	 리눅스는 커널과의 직접적인 통신을 지원하기 위해 <filename>proc</filename> 디렉토리를 사용한다: 이것은 많은 경우에 유용하다 - 예를 들어 메인 프로세스의 자료 구조를 보고 싶다거나 특정한 하나의 네트워크 인터페이스에만 <emphasis>proxy-arp</emphasis> 기능을 활성화하는 경우, 최대 쓰레드의 갯수를 변경하고 싶은 경우, 혹은 ISA 나 PCI 와 같은 특정 버스의 상태를 디버깅하는 경우에 있어서 어떤 카드가 설치되어 있고, 어떤 I/O address 를 사용하며, 할당된 IRQ 가 어떤 것이 있는지를 알고 싶은 경우 등이 있겠다.
	</para>

	<para>
	 <screen>
|-- bus
|   |-- pci
|   |   |-- 00
|   |   |   |-- 00.0
|   |   |   |-- 01.0
|   |   |   |-- 07.0
|   |   |   |-- 07.1
|   |   |   |-- 07.2
|   |   |   |-- 07.3
|   |   |   |-- 07.4
|   |   |   |-- 07.5
|   |   |   |-- 09.0
|   |   |   |-- 0a.0
|   |   |   `-- 0f.0
|   |   |-- 01
|   |   |   `-- 00.0
|   |   `-- devices
|   `-- usb
|-- cmdline
|-- cpuinfo
|-- devices
|-- dma
|-- dri
|   `-- 0
|       |-- bufs
|       |-- clients
|       |-- mem
|       |-- name
|       |-- queues
|       |-- vm
|       `-- vma
|-- driver
|-- execdomains
|-- filesystems
|-- fs
|-- ide
|   |-- drivers
|   |-- hda -> ide0/hda
|   |-- hdc -> ide1/hdc
|   |-- ide0
|   |   |-- channel
|   |   |-- config
|   |   |-- hda
|   |   |   |-- cache
|   |   |   |-- capacity
|   |   |   |-- driver
|   |   |   |-- geometry
|   |   |   |-- identify
|   |   |   |-- media
|   |   |   |-- model
|   |   |   |-- settings
|   |   |   |-- smart_thresholds
|   |   |   `-- smart_values
|   |   |-- mate
|   |   `-- model
|   |-- ide1
|   |   |-- channel
|   |   |-- config
|   |   |-- hdc
|   |   |   |-- capacity
|   |   |   |-- driver
|   |   |   |-- identify
|   |   |   |-- media
|   |   |   |-- model
|   |   |   `-- settings
|   |   |-- mate
|   |   `-- model
|   `-- via
|-- interrupts
|-- iomem
|-- ioports
|-- irq
|   |-- 0
|   |-- 1
|   |-- 10
|   |-- 11
|   |-- 12
|   |-- 13
|   |-- 14
|   |-- 15
|   |-- 2
|   |-- 3
|   |-- 4
|   |-- 5
|   |-- 6
|   |-- 7
|   |-- 8
|   |-- 9
|   `-- prof_cpu_mask
|-- kcore
|-- kmsg
|-- ksyms
|-- loadavg
|-- locks
|-- meminfo
|-- misc
|-- modules
|-- mounts
|-- mtrr
|-- net
|   |-- arp
|   |-- dev
|   |-- dev_mcast
|   |-- ip_fwchains
|   |-- ip_fwnames
|   |-- ip_masquerade
|   |-- netlink
|   |-- netstat
|   |-- packet
|   |-- psched
|   |-- raw
|   |-- route
|   |-- rt_acct
|   |-- rt_cache
|   |-- rt_cache_stat
|   |-- snmp
|   |-- sockstat
|   |-- softnet_stat
|   |-- tcp
|   |-- udp
|   |-- unix
|   `-- wireless
|-- partitions
|-- pci
|-- scsi
|   |-- ide-scsi
|   |   `-- 0
|   `-- scsi
|-- self -> 2069
|-- slabinfo
|-- stat
|-- swaps
|-- sys
|   |-- abi
|   |   |-- defhandler_coff
|   |   |-- defhandler_elf
|   |   |-- defhandler_lcall7
|   |   |-- defhandler_libcso
|   |   |-- fake_utsname
|   |   `-- trace
|   |-- debug
|   |-- dev
|   |   |-- cdrom
|   |   |   |-- autoclose
|   |   |   |-- autoeject
|   |   |   |-- check_media
|   |   |   |-- debug
|   |   |   |-- info
|   |   |   `-- lock
|   |   `-- parport
|   |       |-- default
|   |       |   |-- spintime
|   |       |   `-- timeslice
|   |       `-- parport0
|   |           |-- autoprobe
|   |           |-- autoprobe0
|   |           |-- autoprobe1
|   |           |-- autoprobe2
|   |           |-- autoprobe3
|   |           |-- base-addr
|   |           |-- devices
|   |           |   |-- active
|   |           |   `-- lp
|   |           |       `-- timeslice
|   |           |-- dma
|   |           |-- irq
|   |           |-- modes
|   |           `-- spintime
|   |-- fs
|   |   |-- binfmt_misc
|   |   |-- dentry-state
|   |   |-- dir-notify-enable
|   |   |-- dquot-nr
|   |   |-- file-max
|   |   |-- file-nr
|   |   |-- inode-nr
|   |   |-- inode-state
|   |   |-- jbd-debug
|   |   |-- lease-break-time
|   |   |-- leases-enable
|   |   |-- overflowgid
|   |   `-- overflowuid
|   |-- kernel
|   |   |-- acct
|   |   |-- cad_pid
|   |   |-- cap-bound
|   |   |-- core_uses_pid
|   |   |-- ctrl-alt-del
|   |   |-- domainname
|   |   |-- hostname
|   |   |-- modprobe
|   |   |-- msgmax
|   |   |-- msgmnb
|   |   |-- msgmni
|   |   |-- osrelease
|   |   |-- ostype
|   |   |-- overflowgid
|   |   |-- overflowuid
|   |   |-- panic
|   |   |-- printk
|   |   |-- random
|   |   |   |-- boot_id
|   |   |   |-- entropy_avail
|   |   |   |-- poolsize
|   |   |   |-- read_wakeup_threshold
|   |   |   |-- uuid
|   |   |   `-- write_wakeup_threshold
|   |   |-- rtsig-max
|   |   |-- rtsig-nr
|   |   |-- sem
|   |   |-- shmall
|   |   |-- shmmax
|   |   |-- shmmni
|   |   |-- sysrq
|   |   |-- tainted
|   |   |-- threads-max
|   |   `-- version
|   |-- net
|   |   |-- 802
|   |   |-- core
|   |   |   |-- hot_list_length
|   |   |   |-- lo_cong
|   |   |   |-- message_burst
|   |   |   |-- message_cost
|   |   |   |-- mod_cong
|   |   |   |-- netdev_max_backlog
|   |   |   |-- no_cong
|   |   |   |-- no_cong_thresh
|   |   |   |-- optmem_max
|   |   |   |-- rmem_default
|   |   |   |-- rmem_max
|   |   |   |-- wmem_default
|   |   |   `-- wmem_max
|   |   |-- ethernet
|   |   |-- ipv4
|   |   |   |-- conf
|   |   |   |   |-- all
|   |   |   |   |   |-- accept_redirects
|   |   |   |   |   |-- accept_source_route
|   |   |   |   |   |-- arp_filter
|   |   |   |   |   |-- bootp_relay
|   |   |   |   |   |-- forwarding
|   |   |   |   |   |-- log_martians
|   |   |   |   |   |-- mc_forwarding
|   |   |   |   |   |-- proxy_arp
|   |   |   |   |   |-- rp_filter
|   |   |   |   |   |-- secure_redirects
|   |   |   |   |   |-- send_redirects
|   |   |   |   |   |-- shared_media
|   |   |   |   |   `-- tag
|   |   |   |   |-- default
|   |   |   |   |   |-- accept_redirects
|   |   |   |   |   |-- accept_source_route
|   |   |   |   |   |-- arp_filter
|   |   |   |   |   |-- bootp_relay
|   |   |   |   |   |-- forwarding
|   |   |   |   |   |-- log_martians
|   |   |   |   |   |-- mc_forwarding
|   |   |   |   |   |-- proxy_arp
|   |   |   |   |   |-- rp_filter
|   |   |   |   |   |-- secure_redirects
|   |   |   |   |   |-- send_redirects
|   |   |   |   |   |-- shared_media
|   |   |   |   |   `-- tag
|   |   |   |   |-- eth0
|   |   |   |   |   |-- accept_redirects
|   |   |   |   |   |-- accept_source_route
|   |   |   |   |   |-- arp_filter
|   |   |   |   |   |-- bootp_relay
|   |   |   |   |   |-- forwarding
|   |   |   |   |   |-- log_martians
|   |   |   |   |   |-- mc_forwarding
|   |   |   |   |   |-- proxy_arp
|   |   |   |   |   |-- rp_filter
|   |   |   |   |   |-- secure_redirects
|   |   |   |   |   |-- send_redirects
|   |   |   |   |   |-- shared_media
|   |   |   |   |   `-- tag
|   |   |   |   |-- eth1
|   |   |   |   |   |-- accept_redirects
|   |   |   |   |   |-- accept_source_route
|   |   |   |   |   |-- arp_filter
|   |   |   |   |   |-- bootp_relay
|   |   |   |   |   |-- forwarding
|   |   |   |   |   |-- log_martians
|   |   |   |   |   |- mc_forwarding
|   |   |   |   |   |-- proxy_arp
|   |   |   |   |   |-- rp_filter
|   |   |   |   |   |-- secure_redirects
|   |   |   |   |   |-- send_redirects
|   |   |   |   |   |-- shared_media
|   |   |   |   |   `-- tag
|   |   |   |   `-- lo
|   |   |   |       |-- accept_redirects
|   |   |   |       |-- accept_source_route
|   |   |   |       |-- arp_filter
|   |   |   |       |-- bootp_relay
|   |   |   |       |-- forwarding
|   |   |   |       |-- log_martians
|   |   |   |       |-- mc_forwarding
|   |   |   |       |-- proxy_arp
|   |   |   |       |-- rp_filter
|   |   |   |       |-- secure_redirects
|   |   |   |       |-- send_redirects
|   |   |   |       |-- shared_media
|   |   |   |       `-- tag
|   |   |   |-- icmp_echo_ignore_all
|   |   |   |-- icmp_echo_ignore_broadcasts
|   |   |   |-- icmp_ignore_bogus_error_responses
|   |   |   |-- icmp_ratelimit
|   |   |   |-- icmp_ratemask
|   |   |   |-- inet_peer_gc_maxtime
|   |   |   |-- inet_peer_gc_mintime
|   |   |   |-- inet_peer_maxttl
|   |   |   |-- inet_peer_minttl
|   |   |   |-- inet_peer_threshold
|   |   |   |-- ip_autoconfig
|   |   |   |-- ip_conntrack_max
|   |   |   |-- ip_default_ttl
|   |   |   |-- ip_dynaddr
|   |   |   |-- ip_forward
|   |   |   |-- ip_local_port_range
|   |   |   |-- ip_no_pmtu_disc
|   |   |   |-- ip_nonlocal_bind
|   |   |   |-- ipfrag_high_thresh
|   |   |   |-- ipfrag_low_thresh
|   |   |   |-- ipfrag_time
|   |   |   |-- neigh
|   |   |   |   |-- default
|   |   |   |   |   |-- anycast_delay
|   |   |   |   |   |-- app_solicit
|   |   |   |   |   |-- base_reachable_time
|   |   |   |   |   |-- delay_first_probe_time
|   |   |   |   |   |-- gc_interval
|   |   |   |   |   |-- gc_stale_time
|   |   |   |   |   |-- gc_thresh1
|   |   |   |   |   |-- gc_thresh2
|   |   |   |   |   |-- gc_thresh3
|   |   |   |   |   |-- locktime
|   |   |   |   |   |-- mcast_solicit
|   |   |   |   |   |-- proxy_delay
|   |   |   |   |   |-- proxy_qlen
|   |   |   |   |   |-- retrans_time
|   |   |   |   |   |-- ucast_solicit
|   |   |   |   |   `-- unres_qlen
|   |   |   |   |-- eth0
|   |   |   |   |   |-- anycast_delay
|   |   |   |   |   |-- app_solicit
|   |   |   |   |   |-- base_reachable_time
|   |   |   |   |   |-- delay_first_probe_time
|   |   |   |   |   |-- gc_stale_time
|   |   |   |   |   |-- locktime
|   |   |   |   |   |-- mcast_solicit
|   |   |   |   |   |-- proxy_delay
|   |   |   |   |   |-- proxy_qlen
|   |   |   |   |   |-- retrans_time
|   |   |   |   |   |-- ucast_solicit
|   |   |   |   |   `-- unres_qlen
|   |   |   |   |-- eth1
|   |   |   |   |   |-- anycast_delay
|   |   |   |   |   |-- app_solicit
|   |   |   |   |   |-- base_reachable_time
|   |   |   |   |   |-- delay_first_probe_time
|   |   |   |   |   |-- gc_stale_time
|   |   |   |   |   |-- locktime
|   |   |   |   |   |-- mcast_solicit
|   |   |   |   |   |-- proxy_delay
|   |   |   |   |   |-- proxy_qlen
|   |   |   |   |   |-- retrans_time
|   |   |   |   |   |-- ucast_solicit
|   |   |   |   |   `-- unres_qlen
|   |   |   |   `-- lo
|   |   |   |       |-- anycast_delay
|   |   |   |       |-- app_solicit
|   |   |   |       |-- base_reachable_time
|   |   |   |       |-- delay_first_probe_time
|   |   |   |       |-- gc_stale_time
|   |   |   |       |-- locktime
|   |   |   |       |-- mcast_solicit
|   |   |   |       |-- proxy_delay
|   |   |   |       |-- proxy_qlen
|   |   |   |       |-- retrans_time
|   |   |   |       |-- ucast_solicit
|   |   |   |       `-- unres_qlen
|   |   |   |-- route
|   |   |   |   |-- error_burst
|   |   |   |   |-- error_cost
|   |   |   |   |-- flush
|   |   |   |   |-- gc_elasticity
|   |   |   |   |-- gc_interval
|   |   |   |   |-- gc_min_interval
|   |   |   |   |-- gc_thresh
|   |   |   |   |-- gc_timeout
|   |   |   |   |-- max_delay
|   |   |   |   |-- max_size
|   |   |   |   |-- min_adv_mss
|   |   |   |   |-- min_delay
|   |   |   |   |-- min_pmtu
|   |   |   |   |-- mtu_expres
|   |   |   |   |-- redirect_load
|   |   |   |   |-- redirect_number
|   |   |   |   `-- redirect_silence
|   |   |   |-- tcp_abort_on_overflow
|   |   |   |-- tcp_adv_win_scale
|   |   |   |-- tcp_app_win
|   |   |   |-- tcp_dsack
|   |   |   |-- tcp_ecn
|   |   |   |-- tcp_fack
|   |   |   |-- tcp_fin_timeout
|   |   |   |-- tcp_keepalive_intvl
|   |   |   |-- tcp_keepalive_probes
|   |   |   |-- tcp_keepalive_time
|   |   |   |-- tcp_max_orphans
|   |   |   |-- tcp_max_syn_backlog
|   |   |   |-- tcp_max_tw_buckets
|   |   |   |-- tcp_mem
|   |   |   |-- tcp_orphan_retries
|   |   |   |-- tcp_reordering
|   |   |   |-- tcp_retrans_collapse
|   |   |   |-- tcp_retries1
|   |   |   |-- tcp_retries2
|   |   |   |-- tcp_rfc1337
|   |   |   |-- tcp_rmem
|   |   |   |-- tcp_sack
|   |   |   |-- tcp_stdurg
|   |   |   |-- tcp_syn_retries
|   |   |   |-- tcp_synack_retries
|   |   |   |-- tcp_syncookies
|   |   |   |-- tcp_timestamps
|   |   |   |-- tcp_tw_recycle
|   |   |   |-- tcp_window_scaling
|   |   |   `-- tcp_wmem
|   |   `-- unix
|   |       `-- max_dgram_qlen
|   |-- proc
|   `-- vm
|       |-- bdflush
|       |-- kswapd
|       |-- max-readahead
|       |-- min-readahead
|       |-- overcommit_memory
|       |-- page-cluster
|       `-- pagetable_cache
|-- sysvipc
|   |-- msg
|   |-- sem
|   `-- shm
|-- tty
|   |-- driver
|   |   `-- serial
|   |-- drivers
|   |-- ldisc
|   `-- ldiscs
|-- uptime
`-- version
	 </screen>
	</para>

	<para>
	 이 디렉토리에는 모든 태스크에 관한 정보가 들어있다. (각 디렉토리의 이름은 태스크의 PID 에 해당하는 숫자이고 이를 이용하여 태스크에 대한 모든 정보 - 실행 파일의 경로라든지 메모리 사용량 등 - 를 볼 수 있다)
	</para>

	<para>
	 흥미로운 점은 이러한 커널의 정보 들을 단지 볼 수만 있는 것이 아니라 (태스크의 정보 혹은 TCP/IP 스택을 활성화하는 네트워크 옵션에 관한 사항들을 보는 것) 몇몇 정보들은 수정할 수도 있다는 것이다. 일반적으로 이러한 것들은 <filename>/proc/sys</filename> 디렉토리 아래에 있다.
	</para>

	<para>
	 <screen>
/proc/sys/ 
          acpi
          dev
          debug
          fs
          proc
          net
          vm
          kernel
	 </screen>
	</para>

	<simplesect id="proc_sys_kernel">
	 <title>/proc/sys/kernel</title>

	 <para>
	  다음은 매우 중요하고 잘 알려진 수정할 수 있는 커널 정보들이다:
	 </para>

	 <para>
	  <screen>
overflowgid
overflowuid
random
threads-max 	// Max number of threads, typically 16384
sysrq 		// kernel hack: you can view instant register values and more
sem
msgmnb
msgmni
msgmax
shmmni
shmall
shmmax
rtsig-max
rtsig-nr
modprobe 	// modprobe file location
printk
ctrl-alt-del
cap-bound
panic
domainname 	// domain name of your Linux box
hostname 	// host name of your Linux box
version 	// date info about kernel compilation
osrelease 	// kernel version (i.e. 2.4.5)
ostype 		// Linux! 
	  </screen>
	 </para>

	</simplesect>

	<simplesect id="proc_sys_net">
	 <title>/proc/sys/net</title>

	 <para>
	  이것은 <filename>proc</filename> 의 하위 디렉토리 중에서 가장 유용한 디렉토리일 것이다. 여기서는 커널의 네트워크 설정에 대한 매우 중요한 부분들을 변경할 수 있다.
	 </para>

	 <para>
	  <screen>
core
ipv4
ipv6
unix
ethernet
802
	  </screen>
	 </para>

	</simplesect>

	<simplesect id="proc_sys_net_core">
	 <title>/proc/sys/net/core</title>

	 <para>
	  아래의 리스트는 <filename>netdev_max_backlog</filename> - 네트워크 패킷의 전체 갯수 (보통 300) - 와 같은 일반적인 네트워크 설정을 보여준다. 이 값은 패킷을 받을 때 네트워크의 BandWidth 를 제한할 수 있다. 리눅스는 버퍼를 비우기 위해 스케줄링이 일어날 때 까지 기다려야 한다 (bottom half 메카니즘에 의해 처리). 1000/HZ ms 일 때
	 </para>

	 <para>
	  <screen>
  300    *        100             =     30 000
packets     HZ(Timeslice freq)         packets/s
 
30 000   *       1000             =      30 M
packets     average (Bytes/packet)   throughput Bytes/s
	  </screen>
	 </para>

	 <para>
	  만약 더 높은 throughput 을 원한다면 <filename>netdev_max_backlog</filename> 을 다음과 같이 증가시킬 수 있다:
	 </para>

	 <para>
	  <command>echo 4000 > /proc/sys/net/core/netdev_max_backlog</command>
	 </para>

	 <para>
	  <note>
	   <para>
	    HZ 값에 주의하기 바란다: (alpha 나 arm-tbox 같은) 특정 환경에서는 이 값이 1000 으로 설정되어 있기 때문에 평균 300 MBytes/sec 의 throughput 이 나온다.
	   </para>
	  </note>
	 </para>

	</simplesect>

	<simplesect id="proc_sys_net_ipv4">
	 <title>/proc/sys/net/ipv4</title>

	 <para>
	  <filename>ip_forward</filename> 값은 리눅스 박스의 IP 포워딩을 활성화하거나 비활성화 시키는 역할을 한다. 이것은 모든 장치에 대한 일반적인 설정이고, 각각의 장치를 선택하여 명시할 수도 있다.
	 </para>

	</simplesect>

	<simplesect id="proc_sys_net_ipv4_conf_interface">
	 <title>/proc/sys/net/ipv4/conf/interface</title>

	 <para>
	  개인적인 생각으로는 이 디렉토리가 <filename>/proc</filename> 중에서 가장 유용한 곳이라고 생각한다. 왜냐하면 이곳에서 네트워크 설정을 조금 변경하여 무선 네트워크를 지원하도록 할 수 있기 때문이다. (자세한 내용은 <ulink url="http://www.bertolinux.com">Wireless-HOWTO</ulink> 문서를 참고하기 바란다)
	 </para>

	 <para>
	  이를 이용하는 예제로는 다음과 같은 것들이 있다:
	 </para>

	 <para>
	  <itemizedlist>
	   
	   <listitem>
	    <para> <filename>forwarding</filename>, 네트워크 인터페이스의 IP 포워딩 기능을 활성화한다. </para>
	   </listitem>

	   <listitem>
	    <para> <filename>proxy_arp</filename>, <emphasis>proxy arp</emphasis> 기능을 활성화한다. 이에 대한 더 자세한 내용은 <ulink url="http://tldp.org">The Linux Documentation Project</ulink> 의 <ulink url="http://tldp.org/HOWTO/mini/Proxy-ARP-Subnet/index.html">ProxyARP Subnetting HOWTO</ulink> 문서와 무선 네트워크 상에서 <emphasis>proxy arp</emphasis> 를 사용하는 법에 대해서 설명한 <ulink url="http://www.bertolinux.com">Wireless HOWTO</ulink> 문서를 참고하기 바란다) </para>
	   </listitem>

	   <listitem>
	    <para> <filename>send_redirects</filename>, 네트워크 인터페이스가 <command>ICMP_REDIRECT</command> 메시지를 보내지 않도록 설정할 수 있다. (위에도 얘기했듯이 더 자세한 내용은 <ulink url="http://www.bertolinux.com">Wireless HOWTO</ulink> 문서를 참고하라) </para>
	   </listitem>

	  </itemizedlist>
	 </para>

	</simplesect>

   </sect2>

 </sect1>

 <sect1 id="linux_multitasking">
  <title>리눅스 멀티태스킹</title>

  <sect2 id="linux_multitasking_overview">
   <title>개요</title>

   <para>
    이 절에서는 리눅스에서 멀티태스킹 환경을 관리하는 메카니즘과 자료 구조에 대해서 분석해 볼 것이다.
   </para>

   <simplesect id="task_states">
    <title>태스크 상태</title>

	<para>
	 리눅스의 태스크는 다음 중의 한가지 상태를 가진다 ([include/linux.h] 참고):
	</para>

	<para>
	 <orderedlist>
	  
	  <listitem>
	   <para> TASK_RUNNING, 이 상태는 태스크가 <emphasis>Ready 리스트</emphasis> 에 들어있음을 말한다. </para>
	  </listitem>

	  <listitem>
	   <para> TASK_INTERRUPTIBLE, 태스크는 시그널이나 자원을 기다리는 중이다. (sleep) </para>
	  </listitem>

	  <listitem>
	   <para> TASK_UNINTERRUPTIBLE, 태스크는 자원을 기다리고 있으며 (sleep), 같은 <emphasis>대기 큐</emphasis> 에 들어있다. </para>
	  </listitem>

	  <listitem>
	   <para> TASK_ZOMBIE, 부모가 없는 자식 태스크 </para>
	  </listitem>

	  <listitem>
	   <para> TASK_STOPPED, 태스크는 디버깅 중이다. </para>
	  </listitem>

	 </orderedlist>
	</para>

	<para>
	 <screen>
       ______________     CPU Available     ______________
      |              |  ---------------->  |              |
      | TASK_RUNNING |                     | Real Running |  
      |______________|  &lt;----------------  |______________|
                           CPU Busy
            |   /|\       
Waiting for |    | Resource  
 Resource   |    | Available             
           \|/   |      
    ______________________                     
   |                      |
   | TASK_INTERRUPTIBLE / |
   | TASK-UNINTERRUPTIBLE |
   |______________________|
 
                     Main Multitasking Flow
	 </screen>
	</para>

   </simplesect>

  </sect2>

  <sect2 id="time_slice">
   <title>타임 슬라이스</title>

   <simplesect id="pit_8253_programming">
    <title>PIT 8253 프로그래밍</title>

	<para>
	 매 10 ms 마다 (HZ 값의 설정에 따라 다를 수 있음) 멀티태스킹 환경을 지원하도록 IRQ0 이 발생한다. 이 신호는 1.19318 MHz 의 주파수를 가지는 PIT 8253 클럭에 연결된 PIC 8259 (i386 이상에서) 칩에서 발생된다.
	</para>

	<para>
	 <screen>
    _____         ______        ______        
   | CPU |&lt;------| 8259 |------| 8253 |
   |_____| IRQ0  |______|      |___/|\|
                                    |_____ CLK 1.193.180 MHz
	 </screen>
	</para>

	<para>
	 <programlisting>
// From include/asm/param.h
#ifndef HZ 
#define HZ 100 
#endif
 
// From include/asm/timex.h
#define CLOCK_TICK_RATE 1193180 /* Underlying HZ */
 
// From include/linux/timex.h
#define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */
 
// From arch/i386/kernel/i8259.c
outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ 
outb_p(LATCH &amp; 0xff , 0x40); /* LSB */
outb(LATCH >> 8 , 0x40); /* MSB */
	 </programlisting>
	</para>

	<para>
	 그래서 8253 PIT (Programmable Inteval Timer) 를 프로그래밍할 때 HZ 값이 100 인 경우 (기본값) LATCH 값을 11931.8 (1193180 / HZ) 로 설정한다. LATCH 값은 주파수를 결정하는 요소가 된다. (frequence divisor factor)
	</para>

	<para>
	 LATCH 를 11931.8 로 설정하는 것은 8253 의 출력값이 1193180/11931.8 = 100 HZ 의 주파수를 가지도록 한다. 따라서 주기는 10 ms 가 된다.
	</para>

	<para>
	 즉, 타임 슬라이스 값은 1/HZ 이다.
	</para>
	
	<para>
	 각 타임 슬라이스 마다 일시적으로 현재 프로세스의 실행을 중지하고 (태스크 스위칭 없이) 몇가지 부수적인 일들을 처리한 후에 다시 이전의 프로세스로 돌아오게 된다.
	</para>

   </simplesect>

   <simplesect id="linux_timer_irq_ica">
    <title>리눅스 타이머 인터럽트 - 함수간 호출 분석</title>

	<para>
	 <screen>
Linux Timer IRQ
IRQ 0 [Timer]
 |  
\|/
|IRQ0x00_interrupt        //   포장 (wrapper) IRQ 핸들러
   |SAVE_ALL              ---   
      |do_IRQ                |  포장 함수 
         |handle_IRQ_event  ---
            |handler() -> timer_interrupt  // 등록된 IRQ 0 핸들러
               |do_timer_interrupt
                  |do_timer  
                     |jiffies++;
                     |update_process_times  
                     |if (--counter &lt;= 0) { // 프로세스의 타임 슬라이스가 다 된 경우
                        |counter = 0;        //   counter 값을 지우고 
                        |need_resched = 1;   //   스케줄링을 요청함
                     |}
         |do_softirq
         |while (need_resched) { // 필요한 경우
            |schedule             //   프로세스를 스케줄링
            |handle_softirq
         |}
   |RESTORE_ALL

	 </screen>
	</para>

	<para>
	 각 함수들은 아래와 같이 찾아볼 수 있다:
	</para>

	<para>
	 <itemizedlist>
	  
	  <listitem>
	   <para> IRQ0x00_interrupt, SAVE_ALL [include/asm/hw_irq.h] </para>
	  </listitem>

	  <listitem>
	   <para> IRQ0x00_interrupt, SAVE_ALL [include/asm/hw_irq.h] </para>
	  </listitem>

	  <listitem>
	   <para> timer_interrupt, do_timer_interrupt [arch/i386/kernel/time.c] </para>
	  </listitem>

	  <listitem>
	   <para> do_timer, update_process_times [kernel/timer.c] </para>
	  </listitem>

	  <listitem>
	   <para> do_softirq [kernel/soft_irq.c] </para>
	  </listitem>

	  <listitem>
	   <para> RESTORE_ALL, while loop [arch/i386/kernel/entry.S] </para>
	  </listitem>

	 </itemizedlist>
	</para>

	<para>
	 <note>
	  <para>
	   <orderedlist>
	    
		<listitem>
		 <para> <function>IRQ0x00_interrupt</function> 함수는 (다른 <function>IRQ0xXY_interrupt</function> 함수처럼) IDT (Interrupt Descriptor Table, 리얼 모드의 Interrupt Vector Table 과 같다. 11 장 참고) 에서 직접 가리키고 있으므로, 프로세서로 들어오는 <emphasis>모든</emphasis> 인터럽트는 <function>IRQ0x#NR_interrupt</function> 함수에 의해 처리된다. (여기서 #NR 은 인터럽트 번호를 의미) 그래서 여기에서는 이런 함수들은 포장 IRQ 핸들러 (wrapper irq handler) 라고 부른다. </para>
		</listitem>

		<listitem>
		 <para> <function>do_IRQ</function> 이나 <function>handle_IRQ_event</function> 와 같은 포장 함수들이 실행된다. [arch/i386/kernel/irq.c] </para>
		</listitem>

		<listitem>
		 <para> 그 이후에는 이전에 <function>request_irq</function> [arch/i386/kernel/irq.c] 에서 등록된 (<function>handler()</function> 로 참조되는) 공식적인 IRQ 루틴 으로 제어가 넘어간다. 이 경우에는 <function>timer_interrupt</function> [arch/i386/kernel/time.c] 가 된다. </para>
		</listitem>

		<listitem>
		 <para> <function>timer_interrupt</function> [arch/i386/kernel/time.c] 가 실행된다. </para>
		</listitem>

		<listitem>
		 <para> 제어는 어셈블리 루틴으로 넘어간다. [arch/i386/kernel/entry.S] </para>
		</listitem>

	   </orderedlist>
	  </para>
	 </note>
	</para>

	<para>
	 설명:
	</para>

	<para>
	 멀티태스킹을 관리하기 위해 리눅스에서는 (다른 유닉스에서와 같이) <varname>counter</varname> 이라는 변수를 두어서 태스크가 CPU 를 얼만큼 사용했는지를 기록해 둔다. 그래서 매 IRQ 0 에서 이 값을 감소시키고 (4 번 부분에 해당), <varname>counter</varname> 값이 0 이 되면 태스크 스위칭이 일어나도록 한다. (4 번에서 <varname>need_resched</varname> 값을 1 로 설정하고, 5 번에서 <varname>resched</varname> 값에 따라 <function>schedule</function> [kernel/sched.c] 이 실행된다)
	</para>

   </simplesect>

  </sect2>

  <sect2 id="scheduler">
   <title>스케줄러</title>

   <para>
    스케줄러는 주어진 특정 시간에서 실행되어야 할 태스크를 선택하는 부분의 코드를 말한다.
   </para>

   <para>
    실행되고 있는 태스크를 교체해야 하는 경우에 그 후보가 될 태스크를 선택한다. 아래는 <function>schedule</function> 함수이다. [kernel/sched.c] 
   </para>

   <para>
    <screen>
|schedule
   |do_softirq // 이전에 걸린 soft-IRQ 처리
   |for each task
      |calculate counter
   |prepare_to__switch // 태스크 스위칭을 위한 환경 설정
   |switch_mm // 메모리 환경 변경 (CR3 레지스터 값 수정)
   |switch_to (assembler)
      |SAVE ESP
      |RESTORE future_ESP
      |SAVE EIP
      |push future_EIP *** 함수 호출을 한 것 처럼 파라미터 설정
         |jmp __switch_to (TSS 에 관련된 작업을 수행)
         |__switch_to()
          ..
         |ret *** future_EIP 가 가리키는 곳으로 복귀
      new_task
	</screen>
   </para>

  </sect2>

  <sect2 id="bottom_half_task_queues_and_tasklets">
   <title>하반부 (Bottom Half), 태스크 큐 (Task Queues), 소작업 (Tasklets)</title>

   <simplesect id="bottom_half_overview">
    <title>개요</title>

	<para>
	 전통적인 유닉스에서는 (장치로 부터) IRQ 가 발생했을 때, 장치가 요청한 태스크를 처리하기 위해 <emphasis>태스크 스위칭</emphasis> 을 수행하였다.
	</para>

	<para>
	 리눅스에서는 성능을 향상시키기 위해 긴급하지 않은 작업은 뒤로 미루어 더 빠른 이벤트 처리를 할 수 있도록 한다.
	</para>

	<para>
	 이 기능은 커널 버전 1.x 에서부터 <emphasis>하반부 (Bottom Half - <acronym>BH</acronym>)</emphasis> 에 의해 수행되어 졌다. IRQ 핸들러에서는 (나중에 - 스케줄링시 - 수행되도록) 하반부로 표시한다.
	</para>

	<para>
	 최신의 커널 버전에서는 좀 더 동적이고 멀티 프로세서 환경을 지원하는 <emphasis>소작업 (Tasklet)</emphasis> 이라는 개념을 도입하였다.
	</para>

	<para>
	 하반부는 다음과 같은 형태가 될 것이다:
	</para>

	<para>
	 <orderedlist>
	  
	  <listitem>
	   <para> 선언 (Declaration)</para>
	  </listitem>

	  <listitem>
	   <para> 표시 (Mark)</para>
	  </listitem>

	  <listitem>
	   <para> 실행 (Execution)</para>
	  </listitem>

	 </orderedlist>
	</para>

   </simplesect>

   <simplesect id="bottom_half_declaration">
    <title>선언</title>

	<para>
	 <programlisting>
#define DECLARE_TASK_QUEUE(q) LIST_HEAD(q)
#define LIST_HEAD(name) \
   struct list_head name = LIST_HEAD_INIT(name) 
struct list_head { 
   struct list_head *next, *prev; 
};
#define LIST_HEAD_INIT(name) { &amp;(name), &amp;(name) }
	 </programlisting>
	</para>

	<para>
	 <screen>
        DECLARE_TASK_QUEUE [include/linux/tqueue.h, include/linux/list.h]
	 </screen>
	</para>

	<para>
	 <function>DECLARE_TASK_QUEUE(q)</function> 매크로는 태스크 큐를 관리하는 <varname>q</varname> 라는 구조체를 선언하는데 사용된다.
	</para>

   </simplesect>

   <simplesect id="bottom_half_mark">
    <title>표시</title>

	<para>
	 아래는 <function>mark_bh</function> 함수에 대한 함수간 호출 분석이다 [include/linux/interrupt.h]:
	</para>

	<para>
	 <screen>
|mark_bh(NUMBER)
   |tasklet_hi_schedule(bh_task_vec + NUMBER)
      |insert into tasklet_hi_vec
         |__cpu_raise_softirq(HI_SOFTIRQ) 
            |soft_active |= (1 &lt;&lt; HI_SOFTIRQ)

                   ''mark_bh''[include/linux/interrupt.h]
	 </screen>
	</para>

   </simplesect>

   <simplesect id="bottom_half_execution">
    <title>실행</title>

	<para>
	 <function>do_IRQ</function> 함수에서 아래와 같은 부분이 호출되는 것을 볼 수 있을 것이다 [arch/i386/kernel/irq.c]:
	</para>

	<para>
	 <screen>
|do_softirq
   |h->action(h)-> softirq_vec[TASKLET_SOFTIRQ]->action -> tasklet_action
      |tasklet_vec[0].list->func
	 </screen>
	</para>

	<para>
	 <function>h->action(h)</function> 은 이전에 큐에 들어가 있던 함수이다.
	</para>

   </simplesect>

  </sect2>

  <sect2 id="very_low_level_routines">
   <title>저수준 루틴들</title>

   <para>
    set_intr_gate
   </para>

   <para>
    set_trap_gate
   </para>

   <para>
    set_task_gate (사용되지 않음)
   </para>

   <para>
    (*interrupt)[NR_IRQS](void) = { IRQ0x00_interrupt, IRQ0x01_interrupt, ..} 
   </para>

   <para>
    NR_IRQS = 224 [kernel 2.4.2]
   </para>

  </sect2>

  <sect2 id="task_switching">
   <title>태스크 스위칭</title>

   <simplesect id="when_does_task_switching_occur">
    <title>태스크 스위칭은 언제 일어나는가?</title>

	<para>
	 여기서는 리눅스 커널이 수행중인 태스크를 다른 태스크로 교체하는 것을 살펴볼 것이다.
	</para>

	<para>
	 태스크 스위칭은 다음과 같은 많은 경우에서 필요하다:
	</para>

	<para>
	 <itemizedlist>
	  
	  <listitem>
	   <para> 타임 슬라이스가 끝난 경우, 다른 태스크를 실행해야 한다. </para>
	  </listitem>

	  <listitem>
	   <para> 태스크가 자원을 사용하려는 경우 이 자원을 이용할 수 있을 때까지 잠들게 된다. 이 때에도 다른 태스크를 실행해야 한다. </para>
	  </listitem>

	  <listitem>
	   <para> 태스크가 파이프를 기다리고 있는 경우, 파이프에 쓰기 연산을 수행할 다른 태스크를 실행해야 한다. </para>
	  </listitem>

	 </itemizedlist>
	</para>

   </simplesect>

   <simplesect id="task_switching_function">
    <title>태스크 스위칭</title>

	<para>
	 <programlisting>
                           TASK SWITCHING TRICK
#define switch_to(prev,next,last) do {                                  \
        asm volatile("pushl %%esi\n\t"                                  \
                     "pushl %%edi\n\t"                                  \
                     "pushl %%ebp\n\t"                                  \
                     "movl %%esp,%0\n\t"        /* save ESP */          \
                     "movl %3,%%esp\n\t"        /* restore ESP */       \
                     "movl $1f,%1\n\t"          /* save EIP */          \
                     "pushl %4\n\t"             /* restore EIP */       \
                     "jmp __switch_to\n"                                \
                     "1:\t"                                             \
                     "popl %%ebp\n\t"                                   \
                     "popl %%edi\n\t"                                   \
                     "popl %%esi\n\t"                                   \
                     :"=m" (prev->thread.esp),"=m" (prev->thread.eip),  \
                      "=b" (last)                                       \
                     :"m" (next->thread.esp),"m" (next->thread.eip),    \
                      "a" (prev), "d" (next),                           \
                      "b" (prev));                                      \
} while (0)
	 </programlisting>
	</para>

	<para>
	 여기에는 다음과 같은 트릭이 존재한다:
	</para>

	<para>
	 <orderedlist>
	  
	  <listitem>
	   <para> <function>push %4</function> 를 통해 스택에 <varname>future_EIP</varname> 를 넣는다. </para>
	  </listitem>

	  <listitem>
	   <para> <function>jmp __switch_to</function> 는 <function>__switch_to</function> 함수를 실행한다. 하지만 <function>call</function> 명령을 통해 실행한 것이 아니기 때문에 실행된 뒤에는 위에서 설정한 <varname>future_EIP</varname> 부분으로 리턴될 것이다. (따라서 새로운 태스크가 실행된다) </para>
	  </listitem>

	 </orderedlist>
	</para>

	<para>
	 <screen>
      U S E R   M O D E                 K E R N E L     M O D E

 |          |     |          |       |          |     |          |
 |          |     |          | Timer |          |     |          |
 |          |     |  Normal  |  IRQ  |          |     |          |
 |          |     |   Exec   |------>|Timer_Int.|     |          |
 |          |     |     |    |       | ..       |     |          |
 |          |     |    \|/   |       |schedule()|     | Task1 Ret|
 |          |     |          |       |_switch_to|&lt;--  |  Address |
 |__________|     |__________|       |          |  |  |          |
                                     |          |  |S |          | 
Task1 Data/Stack   Task1 Code        |          |  |w |          |
                                     |          | T|i |          |
                                     |          | a|t |          |
 |          |     |          |       |          | s|c |          |
 |          |     |          | Timer |          | k|h |          |
 |          |     |  Normal  |  IRQ  |          |  |i |          | 
 |          |     |   Exec   |------>|Timer_Int.|  |n |          |
 |          |     |     |    |       | ..       |  |g |          |
 |          |     |    \|/   |       |schedule()|  |  | Task2 Ret|
 |          |     |          |       |_switch_to|&lt;--  |  Address |
 |__________|     |__________|       |__________|     |__________|
 
Task2 Data/Stack   Task2 Code        Kernel Code  Kernel Data/Stack
	 </screen>
	</para>

   </simplesect>

  </sect2>

  <sect2 id="fork">
   <title>Fork</title>

   <simplesect id="fork_overview">
    <title>개요</title>

	<para>
	 fork 는 새로운 태스크를 생성하기 위해 사용된다. Task Parent 에서 시작하여 Task Child 로 많은 자료구조를 복사한다.
	</para>

	<para>
	 <screen>
                               |         |
                               | ..      |
         Task Parent           |         |
         |         |           |         |
         |  fork   |---------->|  CREATE |   
         |         |          /|   NEW   |
         |_________|         / |   TASK  |
                            /  |         |
             ---           /   |         |
             ---          /    | ..      |
                         /     |         |
         Task Child     / 
         |         |   /
         |  fork   |&lt;-/
         |         |
         |_________|
              
                       Fork SysCall
	 </screen>
	</para>

   </simplesect>

   <simplesect id="fork_what_is_not_copied">
    <title>무엇이 복사되지 않는가?</title>

	<para>
	 새로 생성된 태스크 (Task Child) 는 그 부모 태스크 (Task Parent) 와 거의 같지만, 다음과 같은 차이점이 있다:
	</para>

	<para>
	 <orderedlist>
	  
	  <listitem>
	   <para> 명백한 차이점으로는 PID 값이 다르다. </para>
	  </listitem>

	  <listitem>
	   <para> 자식 태스크에서는 <function>fork()</function> 함수가 0 을 리턴하지만, 부모 태스크에서는 자식 태스크의 PID 값은 리턴한다. (사용자 모드에서 이를 이용해 서로를 구별한다) </para>
	  </listitem>

	  <listitem>
	   <para> 자식 태스크의 모든 데이타 페이지는 (부모 태스크에서 자신의 페이지에 <emphasis>WRITE</emphasis> 권한을 가지도록 설정된 데 반해) <emphasis>WRITE</emphasis> 권한 없이 <emphasis>READ + EXECUTE</emphasis> 권한으로 설정되어 진다. 그래서 쓰기 연산이 요청되면 <emphasis>Page Fault</emphasis> 예외가 발생되어 새로운 독립적인 페이지를 생성하게 된다. 이러한 메카니즘을 <emphasis>Copy on Write</emphasis> 라고 부른다. (더 자세한 사항은 <xref linkend="copy_on_write">&nbsp;</xref> 에서 설명한다) </para>
	  </listitem>

	 </orderedlist>
	</para>

   </simplesect>

   <simplesect id="fork_ica">
    <title>fork 함수간 호출 분석</title>
	
	<para>
	 <screen>
|sys_fork 
   |do_fork
      |alloc_task_struct 
         |__get_free_pages
       |p->state = TASK_UNINTERRUPTIBLE
       |copy_flags
       |p->pid = get_pid    
       |copy_files
       |copy_fs
       |copy_sighand
       |copy_mm // CopyOnWrite 메카니즘을 관리 (I part)
          |allocate_mm
          |mm_init
             |pgd_alloc -> get_pgd_fast
                |get_pgd_slow
          |dup_mmap
             |copy_page_range
                |ptep_set_wrprotect
                   |clear_bit // 페이지를 read-only 로 설정
          |copy_segments // For LDT
       |copy_thread
          |childregs->eax = 0  
          |p->thread.esp = childregs // 자식 태스크에서 0 을 리턴하도록 설정
          |p->thread.eip = ret_from_fork // 자식 태스크가 수행될 시작 부분 설정
       |retval = p->pid // 부모 태스크는 자식 태스크의 pid 를 리턴하도록 설정
       |SET_LINKS // 태스크를 리스트 포인터에 삽입
       |nr_threads++ // 전역 변수 
       |wake_up_process(p) // 새로 생성된 자식 태스크를 깨움
       |return retval
              
               fork ICA
	 </screen>
	</para>

	<para>
	 <itemizedlist>
	  
	  <listitem>
	   <para> sys_fork [arch/i386/kernel/process.c] </para>
	  </listitem>

	  <listitem>
	   <para> do_fork [kernel/fork.c] </para>
	  </listitem>

	  <listitem>
	   <para> alloc_task_struct [include/asm/processor.c] </para>
	  </listitem>

	  <listitem>
	   <para> __get_free_pages [mm/page_alloc.c] </para>
	  </listitem>

	  <listitem>
	   <para> get_pid [kernel/fork.c] </para>
	  </listitem>

	  <listitem>
	   <para> copy_files </para>
	  </listitem>

	  <listitem>
	   <para> copy_fs </para>
	  </listitem>

	  <listitem>
	   <para> copy_sighand  </para>
	  </listitem>

	  <listitem>
	   <para> copy_mm </para>
	  </listitem>

	  <listitem>
	   <para> allocate_mm </para>
	  </listitem>

	  <listitem>
	   <para> mm_init </para>
	  </listitem>

	  <listitem>
	   <para> pgd_alloc -> get_pgd_fast [include/asm/pgalloc.h] </para>
	  </listitem>

	  <listitem>
	   <para> get_pgd_slow </para>
	  </listitem>

	  <listitem>
	   <para> dup_mmap [kernel/fork.c] </para>
	  </listitem>

	  <listitem>
	   <para> copy_page_range [mm/memory.c] </para>
	  </listitem>

	  <listitem>
	   <para> ptep_set_wrprotect [include/asm/pgtable.h] </para>
	  </listitem>

	  <listitem>
	   <para> clear_bit [include/asm/bitops.h] </para>
	  </listitem>

	  <listitem>
	   <para> copy_segments [arch/i386/kernel/process.c] </para>
	  </listitem>

	  <listitem>
	   <para> copy_thread </para>
	  </listitem>

	  <listitem>
	   <para> SET_LINKS [include/linux/sched.h] </para>
	  </listitem>

	  <listitem>
	   <para> wake_up_process [kernel/sched.c] </para>
	  </listitem>

	 </itemizedlist>
	</para>

   </simplesect>

   <simplesect id="fork_copy_on_write">
    <title>Copy on Write</title>

	<para>
	 리눅스에서는 다음과 같은 방법으로 Copy on Write 를 구현한다:
	</para>

	<para>
	 <orderedlist>
	  
	  <listitem>
	   <para> 쓰기 연산이 일어날 때 <emphasis>Page Falut</emphasis> 가 일어나도록 복사된 모든 페이지를 read-only 로 표시한다. </para>
	  </listitem>

	  <listitem>
	   <para> <emphasis>Page Fault</emphasis> 핸들러에서 새로운 페이지를 생성한다. </para>
	  </listitem>

	 </orderedlist>
	</para>

	<para>
	 <screen>
 | Page 
 | Fault 
 | Exception
 |
 |
 -----------> |do_page_fault
                 |handle_mm_fault
                    |handle_pte_fault 
                       |do_wp_page        
                          |alloc_page      // 새로운 페이지 할당
                          |break_cow
                             |copy_cow_page // 이전 페이지를 새로운 페이지로 복사
                             |establish_pte // 페이지 테이블 포인터를 다시 설정
                                |set_pte
                            
              Page Fault ICA
	 </screen>
	</para>

	<para>
	 <itemizedlist>
	  
	  <listitem>
	   <para> do_page_fault [arch/i386/mm/fault.c] </para>
	  </listitem>

	  <listitem>
	   <para> handle_mm_fault [mm/memory.c] </para>
	  </listitem>

	  <listitem>
	   <para> handle_pte_fault </para>
	  </listitem>

	  <listitem>
	   <para> do_wp_page </para>
	  </listitem>

	  <listitem>
	   <para> alloc_page [include/linux/mm.h] </para>
	  </listitem>

	  <listitem>
	   <para> break_cow [mm/memory.c] </para>
	  </listitem>

	  <listitem>
	   <para> copy_cow_page </para>
	  </listitem>

	  <listitem>
	   <para> establish_pte </para>
	  </listitem>

	  <listitem>
	   <para> set_pte [include/asm/pgtable-3level.h] </para>
	  </listitem>

	 </itemizedlist>
	</para>

   </simplesect>

  </sect2>

 </sect1>

 <sect1 id="linux_memory_management">
  <title>메모리 관리</title>

  <sect2 id="linux_memory_management_overview">
   <title>개요</title>

   <para>
    간단히 말해서, 리눅스는 segmentation 과 pagnation 을 동시에 사용한다.
   </para>

   <simplesect id="linux_memory_management_segments">
    <title>세그먼트</title>

	<para>
	 리눅스는 오직 4 개의 세그먼트 만을 사용한다:
	</para>

	<para>
	 <itemizedlist>
	  
	  <listitem>
	   <para> <emphasis>KERNEL SPACE</emphasis> (0xC000 0000 (3 GB) 부터 0xFFFF FFFF (4 GB) 까지) 를 위한 2 개의 세그먼트 (code 와 data/stack) </para>
	  </listitem>

	  <listitem>
	   <para> <emphasis>USER SPACE</emphasis> (0 (0 GB) 부터 0xBFFF FFFF (3 GB) 까지) 를 위한 2 개의 세그먼트 (code 와 data/stack) </para>
	  </listitem>

	 </itemizedlist>
	</para>

	<para>
	 <screen>
                               __
   4 GB--->|                |    |
           |     Kernel     |    |  Kernel Space (Code + Data/Stack)
           |                |  __|
   3 GB--->|----------------|  __
           |                |    |
           |                |    |
   2 GB--->|                |    |
           |     Tasks      |    |  User Space (Code + Data/Stack)
           |                |    |
   1 GB--->|                |    |
           |                |    |
           |________________|  __| 
 0x00000000
          Kernel/User Linear addresses
	 </screen>
	</para>

   </simplesect>

  </sect2>

  <sect2 id="specific_i386_implementation">
   <title>i386 에서의 특징</title>

   <para>
    이전에도 말했듯이 리눅스는 Pagination 을 3 단계의 페이징을 이용해서 구현하지만, i386 환경에서는 실제로 그 중에서 2 단계 만을 사용한다:
   </para>

   <para>
    <screen>
   ------------------------------------------------------------------
   L    I    N    E    A    R         A    D    D    R    E    S    S
   ------------------------------------------------------------------
        \___/                 \___/                     \_____/ 
 
     PD offset              PF offset                 Frame offset 
     [10 bits]              [10 bits]                 [12 bits]       
          |                     |                          |
          |                     |     -----------          |        
          |                     |     |  Value  |----------|---------
          |     |         |     |     |---------|   /|\    |        |
          |     |         |     |     |         |    |     |        |
          |     |         |     |     |         |    | Frame offset |
          |     |         |     |     |         |   \|/             |
          |     |         |     |     |---------|&lt;------            |
          |     |         |     |     |         |      |            |
          |     |         |     |     |         |      | x 4096     |
          |     |         |  PF offset|_________|-------            |
          |     |         |       /|\ |         |                   |
      PD offset |_________|-----   |  |         |          _________|
            /|\ |         |    |   |  |         |          | 
             |  |         |    |  \|/ |         |         \|/
 _____       |  |         |    ------>|_________|   PHYSICAL ADDRESS 
|     |     \|/ |         |    x 4096 |         |
| CR3 |-------->|         |           |         |
|_____|         | ....... |           | ....... |
                |         |           |         |    
 
               Page Directory          Page File

                       Linux i386 Paging
	</screen>
   </para>

  </sect2>

  <sect2 id="memory_mapping">
   <title>메모리 매핑</title>

   <para>
    리눅스에서는 페이지 단위로만 접근 제어를 관리한다. 그래서 서로 다른 태스크가 같은 세그먼트 주소를 가질 수 있지만, 다른 페이지 테이블 엔트리를 가리키는 각각의 CR3 (페이지 디렉토리의 주소를 저장하고 있는 레지스터) 값을 가진다.
   </para>

   <para>
    사용자 모드의 태스크는 3 GB (0x C000 0000) 의 제한을 넘을 수 없다. 그래서 페이지 디렉토리의 첫 768 엔트리만이 의미를 가진다. (768 * 4 MB = 3 GB)
   </para>

   <para>
    태스크가 (시스템 콜이나 IRQ 에 의해) 커널 모드로 진입하게 되면, 나머지 256 엔트리가 중요해진다. 이들은 다른 모든 태스크에서 동일한 페이지를 가리킨다. (커널에서 사용)
   </para>

   <para>
    (오직) 커널의 선형 주소 공간은 커널의 물리적인 주소 공간과 동일함에 주의하라. 즉:
   </para>

   <para>
    <screen>
            ________________ _____                    
           |Other KernelData|___  |  |                |
           |----------------|   | |__|                |
           |     Kernel     |\  |____|   Real Other   |
  3 GB --->|----------------| \      |   Kernel Data  |
           |                |\ \     |                |
           |              __|_\_\____|__   Real       |
           |      Tasks     |  \ \   |     Tasks      |
           |              __|___\_\__|__   Space      |
           |                |    \ \ |                |
           |                |     \ \|----------------|
           |                |      \ |Real KernelSpace|
           |________________|       \|________________|
      
           Logical Addresses          Physical Addresses
	</screen>
   </para>

   <para>
    커널의 선형 주소 공간은 3 GB 아래에 있는 물리적인 주소 공간에 대응한다. (실제로 페이지 테이블은 { "00000000", "00000001" } 와 같은 형태를 가진다. 즉 어떠한 변환 과정이 일어나는 것은 아니며, 단지 선형 주소 공간에 대한 물리적인 주소를 나타낼 뿐이다.)
   </para>

   <para>
    페이지 테이블을 이용하여 물리 주소를 관리하기 때문에 커널과 사용자 모드 간의 <emphasis>주소 충돌</emphasis> 과 같은 일은 일어나지 않는다.
   </para>

  </sect2>

  <sect2 id="low_level_memory_allocation">
   <title>저수준의 메모리 할당</title>

   <simplesect id="boot_initialization">
    <title>부팅시 초기화 과정</title>

	<para>
	 먼저 <function>kmem_cache_init</function> 함수 (부팅시 <function>start_kernel</function> [init/main.c] 에서 호출) 부터 살펴보자.
	</para>

	<para>
	 <screen>
|kmem_cache_init
   |kmem_cache_estimate
	 </screen>
	</para>

	<para>
	 <itemizedlist>
	  
	  <listitem>
	   <para> kmem_cache_init [mm/slab.c] </para>
	  </listitem>

	  <listitem>
	   <para> kmem_cache_estimate </para>
	  </listitem>

	 </itemizedlist>
	</para>

	<para>
	 다음으로 살펴볼 함수는 <function>mem_init</function> (역시 <function>start_kernel</function> [init/main.c] 에서 호출됨) 이다.
	</para>

	<para>
	 <screen>
|mem_init
   |free_all_bootmem
      |free_all_bootmem_core
	 </screen>
	</para>

	<para>
	 <itemizedlist>
	  
	  <listitem>
	   <para> mem_init [arch/i386/mm/init.c] </para>
	  </listitem>

	  <listitem>
	   <para> free_all_bootmem [mm/bootmem.c] </para>
	  </listitem>

	  <listitem>
	   <para> free_all_bootmem_core </para>
	  </listitem>

	 </itemizedlist>
	</para>

   </simplesect>

   <simplesect id="runtime_allocation">
    <title>런타임 할당</title>

	<para>
	 리눅스에서 메모리를 할당받기를 원하는 경우이다. 예를 들면 <emphasis>Copy on Write</emphasis> 메카니즘 동안에 일어나는 일들을 살펴보면 (<xref linkend="copy_on_write">&nbsp;</xref> 참고):
	</para>

	<para>
	 <screen>
|copy_mm 
   |allocate_mm = kmem_cache_alloc
      |__kmem_cache_alloc
         |kmem_cache_alloc_one
            |alloc_new_slab
               |kmem_cache_grow
                  |kmem_getpages
                     |__get_free_pages
                        |alloc_pages
                           |alloc_pages_pgdat
                              |__alloc_pages
                                 |rmqueue   
                                 |reclaim_pages
	 </screen>
	</para>

	<para>
	 각 함수들은 아래의 리스트에서 찾아볼 수 있다:
	</para>

	<para>
	 <itemizedlist>

	  <listitem>
	   <para> copy_mm [kernel/fork.c] </para>
	  </listitem>

	  <listitem>
	   <para> allocate_mm [kernel/fork.c] </para>
	  </listitem>

	  <listitem>
	   <para> kmem_cache_alloc [mm/slab.c] </para>
	  </listitem>

	  <listitem>
	   <para> __kmem_cache_alloc </para>
	  </listitem>

	  <listitem>
	   <para> kmem_cache_alloc_one </para>
	  </listitem>

	  <listitem>
	   <para> alloc_new_slab </para>
	  </listitem>

	  <listitem>
	   <para> kmem_cache_grow </para>
	  </listitem>

	  <listitem>
	   <para> kmem_getpages </para>
	  </listitem>

	  <listitem>
	   <para> __get_free_pages [mm/page_alloc.c] </para>
	  </listitem>

	  <listitem>
	   <para> alloc_pages [mm/numa.c] </para>
	  </listitem>

	  <listitem>
	   <para> alloc_pages_pgdat </para>
	  </listitem>

	  <listitem>
	   <para> __alloc_pages [mm/page_alloc.c] </para>
	  </listitem>

	  <listitem>
	   <para> rm_queue </para>
	  </listitem>

	  <listitem>
	   <para> reclaim_pages [mm/vmscan.c] </para>
	  </listitem>

	 </itemizedlist>
	</para>

	<para>
	 TODO: Understand Zones 
	</para>

   </simplesect>

  </sect2>

  <sect2 id="swap">
   <title>스와핑</title>

   <simplesect id="swap_overview">
    <title>개요</title>

	<para>
	 스와핑은 <function>kswapd</function> 데몬 (커널 쓰레드) 이 관리한다.
	</para>

   </simplesect>

   <simplesect id="kswapd">
    <title>kswapd</title>

	<para>
	 다른 커널 쓰레드들과 마찬가지로 <function>kswapd</function> 도 깨어나기를 기다리는 메인 루프를 가진다.
	</para>

	<para>
	 <screen>
|kswapd
   |// 초기화 루틴
   |for (;;) { // 메인 루프
      |do_try_to_free_pages
      |recalculate_vm_stats
      |refill_inactive_scan
      |run_task_queue
      |interruptible_sleep_on_timeout // 새로운 스왑 요청이 있을 때까지 잠듬
   |}
	 </screen>
	</para>

	<para>
	 <itemizedlist>
	  
	  <listitem>
	   <para> kswapd [mm/vmscan.c] </para>
	  </listitem>

	  <listitem>
	   <para> do_try_to_free_pages </para>
	  </listitem>

	  <listitem>
	   <para> recalculate_vm_stats [mm/swap.c] </para>
	  </listitem>

	  <listitem>
	   <para> refill_inactive_scan [mm/vmswap.c] </para>
	  </listitem>

	  <listitem>
	   <para> run_task_queue [kernel/softirq.c] </para>
	  </listitem>

	  <listitem>
	   <para> interruptible_sleep_on_timeout [kernel/sched.c] </para>
	  </listitem>

	 </itemizedlist>
	</para>

   </simplesect>

   <simplesect id="when_do_you_need_swapping">
    <title>언제 스와핑이 필요한가?</title>

	<para>
	 스와핑은 실제 메모리 상에 존재하지 않는 페이지에 접근하고자 할 때 필요하다.
	</para>

	<para>
	 리눅스에서는 <function>kswapd</function> 커널 쓰레드를 이용하여 이러한 작업을 수행한다. 태스크가 페이지 폴트 예외를 받게 되면 다음과 같은 일들이 수행된다:
	</para>

	<para>
	 <screen>
 | Page Fault Exception 이 발생하였을 때
 | 다음과 같은 조건을 모두 만족하는 경우:
 |   a-) 사용자 모드의 페이지
 |   b-) 읽기 혹은 쓰기를 위한 접근
 |   c-) 페이지가 물리적인 메모리에 존재하지 않음
 |
 |
 -----------> |do_page_fault
                 |handle_mm_fault
                    |pte_alloc 
                       |pte_alloc_one
                          |__get_free_page = __get_free_pages
                             |alloc_pages
                                |alloc_pages_pgdat
                                   |__alloc_pages
                                      |wakeup_kswapd // kswapd 커널 쓰레드를 깨움
   
                   Page Fault ICA
	 </screen>
	</para>

	<para>
	 <itemizedlist>
	  
	  <listitem>
	   <para> do_page_fault [arch/i386/mm/fault.c] </para>
	  </listitem>

	  <listitem>
	   <para> handle_mm_fault [mm/memory.c] </para>
	  </listitem>

	  <listitem>
	   <para> pte_alloc </para>
	  </listitem>

	  <listitem>
	   <para> pte_alloc_one [include/asm/pgalloc.h] </para>
	  </listitem>

	  <listitem>
	   <para> __get_free_page [include/linux/mm.h] </para>
	  </listitem>

	  <listitem>
	   <para> __get_free_pages [mm/page_alloc.c] </para>
	  </listitem>

	  <listitem>
	   <para> alloc_pages [mm/numa.c] </para>
	  </listitem>

	  <listitem>
	   <para> alloc_pages_pgdat </para>
	  </listitem>

	  <listitem>
	   <para> __alloc_pages </para>
	  </listitem>

	  <listitem>
	   <para> wakeup_kswapd [mm/vmscan.c] </para>
	  </listitem>

	 </itemizedlist>
	</para>

   </simplesect>

  </sect2>

 </sect1>

 <sect1 id="linux_networking">
  <title>리눅스 네트워킹</title>

  <sect2 id="how_linux_networking_is_managed">
   <title>리눅스의 네트워크 관리 방법</title>

   <para>
    각각의 <acronym>NIC</acronym> (Network Interface Card) 의 종류에 따라 디바이스 드라이버가 존재한다. 그 안에서는 <emphasis>항상</emphasis> 표준적인 고수준 루틴인 <function>netif_rx [net/core/dev.c]</function> 를 호출할 것이다. 이 함수는 프레임이 속한 3 단계의 프로토콜 (일반적으로 IP) 을 제어하고, 그에 대한 적절한 함수를 호출한다. (이를 위해 함수 포인터를 사용할 것이다)
   </para>

  </sect2>

  <sect2 id="tcp_example">
   <title>TCP 예제</title>

   <para>
    이제부터는 우리가 리눅스 박스에 TCP 패킷을 보낸 경우에 어떤 일이 일어나는지 살펴볼 것이다.
   </para>

   <simplesect id="interrupt_management_netif_rx">
    <title>인터럽트 처리: netif_rx</title>

	<para>
	 <screen>
|netif_rx
   |__skb_queue_tail
      |qlen++
      |* 단순한 포인터 삽입 연산 *    
   |cpu_raise_softirq
      |softirq_active(cpu) |= (1 &lt;&lt; NET_RX_SOFTIRQ) // BH 벡터 (softirq) 내에 NET_RX_SOFTIRQ 비트 설정
	 </screen>
	</para>

	<para>
	 <itemizedlist>
	  
	  <listitem>
	   <para> __skb_queue_tail [include/linux/skbuff.h] </para>
	  </listitem>

	  <listitem>
	   <para> cpu_raise_softirq [kernel/softirq.c] </para>
	  </listitem>

	 </itemizedlist>
	</para>

   </simplesect>

   <simplesect id="post_interrupt_management_net_rx_action">
    <title>인터럽트 처리 후: net_rx_action</title>

	<para>
	 일단 IRQ 가 처리된 후에 프레임이 어떻게 처리되는지와 <varname>NET_RX_SOFTIRQ</varname> 이 무슨 일을 하는지를 살펴볼 필요가 있다.
	</para>

	<para>
	 다음으로 살펴볼 함수는 <function>net_dev_init [net/core/dev.c]</function> 에서 설정한 <function>net_rx_action [net/core/dev.c]</function> 함수이다. 
	</para>

	<para>
	 <screen>
|net_rx_action
   |skb = __skb_dequeue (the exact opposite of __skb_queue_tail)
   |for (ptype = first_protocol; ptype &lt; max_protocol; ptype++) // 네트워크 프로토콜이 무엇인지 알아냄
      |if (skb->protocol == ptype)                               
         |ptype->func -> ip_rcv // ''struct ip_packet_type [net/ipv4/ip_output.c]'' 에서 설정
 
    **** 여기서부터는 IP 패킷에 대한 처리 ****
         |ip_rcv
            |NF_HOOK (ip_rcv_finish)
               |ip_route_input // 호출할 함수를 결정하기 위해 라우팅 테이블 검색
                  |skb->dst->input -> ip_local_deliver // 앞의 라우팅 테이블 검색에 따라 목적지는 로컬 머신
                     |ip_defrag // IP 패킷의 조각 (fragments) 들을 다시 합침
                        |NF_HOOK (ip_local_deliver_finish)
                           |ipprot->handler -> tcp_v4_rcv // ''tcp_protocol [include/net/protocol.c]'' 에서 설정
 
     **** 여기서부터는 TCP 패킷에 대한 처리 ****
                           |tcp_v4_rcv   
                              |sk = __tcp_v4_lookup 
                              |tcp_v4_do_rcv
                                 |switch(sk->state) 

     *** 패킷은 해당하는 소켓을 사용하는 태스크에게 보내진다 ***
                                 |case TCP_ESTABLISHED:
                                    |tcp_rcv_established
                                       |__skb_queue_tail // 패킷을 소켓의 큐에 넣음
                                       |sk->data_ready -> sock_def_readable 
                                          |wake_up_interruptible
                                

     *** 패킷은 3-way TCP handshake 과정에 의해 연결되어야 한다 ***
                                 |case TCP_LISTEN:
                                    |tcp_v4_hnd_req
                                       |tcp_v4_search_req
                                       |tcp_check_req
                                          |syn_recv_sock -> tcp_v4_syn_recv_sock
                                       |__tcp_v4_lookup_established
                                 |tcp_rcv_state_process

                    *** 3-Way TCP Handshake ***
                                    |switch(sk->state)
                                    |case TCP_LISTEN: // SYN 패킷을 받음
                                       |conn_request -> tcp_v4_conn_request
                                          |tcp_v4_send_synack // SYN + ACK 패킷을 보냄
                                             |tcp_v4_synq_add // set SYN state
                                    |case TCP_SYN_SENT: // SYN + ACK 패킷을 받음
                                       |tcp_rcv_synsent_state_process
                                          tcp_set_state(TCP_ESTABLISHED)
                                             |tcp_send_ack
                                                |tcp_transmit_skb
                                                   |queue_xmit -> ip_queue_xmit
                                                      |ip_queue_xmit2
                                                         |skb->dst->output
                                    |case TCP_SYN_RECV: // ACK 패킷을 받음
                                       |if (ACK)
                                          |tcp_set_state(TCP_ESTABLISHED)
	 </screen>
	</para>

	<para>
	 각 함수들은 아래와 같이 찾아볼 수 있다:
	</para>

	<para>
	 <itemizedlist>
	  
	  <listitem>
	   <para> net_rx_action [net/core/dev.c] </para>
	  </listitem>

	  <listitem>
	   <para> __skb_dequeue [include/linux/skbuff.h] </para>
	  </listitem>

	  <listitem>
	   <para> ip_rcv [net/ipv4/ip_input.c] </para>
	  </listitem>

	  <listitem>
	   <para> NF_HOOK -> nf_hook_slow [net/core/netfilter.c] </para>
	  </listitem>

	  <listitem>
	   <para> ip_rcv_finish [net/ipv4/ip_input.c] </para>
	  </listitem>

	  <listitem>
	   <para> ip_route_input [net/ipv4/route.c] </para>
	  </listitem>

	  <listitem>
	   <para> ip_local_deliver [net/ipv4/ip_input.c] </para>
	  </listitem>

	  <listitem>
	   <para> ip_defrag [net/ipv4/ip_fragment.c] </para>
	  </listitem>

	  <listitem>
	   <para> ip_local_deliver_finish [net/ipv4/ip_input.c] </para>
	  </listitem>

	  <listitem>
	   <para> tcp_v4_rcv [net/ipv4/tcp_ipv4.c] </para>
	  </listitem>

	  <listitem>
	   <para> __tcp_v4_lookup </para>
	  </listitem>

	  <listitem>
	   <para> tcp_v4_do_rcv </para>
	  </listitem>

	  <listitem>
	   <para> tcp_rcv_established [net/ipv4/tcp_input.c] </para>
	  </listitem>

	  <listitem>
	   <para> __skb_queue_tail [include/linux/skbuff.h] </para>
	  </listitem>

	  <listitem>
	   <para> sock_def_readable [net/core/sock.c] </para>
	  </listitem>

	  <listitem>
	   <para> wake_up_interruptible [include/linux/sched.h] </para>
	  </listitem>

	  <listitem>
	   <para> tcp_v4_hnd_req [net/ipv4/tcp_ipv4.c] </para>
	  </listitem>

	  <listitem>
	   <para> tcp_v4_search_req </para>
	  </listitem>

	  <listitem>
	   <para> tcp_check_req </para>
	  </listitem>

	  <listitem>
	   <para> tcp_v4_syn_recv_sock </para>
	  </listitem>

	  <listitem>
	   <para> __tcp_v4_lookup_established </para>
	  </listitem>

	  <listitem>
	   <para> tcp_rcv_state_process [net/ipv4/tcp_input.c] </para>
	  </listitem>

	  <listitem>
	   <para> tcp_v4_conn_request [net/ipv4/tcp_ipv4.c] </para>
	  </listitem>

	  <listitem>
	   <para> tcp_v4_send_synack </para>
	  </listitem>

	  <listitem>
	   <para> tcp_v4_synq_add </para>
	  </listitem>

	  <listitem>
	   <para> tcp_rcv_synsent_state_process [net/ipv4/tcp_input.c] </para>
	  </listitem>

	  <listitem>
	   <para> tcp_set_state [include/net/tcp.h] </para>
	  </listitem>

	  <listitem>
	   <para> tcp_send_ack [net/ipv4/tcp_output.c] </para>
	  </listitem>

	 </itemizedlist>
	</para>

	<para>
	 설명:
	</para>

	<para>
	 <itemizedlist>

	  <listitem>
	   <para> 우선 프로토콜의 타입을 결정한다. (IP 그리고 TCP) </para>
	  </listitem>

	  <listitem>
	   <para> <function>NF_HOOK(function)</function> 은 포장 함수로서 먼저 네트워크 필터 (예를 들면 방화벽) 를 처리하고 그 다음으로 <function>function</function> 함수를 호출한다. </para>
	  </listitem>

	  <listitem>
	   <para> 그리고 아래와 같은 TCP 의 3-way handshake 를 수행하고: </para>

	   <para>
	    <screen>
SERVER (LISTENING)                       CLIENT (CONNECTING)
                           SYN 
                   &lt;-------------------
 
 
                        SYN + ACK
                   ------------------->

 
                           ACK 
                   &lt;-------------------

                    3-Way TCP handshake
		</screen>
	   </para>
	  </listitem>

	  <listitem>
	   <para> 마지막으로 패킷을 사용자 소켓에 전달하고 깨워주는 역할을 하는 <function>tcp_rcv_established [net/ipv4/tcp_input.c]</function> 함수를 호출한다. </para>
	  </listitem>

	 </itemizedlist>
	</para>

   </simplesect>

  </sect2>

 </sect1>


 <sect1 id="linux_file_system">
  <title>리눅스 파일 시스템</title>

  <para>
   TODO
  </para>
 </sect1>


 <sect1 id="useful_tips">
  <title>유용한 팁</title>

  <sect2 id="stack_and_heap">
   <title>스택과 힙</title>

   <simplesect id="stack_and_heap_overview">
    <title>개요</title>

	<para>
	 이번 절에서는 메모리 상에 <emphasis>스택</emphasis> 과 <emphasis>힙</emphasis> 이 어떻게 할당되는지를 살펴보겠다.
	</para>

   </simplesect>

   <simplesect id="stack_and_heap_memory_allocation">
    <title>메모리 할당</title>

	<para>
	 <screen>
FF..        |                 | &lt;-- bottom of the stack
       /|\  |                 |   | 
 higher |   |                 |   |   stack
 values |   |                 |  \|/  growing
            |                 |
XX..        |                 | &lt;-- top of the stack [Stack Pointer]
            |                 |
            |                 |
            |                 |
00..        |_________________| &lt;-- end of stack [Stack Segment]
                 
                   Stack
	 </screen>
	</para>

	<para>
	 메모리 주소값은 00.. (스택 세그먼트가 시작되는 곳이기도 하다) 에서 부터 시작하여 FF.. 값을 가지는 방향으로 증가한다. 
	</para>

	<para>
	 XX.. 는 스택 포인터가 실제로 위치하고 있는 곳의 값이다.
	</para>

	<para>
	 스택은 함수에서 다음과 같은 정보를 저장하기 위해 사용한다.
	</para>

	<para>
	 <orderedlist>
	  
	  <listitem>
	   <para> 전역 변수 (?) </para>
	  </listitem>

	  <listitem>
	   <para> 지역 변수 </para>
	  </listitem>

	  <listitem>
	   <para> 리턴될 주소값 </para>
	  </listitem>

	 </orderedlist>
	</para>

	<para>
	 예를 들어, 다음과 같은 일반적인 함수를 하나 생각해 보면:
	</para>

	<para>
	 <screen>
 |int foo_function (parameter_1, parameter_2, ..., parameter_n) {
    |variable_1 declaration;
    |variable_2 declaration;
      ..
    |variable_n declaration;
   
    |// 함수 본체 
    |dynamic variable_1 declaration;
    |dynamic variable_2 declaration;
     ..
    |dynamic variable_n declaration;
   
    |// 코드 부분은 코드 세그먼트에 포함되므로, 데이타/스택 세그먼트에는 포함되지 않음
    
    |return (ret-type) value; // 리턴값은 특정 레지스터에 저장되기도 한다. i386 의 경우에는 eax 레지스터를 사용한다.
 |}
	 </screen>
	</para>

	<para>
	 위의 함수에 대한 메모리 할당은 아래와 같이 이루어 질 것이다.
	</para>

	<para>
	 <screen>
          |                       |
          | 1. parameter_1 pushed | \
    S     | 2. parameter_2 pushed |  | 함수 호출 전
    T     | ...................   |  | 
    A     | n. parameter_n pushed | /
    C     | ** Return address **  | -- 함수 호출
    K     | 1. local variable_1   | \ 
          | 2. local variable_2   |  | 함수 호출 후
          | .................     |  | 
          | n. local variable_n   | /
          |                       | 
         ...                     ...   스택의 여유 공간
         ...                     ...   
          |                       |
    H     | n. dynamic variable_n | \
    E     | ...................   |  | malloc 이나 kmalloc
    A     | 2. dynamic variable_2 |  | 에 의해 할당된 공간
    P     | 1. dynamic variable_1 | /
          |_______________________|
        
            Typical stack usage
	 </screen>
	</para>

	<para>
	 <note>
	  <para> 변수가 스택에 저장되는 순서는 하드웨어의 구조에 따라 달라질 수 있다. </para>
	 </note>
	</para>

   </simplesect>

  </sect2>

  <sect2 id="application_vs_process">
   <title>응용 프로그램 (Application) 과 프로세스</title>

   <simplesect id="application_vs_process_base_definition">
    <title>기본적인 정의</title>

	<para>
	 다음 2 가지의 개념을 구분해야 한다.
	</para>

	<para>
	 <itemizedlist>
	  
	  <listitem>
	   <para> 응용 프로그램: 우리가 실행하기를 원하는 유용한 코드 </para>
	  </listitem>

	  <listitem>
	   <para> 프로세스: 메모리 상에 있는 응용 프로그램의 <emphasis>이미지</emphasis> (이것은 사용된 메모리 관리 전략, 세그먼테이션 혹은 페이지네이션, 에 의존한다). </para>
	  </listitem>

	 </itemizedlist>
	</para>

	<para>
	 프로세스는 종종 태스크 혹은 쓰레드 라고 불리우기도 한다.
	</para>

   </simplesect>

  </sect2>

  <sect2 id="locks">
   <title>락 (locks)</title>

   <simplesect id="locks_overview">
    <title>개요</title>

	<para>
	 크게 다음과 같은 2 종류의 락이 있다:
	</para>

	<para>
	 <orderedlist>
	  
	  <listitem>
	   <para> CPU 내부적인 락 (intraCPU) </para>
	  </listitem>

	  <listitem>
	   <para> CPU 간의 락 (interCPU) </para>
	  </listitem>

	 </orderedlist>
	</para>

   </simplesect>
  </sect2>

  <sect2 id="copy_on_write">
   <title>Copy on Write</title>

   <para>
    <emphasis>Copy on Write</emphasis> 는 메모리의 사용을 줄이기 위한 메카니즘이다. 이는 메모리의 할당을 메모리가 실제로 필요한 시점까지 미루는 것이다.
   </para>

   <para>
    예를 들면, 태스크가 (새로운 태스크를 생성하기 위해) <function>fork()</function> 시스템 콜을 호출하는 경우, 자식 태스크는 읽기 전용 모드 (RO Access) 로 부모 태스크의 메모리 페이지를 공유한다. 이후에 이 페이지에 대한 쓰기 연산이 수행되는 경우에 예외<footnote><para> 역자주: 페이지 폴트 (Page Fault) 예외에 해당한다. </para></footnote>가 발생되고 페이지는 복사되고 읽기쓰기 모드 (RW Access) 로 설정된다.
   </para>

   <para>
    <orderedlist>
	 
	 <listitem>
	  <para> 페이지 X 는 부모 태스크와 자식 태스크 간에 공유된다.
	   <screen>
 Task Parent
 |         | RO Access  ______
 |         |---------->|Page X|    
 |_________|           |______|
                          /|\
                           |
 Task Child                | 
 |         | RO Access     |  
 |         |----------------                
 |_________| 
	   </screen>
	  </para>
	 </listitem>

	 <listitem>
	  <para> 쓰기 요청이 일어나는 경우
	   <screen>
 Task Parent
 |         | RO Access  ______
 |         |---------->|Page X|    Trying to write
 |_________|           |______|
                          /|\
                           |
 Task Child                | 
 |         | RO Access     |  
 |         |----------------                
 |_________| 
	   </screen>
	  </para>
	 </listitem>

	 <listitem>
	  <para> 마지막 설정: 각각의 태스크는 독립된 페이지 X 와 Y 를 가진다.
	   <screen>
 Task Parent
 |         | RW Access  ______
 |         |---------->|Page X|    
 |_________|           |______|
              
              
 Task Child
 |         | RW Access  ______
 |         |---------->|Page Y|    
 |_________|           |______|
	   </screen>
	  </para>
	 </listitem>

	</orderedlist>
   </para>

  </sect2>

 </sect1>


 <sect1 id="80386_specific_details">
  <title>80386 CPU 에 대한 부분들</title>

  <sect2 id="boot_procedure">
   <title>부팅 과정</title>

   <para>
    <screen>
bbootsect.s [arch/i386/boot]
setup.S (+video.S) 
head.S (+misc.c) [arch/i386/boot/compressed]
start_kernel [init/main.c]
	</screen>
   </para>

  </sect2>

  <sect2 id="80386_and_more_descriptors">
   <title>80386 (그 이후 버전 포함) 의 디스크립터(Descriptors)</title>

   <simplesect id="80386_and_more_descriptors_overview">
    <title>개요</title>

	<para>
	 디스크립터는 인텔 i386+ 마이크로프로세서의 가상 메모리에서 사용되는 자료 구조이다.
	</para>

   </simplesect>

   <simplesect id="80386_and_more_descriptors_kind_of_descriptors">
    <title>디스크립터의 종류</title>

	<para>
	 <itemizedlist>

	  <listitem>
	   <para> GDT (Global Descriptor Table) </para>
	  </listitem>

	  <listitem>
	   <para> LDT (Local Descriptor Table) </para>
	  </listitem>

	  <listitem>
	   <para> IDT (Interrupt Descriptor Table) </para>
	  </listitem>

	 </itemizedlist>
	</para>

   </simplesect>

  </sect2>

 </sect1>


 <sect1 id="IRQ">
  <title>IRQ</title>

  <sect2 id="IRQ_overview">
   <title>개요</title>

   <para>
    IRQ 는 요청한 작업이 완료되었음을 알려주기 위해 마이크로프로세서에게 보내지는 비동기적인 시그널이다.
   </para>

  </sect2>

  <sect2 id="interaction_schema">
   <title>상호작용 도식 (Interaction Schema)</title>

   <para>
    <screen>
                                 |&lt;-->  IRQ(0) [Timer]
                                 |&lt;-->  IRQ(1) [Device 1]
                                 | ..
                                 |&lt;-->  IRQ(n) [Device n]
    _____________________________| 
     /|\      /|\          /|\
      |        |            |
     \|/      \|/          \|/
 
    Task(1)  Task(2) ..   Task(N)
              
             
             IRQ - Tasks Interaction Schema
	</screen>
   </para>

   <simplesect id="interaction_schema_what_happens">
    <title>무슨 일이 일어나는가?</title>

	<para>
	 일반적인 운영체제는 프로세스의 실행을 중지 (interrupt) 시키고 시스템을 관리하는 작업들을 수행하기 위해 많은 IRQ 시그널을 사용한다.
	</para>

	<para>
	 <orderedlist>

	  <listitem>
	   <para> IRQ (i) 가 발생하고 Task (j) 는 인터럽트된다. </para>
	  </listitem>

	  <listitem>
	   <para> IRQ(i)_handler 가 실행된다. </para>
	  </listitem>

	  <listitem>
	   <para> 제어는 Task (j) 의 인터럽트된 부분으로 돌아온다. </para>
	  </listitem>

	 </orderedlist>
	</para>

	<para>
	 리눅스에서는 IRQ 가 발생하였을 때, 먼저 IRQ 포장 함수 (<function>interrupt0x??</function> 과 같은 이름일 것이다) 가 실행되고 다음으로 <emphasis>공식적인</emphasis> IRQ(i)_handler 가 실행될 것이다. 이것은 타임 슬라이스 선점과 같은 몇몇가지 작업들을 가능하게 해준다.
	</para>

   </simplesect>

  </sect2>

 </sect1>


 <sect1 id="utility_functions">
  <title>유틸리티 함수들</title>

  <sect2 id="list_entry">
   <title>list_entry [include/linux/list.h]</title>

   <para> 정의: </para>

   <para>
    <programlisting>
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&amp;((type *)0)->member)))
	</programlisting>
   </para>

   <para> 의미: </para>

   <para>
    <function>list_entry</function> 매크로는 구조체 내부의 멤버에 대한 포인터를 통해서 그 구조체 자체의 포인터를 얻어내기 위해 사용된다.
   </para>

   <para>
    <example>
	 <para>
	  <programlisting>
struct __wait_queue {
   unsigned int flags; 
   struct task_struct * task; 
   struct list_head task_list;
};
struct list_head { 
   struct list_head *next, *prev; 
};

// wait queue 에 대한 타입을 정의하고
typedef struct __wait_queue wait_queue_t;

// 다음과 같이 사용한다.
wait_queue_t *out = list_entry(tmp, wait_queue_t, task_list);

// 이 때 tmp 는 list_head 를 가리키는 포인터여야 한다.
	  </programlisting>
	 </para>
	</example>
   </para>

   <para>
    즉, 이 경우에는 <function>*tmp [list_head]</function> 포인터를 통해 <function>*out [wait_queue_t]</function> 포인터를 얻어냈다.
   </para>

   <para>
    <screen>
 ____________ &lt;---- *out [we calculate that]
|flags       |             /|\
|task *-->   |              |
|task_list   |&lt;----    list_entry
|  prev * -->|    |         |
|  next * -->|    |         |
|____________|    ----- *tmp [we have this]
	</screen>
   </para>

  </sect2>

  <sect2 id="sleep">
   <title>Sleep</title>

   <simplesect id="sleep_sleep_code">
    <title>Sleep Code</title>

	<para> 파일: </para>

	<para>
	 <itemizedlist>
	  
	  <listitem>
	   <para> kernel/sched.c </para>
	  </listitem>

	  <listitem>
	   <para> include/linux/sched.h </para>
	  </listitem>

	  <listitem>
	   <para> include/linux/wait.h </para>
	  </listitem>

	  <listitem>
	   <para> include/linux/list.h </para>
	  </listitem>

	 </itemizedlist>
	</para>

	<para> 함수: </para>

	<para>
	 <itemizedlist>
	  
	  <listitem>
	   <para> interruptible_sleep_on </para>
	  </listitem>

	  <listitem>
	   <para> interruptible_sleep_on_timeout </para>
	  </listitem>

	  <listitem>
	   <para> sleep_on </para>
	  </listitem>

	  <listitem>
	   <para> sleep_on_timeout </para>
	  </listitem>

	 </itemizedlist>
	</para>

	<para> 불려지는 함수: </para>

	<para>
	 <itemizedlist>
	  
	  <listitem>
	   <para> init_waitqueue_entry </para>
	  </listitem>

	  <listitem>
	   <para> __add_wait_queue </para>
	  </listitem>

	  <listitem>
	   <para> list_add </para>
	  </listitem>

	  <listitem>
	   <para> __list_add </para>
	  </listitem>

	  <listitem>
	   <para> __remove_wait_queue </para>
	  </listitem>

	 </itemizedlist>
	</para>

	<para> 함수간 호출 분석: </para>

	<para>
	 <screen>
|sleep_on
   |init_waitqueue_entry  --
   |__ add_wait_queue        |   요청한 작업을 큐에 집어 넣음
      |list_add              |
         |__list_add        -- 
   |schedule              ---    요청한 작업이 수행될 수 있을때 까지 기다림 
      |__remove_wait_queue --   
      |list_del              |   큐에서 요청을 작업을 꺼내옴
         |__list_del        -- 
	 </screen>
	</para>

	<para> 설명: </para>

	<para>
	 리눅스에서 각각의 자원들은 (이상적으로 객체는 다수의 사용자와 프로세스 사이에서 공유된다) 자신을 요청한 <emphasis>모든</emphasis> 태스크를 관리하기 위해서 큐를 가진다.
	</para>

	<para>
	 이러한 큐를 <emphasis>wait queue</emphasis> 라고 하며, 이것은 많은 멤버들을 포함하고 있다 (앞으로 이것은 <emphasis>wait queue element</emphasis> 라고 부르겠다):
	</para>

	<para>
	 <programlisting>
***   wait queue structure [include/linux/wait.h]  ***


struct __wait_queue {
   unsigned int flags; 
   struct task_struct * task; 
   struct list_head task_list;
}
struct list_head { 
   struct list_head *next, *prev; 
};
	 </programlisting>
	</para>

	<para> 그림으로 나타내면: </para>

	<para>
	 <screen>
        ***  wait queue element  ***

                             /|\
                              |
       &lt;--[prev *, flags, task *, next *]-->
 
                     


                 ***  wait queue list ***  
 
          /|\           /|\           /|\                /|\
           |             |             |                  |
--> &lt;--[task1]--> &lt;--[task2]--> &lt;--[task3]--> .... &lt;--[taskN]--> &lt;--
|                                                                  |
|__________________________________________________________________|
          

           
              ***   wait queue head ***

       task1 &lt;--[prev *, lock, next *]--> taskN
	 </screen>
	</para>

	<para>
	 <varname>wait queue head</varname> 이라는 포인터는 <emphasis>wait queue list</emphasis> 의 첫번째 원소 (<varname>next *</varname>) 와 마지막 원소 (<varname>prev *</varname>) 를 가리킨다.
	</para>

	<para>
	 새로운 원소가 하나 삽입되어야 할 때는 <function>__add_wait_queue [include/linix/wait.h}</function> 가 호출된다. 이것은 일반적인 함수인 <function>list_add [include/linux/list.h]</function> 를 실행시킬 것이다:
	</para>

	<para>
	 <programlisting>
***   function list_add [include/linux/list.h]  ***

// classic double link list insert
static __inline__ void __list_add (struct list_head * new,  \
                                   struct list_head * prev, \
                                   struct list_head * next) { 
   next->prev = new; 
   new->next = next; 
   new->prev = prev; 
   prev->next = new; 
}
	 </programlisting>
	</para>

	<para>
	 마지막으로 살펴볼 함수는 <function>__list_del [include/linux/list.h]</function> 이다. 이 함수는 <function>remove_wait_queue [include/linux/wait.h]</function> 함수 내부에서 <function>list_del [include/linux/list.h]</function> 함수에 의해 호출된다:
	</para>

	<para>
	 <programlisting>
***   function list_del [include/linux/list.h]  ***


// classic double link list delete
static __inline__ void __list_del (struct list_head * prev, struct list_head * next) { 
   next->prev = prev; 
   prev->next = next; 
}
	 </programlisting>
	</para>

   </simplesect>

   <simplesect id="stack_consideration">
    <title>스택의 고려</title>

	<para>
	 일반적인 리스트 (혹은 큐) 는 보통 힙 영역에 할당하여 관리한다. (스택과 힙에 관한 내용은 <xref linkend="stack_and_heap">&nbsp;</xref> 을 참고하기 바란다) 하지만 여기서는 Wait Queue 데이타를 지역 변수로 (스택) 할당하였기 때문에 함수가 스케줄링되어 리턴되면 지역 변수를 지우게 될 것이다..???
	</para>

	<para>
	 <screen>
  new task &lt;----|          task1 &lt;------|          task2 &lt;------|
                |                       |                       |
                |                       |                       | 
|..........|    |       |..........|    |       |..........|    | 
|wait.flags|    |       |wait.flags|    |       |wait.flags|    |
|wait.task_|____|       |wait.task_|____|       |wait.task_|____|   
|wait.prev |-->         |wait.prev |-->         |wait.prev |-->
|wait.next |-->         |wait.next |-->         |wait.next |-->   
|..        |            |..        |            |..        |    
|schedule()|            |schedule()|            |schedule()|     
|..........|            |..........|            |..........|    
|__________|            |__________|            |__________|     
 
   Stack                   Stack                   Stack
	 </screen>
	</para>

   </simplesect>

  </sect2>

 </sect1>


 <sect1 id="static_variables">
  <title>정적 변수들</title>

  <sect2 id="static_variables_overview">
   <title>개요</title>

   <para>
    리눅스는 C 언어로 짜여졌으며, 다른 응용 프로그램들과 같이 다음과 같은 것들을 포함한다:
   </para>

   <para>
    <orderedlist>
	 
	 <listitem>
	  <para> 지역 변수 </para>
	 </listitem>

	 <listitem>
	  <para> 모듈 변수 (소스 파일에 존재하며, 오직 그 모듈 내에서만 사용되는 변수) </para>
	 </listitem>

	 <listitem>
	  <para> 오직 1 개 만이 존재하는 전역/정적 변수 (모든 모듈에서 접근 가능한 변수) </para>
	 </listitem>

	</orderedlist>
   </para>

   <para>
    한 모듈에서 정적 변수 값을 수정하면, 다른 모듈에서도 변화된 값이 사용될 것이다.
   </para>

   <para>
    리눅스에서 정적 변수는 매우 중요한 역할을 한다. 이를 이용해서 커널에 새로운 기능을 추가할 수 있도록 해 주기 때문이다: 이들 정적 변수는 주로 등록된 원소들의 리스트의 맨 처음을 가리키는 포인터일 것이며, 다음과 같은 일들이 가능하다: 
   </para>

   <para>
    <itemizedlist>
	 
	 <listitem>
	  <para> 추가 </para>
	 </listitem>

	 <listitem>
	  <para> 삭제 </para>
	 </listitem>

	 <listitem>
	  <para> 수정 </para>
	 </listitem>

	</itemizedlist>
   </para>

   <para>
    <screen>
                           _______      _______      _______
Global variable  -------> |Item(1)| -> |Item(2)| -> |Item(3)|  ..
                          |_______|    |_______|    |_______|
	</screen>
   </para>

  </sect2>

  <sect2 id="main_variables">
   <title>주요 변수들</title>

   <simplesect id="main_variables_current">
    <title>current</title>

	<para>
	 <screen>
                           ________________
Current ----------------> | Actual process |
                          |________________|
	 </screen>
	</para>

	<para>
	 <varname>current</varname> 는 <varname>task_struct</varname> 구조체를 가리킨다. 이 구조체는 다음과 같이 프로세스에 대한 모든 정보를 포함하고 있다:
	</para>

	<para>
	 <itemizedlist>

	  <listitem>
	   <para> pid, 이름, 상태, 타임 슬라이스, 스케줄링 정책 </para>
	  </listitem>

	  <listitem>
	   <para> 다른 자료 구조에 대한 포인터들: files, vfs, signal, 다른 프로세스, .. </para>
	  </listitem>

	 </itemizedlist>
	</para>

 	<para>
	 사실 <varname>current</varname> 은 실제 변수가 아니라 다음과 같은 함수의 형태이다.
	</para>

	<para>
	 <programlisting>
static inline struct task_struct * get_current(void) { 
   struct task_struct *current; 
   __asm__("andl %%esp,%0; ":"=r" (current) : "0" (~8191UL)); 
   return current; 
}
#define current get_current()
	 </programlisting>
	</para>

	<para>
	 위에서 보듯이 <varname>esp</varname> 레지스터 (스택 포인터) 의 값을 이용하여 현재의 <varname>task_struct</varname> 구조체의 포인터를 얻어오게 된다. <footnote><para> 역자주: 각 프로세스는 자신의 <varname>task_struct</varname> 구조체와 스택 공간을 8192 바이트 단위로 할당받는다. 할당받은 공간의 가장 아래쪽에 <varname>task_struct</varname> 구조체가 위치하고, 가장 위쪽부터 스택으로 이용하므로 현재 스택 포인터의 하위 13 비트를 지우면 8192 크기 블럭의 가장 하위 주소를 얻을 수 있고, 이는 <varname>task_struct</varname> 구조체의 주소가 된다. </para></footnote>
	</para>

	<para>
	 <varname>current</varname> 변수를 이용하여 다른 프로세스들 (다른 상태에 있는 프로세스들을 포함하여) 의 자료 구조에 접근할 수 있다. 예를 들어, (I/O 드라이버에서 하듯이) 프로세스의 상태를 변화시키거나, pid, 준비 리스트 혹은 블록 리스트를 수정하는 일 등이 가능하다.
	</para>

   </simplesect>

   <simplesect id="main_variables_registered_filesystems">
    <title>등록된 파일 시스템</title>

	<para>
	 <screen>
                       ______      _______      ______
file_systems  ------> | ext2 | -> | msdos | -> | ntfs |
 [fs/super.c]         |______|    |_______|    |______|
	 </screen>
	</para>

	<para>
	 만약 쉘에서 <command>modprobe  some_fs</command> 라는 명령을 입력한다면 파일 시스템의 리스트에 새로운 엔트리가 추가될 것이다. 반대로 (<command>rmmod</command> 명령을 이용해서) 파일 시스템의 리스트에서 지울 수 있다.
	</para>

   </simplesect>

   <simplesect id="main_variables_mounted_filesystems">
    <title>마운트된 파일 시스템</title>

	<para>
	 <screen>
                        ______      _______      ______
mount_hash_table  ---->|   /  | -> | /usr  | -> | /var |
[fs/namespace.c]       |______|    |_______|    |______|
	 </screen>
	</para>

	<para>
	 <command>mount</command> 명령을 이용해서 파일 시스템을 추가하는 경우, 이 리스트에 새로운 원소가 추가된다. 반대로 <command>unmount</command> 명령은 리스트에서 원소를 지울 것이다.
	</para>

   </simplesect>

   <simplesect id="main_variables_reistered_network_packet_type">
    <title>등록된 네트워크 패킷 타입</title>

	<para>
	 <screen>
                        ______      _______      ______ 
     ptype_all  ------>|  ip  | -> |  x25  | -> | ipv6 |
[net/core/dev.c]       |______|    |_______|    |______|
	 </screen>
	</para>

	<para>
	 예를 들어, IPv6 에 대한 지원을 추가한다면 (관련된 모듈을 로딩) 이 리스트에 새로운 원소가 추가될 것이다.
	</para>

   </simplesect>

   <simplesect id="main_variables_registered_network_internet_protocol">
    <title>등록된 네트워크 인터넷 프로토콜</title>

	<para>
	 <screen>
                          ______      _______      _______ 
inet_protocol_base ----->| icmp | -> |  tcp  | -> |  udp  |
[net/ipv4/protocol.c]    |______|    |_______|    |_______|
	 </screen>
	</para>

	<para>
	 또한 (IPv6 와 같은) 다른 패킷 타입들에서 대해서도 각각의 리스트에 많은 인터넷 프로토콜 정보를 가지고 있다.
	</para>

	<para>
	 <screen>
                          ______      _______      _______ 
inet6_protos ----------->|icmpv6| -> | tcpv6 | -> | udpv6 |
[net/ipv6/protocol.c]    |______|    |_______|    |_______|
	 </screen>
	</para>

   </simplesect>

   <simplesect id="main_variables_registered_network_device">
    <title>등록된 네트워크 장치</title>

	<para>
	 <screen>
                          ______      _______      _______ 
dev_base --------------->|  lo  | -> |  eth0 | -> |  ppp0 |
[drivers/core/Space.c]   |______|    |_______|    |_______|
	 </screen>
	</para>

   </simplesect>

   <simplesect id="main_variables_registered_char_device">
    <title>등록된 문자 장치</title>

	<para>
	 <screen>
                          ______      _______      ________ 
chrdevs ---------------->|  lp  | -> | keyb  | -> | serial |
[fs/devices.c]           |______|    |_______|    |________|
	 </screen>
	</para>

	<para>
	 <varname>chrdevs</varname> 은 실제로 리스트에 대한 포인터가 아니라, 표준적인 벡터 (배열?) 에 대한 포인터이다.
	</para>

   </simplesect>

   <simplesect id="main_variables_registered_block_device">
    <title>등록된 블럭 장치</title>

	<para>
	 <screen>
                          ______      ______      ________ 
bdev_hashtable --------->|  fd  | -> |  hd  | -> |  scsi  |
[fs/block_dev.c]         |______|    |______|    |________|
	 </screen>
	</para>

	<para>
	 <varname>bdev_hashtable</varname> 은 해쉬 벡터이다.
	</para>

   </simplesect>

  </sect2>

 </sect1>


 <sect1 id="glossary">
  <title>용어</title>
 </sect1>


 <sect1 id="links">
  <title>링크</title>

  <para>
   <ulink url="http://www.kernel.org">공식 리눅스 커널 싸이트</ulink>
  </para>

  <para>
   <ulink url="http://jungla.dit.upm.es/~jmseyas/linux/kernel/hackers-docs.html">리눅스 커널에 대한 유용한 문서</ulink>
  </para>

  <para>
   <ulink url="http://www.uwsg.indiana.edu/hypermail/linux/kernel/index.html">공식 커널 메일링 리스트</ulink>
  </para>

  <para>
   <ulink url="http://www.tldp.org/guides.html">리눅스 문서화 프로젝트 가이드</ulink>
  </para>

  <para>
   <ulink url="http://kldp.org">리눅스 한글문서 프로젝트</ulink>
  </para>

  <para>
   <ulink url="http://linuxkernel.net">이호님의 리눅스 커널 홈페이지</ulink>
  </para>

  <para>
   <ulink url="http://kelp.or.kr">한국 임베디드 리눅스 프로젝트</ulink>
  </para>

 </sect1>

</article>

<!-- vi:encoding=utf8: -->
}}}