리눅스 정리 a4참조

 

1. 리눅스의 장점

  • 사용자 임의대로 재구성이 가능
  • 커널의 크기가 작다
  • 완벽한 멀티유저, 멀티태스킹 시스템
  • 뛰어난 안정성
  • 빠른 업그레이드
  • 강력한 네트워크 지원

 

 

 

2. 운영체제가 관리해야 할 자원은 크게 물리적 자원(physical resource)과 추상적인 자원(abstract resource)으로 구분 가능

물리적 자원 : CPU 메모리 디스크 터미널 네트워크 등 시스템을 구성하고 있는 요소들과 주변 장치 등

추상적인 자원 : 물리적 자원을 운영체제가 관리하기 위해 추상화 시킨 객체들. CPU를 추상화 시킨 태스크(task), 메모리를 추상화시킨 세그먼트와 페이지, 디스크를 추상화시킨 파일, 네트워크를 추상화시킨 통신 프로토콜, 패킷 등

 

3. 리눅스 커널의 주요 디렉터리

  • kernel : 태스크 관리자가 구현된 디렉터리. 태스크의 생성과 소멸, 프로그램의 실행, 스케줄링, 시그널 처리 등의 기능이 이 디렉터리에 구현되어 있다. 한편 문맥 교환(context Switch)과 같은 하드웨어 종속적인 태스크 관리 부분은 arch/$(ARCH)/kernel 디렉터리에 구현 되어 있다.
  • fs : 리눅스에서 지원하는 다양한 파일시스템과 open read write 등의 시스템 호출이 구현된 디렉터리. 대표적인 파일 시스템으로는 ext2 ext3 ext4 nf ufs msdos vfat proc sysfs devfs isofs ntfs reiserfs 등이 있다. 이처럼 다양한 파일 시스템을 사용자가 일관된 인터페이스로 접근 할 수 있도록 하기 위해 리눅스가 도입한 가상파일 시스템(virtual file system)도 이 디렉터리에 존재
  • init : 커널 초기화 부분, 즉 커널의 메인 시작 함수가 구현된 디렉터리. 하드웨어 종속적인 초기화가 arch/$(ARCH)/kernel 디렉터리 하위에 있는 head.s와 mlCS.C에서 이뤄지고 나면, 이 디렉터리에 구현되어 있는 start_kernel()이라는 C함수로 제어가 넘어온다. 이 함수가 커널 전역적인 초기화룰 수행하게 된다.

 

 

 

4. 태스크

일반적인 운영체제 서적에서는 태스크를 '자원소유권의 단위'로 쓰렏를 '수행의 단위'로 정의하고 있으며, 프로세스를 '동작중인 프로그램' 으로 정의한다.

리눅스에서는 프로세스와 쓰레드를 모두 태스크로써 다룬다. 리눅스는 task_struct라는 자료구조로 프로세스와 쓰레드를 모두 표현.

 

5. 태스크는 사용자 요청을 대리하며 시스템 자원을 두고 다른 태스크와 경쟁한다.

각 태스크는 자신만의 메모리 영역을 가지며 텍스트, 데이터, 스택, 힙 이라는 네 영역(region)으로 구분

  • 텍스트 : CPU에서 직접 수행되는 명령어(Instructions)
  • 데이터 : 전역변수
  • 스택 : 지역변수와 인자 그리고 함수의 리턴 주소 등
  • 힙 : 동적할당 받은 내용이 존재

이때 각 영역을 세그먼트(segment) 또는 가상 메모리 객체(vm_area_struct)라고도 부름

 

6. 여러 태스크들은 서로 부모와 자식, 또는 형제 관계를 맺는다.

따라서 전체적인 태스크 관계도는 계층구조로 표현 가능. 그리고 프로세스의 몇 가지의 상태(ready, running, wati, exit 등)를 가지며 이 상태는 수시로 바뀐다.

태스크는 생성된 뒤, 자신에게 주어진 일을 수행하며 이를 위해 CPU 이외의 자원을 요청하기도 한다. 특정 태스크가 당장 제공해 줄 수 없는 자원을 요청한다면 커널은 이 태스크를 잠시 '대기' 하도록 만든 뒤 다른 태스크를 먼저 수행시키며, 태스크가 요청했던 자원이 사용가능해지면 다시 '수행' 시켜 줌으로써 보다 높은 시스템 활용률을 제공하려 한다. 따라서 태스크는 상태 전이(state transition) 라는 특징을 갖는다.

 

7. 태스크가 생성되면 그 태스크는 준비 상태(TASK_RUNNING)가 된다.

스케줄러는 여러 태스크 중에서 실행시킬 태스크를 선택하여 수행시킨다. 따라서 TASK_RUNNING 상태는 구체적으로 준비 (TASK_RUNNING(ready)) 상태와 실제 CPU를 배정받아 명령어 들을 처리하고 있는 실행(TASK_RUNNING(running)) 상태 두 가지로 나뉘게 된다. 즉, n개의 CPU를 갖는 갖는 시스템에서는 임의의 시점에 최대 n개의 태스크가 실제 실행상태에 있을 수 있다.

 

8. 태스크와 관련된 '모든' 정보를 문맥(context) 이라고 부른다. 태스크 문맥은 크게 세 부분으로 구분할 수 있다.

  • 시스템 문맥(System Context) : 태스크의 정보를 유지하기 위해 사용되며 대표적인 자료구조로는 tasg_struct, 파일 디스크립터, 파일 테이블, 세그먼트 테이블, 페이지 테이블 등이 있다.
  • 메모리 문맥(Memory Context) : 텍스트, 데이터, 스택, 힙(heap) 영역, 스왑 공간 등이 있다
  • 하드웨어 문맥 : 문맥교환(Context Switching)을 위해 현재 태스크의 CPU 레지스터 상태를 저장. 이런 특성 때문에 하드웨어 레지스터 문맥이라고도 불린다. 이후 해당 태스크가 다시 실행될 때 저장해 두었던 CPU 레지스터의 상태를 복원하여 실행이 멈추었던 곳에서부터 다시 실행을 이어서 진행

 

 

9. 하드웨어 컨택스트

수행 중이던 A라는 태스크에게 할당되어 있던 타임 슬라이스가 모두 소진되거나 혹은 수행 중이던 A라는 태스크가 특정 사건을 기다리기 위해 잠들어야 하는 경우 리눅스 커널은 새로이 수행할 태스크 B를 선택하여 CPU라는 자원을 제공해준다. 이렇게 수행 중이던 태스크의 동작을 멈추고 다른 태스크로 전환하는 과정을 문맥 교환(context switch)이라 부른다.

리눅스 커널은 태스크가 문맥교환되는 시점에 어디까지 수행했는지 현재 CPU의 레지스터 값은 얼마인지 등을 저장해 둔다. 이를 문맥 저장(context save)이라 한다. 즉, 스케줄링이 일어나면 문맥 교환이 발생하고, 문맥 교환 시엔 현재 수행 중이던 태스크의 문맥을 저장해 두어야 한다. 이때 문맥은 CPU 레지스터 즉, H/W context를 뜻 한다. H/W context는 task_struct에 담아두기 위한 필드가 존재 그 필드의 이름은 thread이다.

 

10. 사용자 수준 수행과 커널 수준 수행의 문제는 좀 더 복잡한 스택 관리를 요구 한다

사용자 수준에서 프로그램이 수행될 때에는 0~4GB 주소 공간 중 3GB아래부분에 존재하는 스택을 사용.

커널 수준에서 수행되는 프로그램은 바로 리눅스 그 자체. 리눅스 커널은 태스크가 생성될 때마다 태스크 별로 8KB의 스택을 할당해 준다. 태스크가 생성되면 리눅스는 task_struct 구조체와 8KB의 스택을 할당하게 된다.

태스크 당 할당되는 8KB 크기의 스택은 thread_union이라 불리며, thread_info(프로세스 디스크립터) 구조체도 포함. 

 

11. 리눅스에서는 프로세스 생성되든 쓰레드가 생성되든 task_struct라는 동일한 자료구조를 생성하여 관리한다.

 

12. 프로세스와 쓰레드를 동일하게 다루는 이러한 특성은 실제 함수들이 구현된 방식에서도 나타난다. 

fork clone pthread_create 는 커널에 구현되어 있는 sys_clone 시스템 호출을 사용하며, vfork는 sys_vfork를 사용. sys_clone과 sys_vfork는 모두 커널 내부 함수인 do_fork()를 호출.

fork는 프로세스 생성, clone은 쓰레드 생성, 커널 내부에서 마지막으로 호출되는 함수는 do_fork로 동일, 모두 '태스크'를 생성하기 때문.

 

13. 태스크가 생성되면 이 태스크를 위한 유일한 번호를 pid로 할당해 준다.

그런 뒤 만약 사용자가 프로세스를 원하는 경우라면 생성된 태스크의 tgid 값을 새로 할당된 pid 값과 동일하게 넣어준다. 따라서 tgid 값도 유일한 번호를 갖게 된다. 사용자가 쓰레드를 원하는 경우라면 부모 쓰레드의 tgid 값과 동일한 값으로 생성된 태스크의 tgid 설정한다. 결국 부모 태스크와 자식 태스크는 동일한 tgid를 갖게 되며 동일한 프로세스에 속해 있는 것을 해석된다.

 

clone()의 인자로 CLONE_CHILD CLONE_CHILD_SETID를 설정하면 리눅스 커널은 태스크를 생성 할 때 프로세스로 해석될 수 있도록 자원공유가 되지 않는 형태로 생성하며, clone()의 인자로 CLONE_THREAD를 설정하면 태스크를 생성할 때 쓰레드로 해석될 수 있도록 자원 공유가 되는 형태로 생성한다.

14. 실제 커널 내에서 getpid() 함수의 구현.

asmlinkage long sys_getpid(void)

{

  return current -> tid;

}

current라는 매크로는 커널 내부에 정의 되어 있는 매크로로써 현재 태스크의 task_struct 구조체를 가리킬 수 있게 해준다. 따라서 이 함수는 task_struct 구조체의 tgid 필드 값을 리턴 하는 단순한 함수라고 볼 수 있다.

 

15. 태스크는 생성되면서 가족관계를 갖는다.

대표적으로 현재 태스크를 생성한 부모 태스크의 task_struct구조체를 가리키는 real_parent와 현재 부모 태스크의 task_struct 구조체를 가리키는 parent 필드가 존재한다. 또한 자식과 형제를 리스트로 연결한 뒤 그 리스트의 헤드를 각각 children, sibling 필드에 저장해 놓았다.

 

16. 시그널은 태스크에게 비동기적인 사건의 발생을 알리는 매커니즘

태스크가 시그널을 원활히 처리하려면 3가지 기능을 지원해야 한다.

  1. 다른 태스크에게 시그널을 보낼 수 있어야 한다. 이를 위해 리눅스 커널은 sys_kill() 시스템 호출 제공
  2. 자신에게 시그널이 오면 그 시그널을 수신할 수 있어야 한다. 이를 위해 task_struct에는 signal, pending이라는 변수가 존재
  3. 자신에게 시그널이 오면 그 시그널을 처리할 수 있는 함수를 지정할 수 있어야 한다. 이를 위해 sys_signal() 시스템호출이 존재, task_struct내에 sighand라는 변수가 존재

 

17. 특정 태스크에게만 시그널을 보내야 할때.

공유하지 않는 시그널은 task_struct 구조체의 pending 필드에 저장. 시그널을 sigal 필드나 pending 필드에 저장할 때는 시그널 번호 등을 구조체로 정의하여 큐에 등록시키는 구조, 이를 위해 systkill() 시스템 호출 도입.

 

18. 스케줄링

선점 스케줄링 : 우선순위가 높은 프로세스에게 실행기회를 주며 빠른 응답시간을 요구하는 시분할 시스템에 유용. but 문맥교환횟수가 많고 오버헤드발생 가능성, 순위가 낮은 프로세스 기아현상(starvation) 초래

비선점 스케줄링 : 현재 실행 중인 프로세스를 선점하지 못하도록 한다. 문맥교환 횟수가 적으며 모든 프로세스들에게 CPU사용 기회를 공평하게 분배

 

19. 스케줄링 알고리즘 별로 분류

우선순위 스케줄링 : 프로세스에게 우선 순위를 부여.  우선순위 부여 방법에는 정적 우선순위방법, 동적 우선순위 방법이 있다.

FIFO 스케줄링 : 대기 큐에 도착한 프로세스 순서대로 CPU 할당. 짧은 작업시간의 프로세스가 오래 기다릴 위험 존재. 중요한 작업이 중요하지 않은 작업때문에 기다릴 위험 존재

다단계 피드백 큐 스케줄링 : 입출력 CPU 위주의 프로세스의 특성에 따라 서로 다른 CPU 사용시간 부여. 짧은 작업에 유리 입출력 위주의 작업에 우선권.

 

20. 프로세스 선점이란

한 프로세스가 CPU 사용도중 더 높은 우선순위의 프로세스에 의해 CPU의 사용권한을 자신의 의지에 상관없이 빼앗기는 일을 말한다.

보통 선점이 발생하는 경우는 프로세스에 주어진 타임 슬라이스를 모두 사용했거나 우선 순위가 더 높은 프로세스가 도착했을때 이다. 선점된 프로세스는 CPU를 빼앗겼지만 TASK_RUNNING 상태를 그대로 유지.

 

 21. 일반적으로 스케줄러는 수행 가능한 상태의 태스크를 큐 자료구조로 연결해 둔다.

리눅스에서는 이 자료구조를 런 큐(Runqueue)라 한다. 런 큐는 한 개 또는 여러 개로 구현될 수 있는데 리눅스에서는 각 CPU별로 하나씩의 런 큐를 사용한다.

다중 CPU 환경에서 런 큐가 여러 개라면 task_list에서 런 큐로 삽입될 때 새로 생성되는 태스크는 부모 태스크가 존재하던 런 큐로 삽입된다. 이는 자식 태스크가 부모 태스크와 같은 CPU에서 수행될 때 더 높은 캐시 친화력(cache affinity)을 얻을 수 있기 때문이다. 대기 상태에서 깨어난 태스크는 대기 전에 수행되던 CPU의 런 큐로 삽입. 이 또한 캐시 친화력을 활용하기 위함. 구체적으로 task_struct의 cpus_allowed필드에 자신이 수행되던 CPU의 번호가 들어있고, 이를 이용해 삽입될 런 큐를 결정.

 

22. 실시간 스케줄링 정책을 사용하는 태스크는 우선순위 설정을 위해 task_struct 구조체의 rt_priority 필드를 사용한다.

rt_priorty는 0~99까지의 우선순위를 가진다. 태스크가 수행을 종료, 스스로 중지, 혹은 자신의 타임 슬라이스를 다 쓸 때까지에 경우는 CHED_RR CPU를 사용한다. 또한 실시간 정책을 사용하는 태스크는 고정 우선순위를 가지게 된다. 따라서 항상 우선순위가 높은 태스크가 낮은 태스크보다 먼저 수행되는것을 보장.

 

23. 리눅스 커널 2.6.23 이후 이상적이고 정확한 멀티태스킹 CPU를 목표로 하는 CFS(Completely Fair Scheduler)라는 새로운 스케줄러가 도입.

CFS는 SCHED_NORMAL, _BATCH, _IDLE 세 가지 정책 지원. 

실시간스케줄링 모듈이 제공하는 SCHED_RR , _FIFO 정책사용이 가능

 

24. CFS를 위한 런큐는 struct cfs_rq라는 자료구조와 struct rt_rq라는 이름의 자료구조를 담고있다.

실시간 스케줄링 기법은 기존과 마찬가지로 비트맵을 이용한 우선순위 기반 스케줄링을 하고 있다.

비실시간 태스크는 struct cfs_rq 자료구조로 관리.

 

파일시스템

25. 메모리 관리 기법과 파일시스템은 모두 기억 장치를 관리한다.

RAM,하드디스크 모두 한정된 자원이기에 최대한 공간을 아껴서 사용해야함

메모리 관리 기법과 파일시스템은 내,외부 단편화를 최소화하기 위해 노력

또 한 두 가지 기법 모두 자신만의 할당,해제 정책이 존재.

메모리 관리 기법과 파일시스템의 차이는 이름이라는 특성.

 

26. 파일 시스템이 하드디스크에 저장하는 정보는 크게 메타 데이터와 사용자 데이터라 나뉜다.

메타 데이터 : 파일의 속성 정보나 데이터 블록 인덱스 정보 등이 해당

사용자 데이터: 사용자가 실제 기록하려 했던 내용

 

27. 디스크 블록의 할당과 회수 방법

디스크 블록을 할당하는 방법에는 연속(sequential) 할당과 불연속(nonsequential) 할당 두 가지 방법이 있다. 연속 할당이란 파일에게 연속된 디스크 블록을 할당하는 방법이다.

연속 할당은 불연속 할당에 비해 파일을 읽는 속도가 빠르다. 연속할당의 경우 파일의 크기가 변하면 문제가 될 수 있다.

불연속 할당 방법은 같은 파일에 속한 디스크 블록들을 연속적으로 저장하지 않는다. 불연속 할당 방법에서는 파일에 속한 디스크 블록들이 어디에 위치하고 있는지에 대한 정보를 기록해 두어야 한다. 이를 위한 방법으로는 블록체인 기법, 인덱스 블록 기법, FAT(File Alllcation Table) 기법 등이 있다.

 

FAT 기법 : 같은 파일에 속해 있는 블록들의 위치를 FAT라는 자료구조에 기록해 놓는 방법. 파일시스템이 관리하는 공간 내에 전역적으로 존재하는 FAT 구조를 사용하여 파일에 속해있는 데이터를 찾아가는 방법. 인덱스 블록 기법은 파일마다 인덱스 블록이 필요한데, FAT기법에서는 파일시스템 전체적으로 하나의 FAT이 존재하는 것이다.

 

28. 리눅스의 디폴트 파일시스템인 Ext2와 Ext3가 채택하고 있는 inode구조

i_blocks  필드는 이 파일이 몇 개의 데이터 블록을 가지고 있는지 나타내며, i_mode는 이 inode가 관리하는 파일의 속성 및 접근 제어 정보를 유지한다. i_links_count는 이 inode를 가리키고 있는 파일 수를(또는 링크 수를)의미.

i_mode는 16비트로 구성, 상위 4개의 비트는 파일의 유형을 의미. 파일의 유형으로는 정규파일(S_IFREG), 디렉터리(S_IFDIR), 문자 장치 파일(S_IFCHR), 블록 장치 파일(S_IFBLK), 링크파일(S_IFLNK), 파이프(S_IFFIFO), 그리고 소켓 (S_IFSOCK)등이 있다.

 

그 다음으로 위치한 3개의 비트는 특별한 목적으로 사용된다. u비트는 setuid(set user id) 비트로, 파일이 수행될 때 수행시킨 태스크의 소유자 권한이 아닌, 파일을 생성한 사용자의 권한으로 동작할 수 있도록 한다. 유사하게 사용되는 g비트는 setgid(set group id) 비트이다. 그리고 s비트는 sticky 비트로 태스크가 메모리에서 쫓겨 날 때, swap공간에 유지되도록 할 때, 또는 디렉터리에 대한 접근 제어에 사용된다.

 

29. VFS

상우 계층에서 open, read 등의 단일한 함수를 통해 파일시스템에 접근하려하면, 인자에 담겨있는 파일의 이름을 보고 파일을 관리하고 있는 파일시스템이 무엇인지를 판단한다. 그런 뒤 해당 파일시스템의 함수를 살펴보고 사용자가 원하는 일을 해줄 수 있는 파일시스템 고유의 함수를 호출한다. 파일시스템의 함수가 결과를 리턴하면, 이를 사용자 태스크에게 건네준다. 바로 이 구조가 리눅스가 채택한 VFS(Virtual File System)의 접근 방법이다.

 

30. 사용자 태스크는 VFS라는 가상적인 파일시스템만을 알고 있으면 충분.

따라서 사용자 태스크 입장에서 본다면 VFS는 가상적이긴 하지만 실제 접근을 하게 되는 '파일시스템' 인 것이다.

1. 슈퍼블록(super block) 객체 : 현재 사용 중인(마운트 된) 파일시스템 당 하나씩 주어진다. 실제로 각 파일시스템은 자신이 관리하고 있는 파티션에 파일시스템 마다 고유한 정보를 슈퍼 블록에 저장해 둔다. VFS는 이를 읽어서 관리하기 위해 범용적인 슈퍼 블록 객체를 정의 한다.

2. 아이노드 객체 : 특정 파일과 관련된 정보를 담기 위한 구조체. 각 파일시스템은 파일을 저장하기 위해 각자 정의한 메타데이터를 저장해 놓을 것이다. VFS가 아이노드 객체를 생성하고 파일시스템에 특정 파일에 대한 정보를 요청하면 파일시스템은 자신이 관리하고 있는 영역에서 파일의 메타데이터를 읽어서 아이노드 객체에 채워준다.

3. 파일객체 : 이 객체는 태스크가 open한 파일과 연관되어 있는 정보를 관리 한다.

4. 디엔트리 객체 : 태스크가 파일에 접근하려면 해당 파일의 아이노드 객체를 자신의 태스크와 연관된 객체인 파일 객체에 연결시켜야 한다. 이때 이 관계를 조금 더 빠르게 연결하기 위한 일종의 캐시 역할을 하는 것이 디엔트리 객체이다.

 

31. task_struct에는 files라는 이름의 변수가 있으며 이 변수는 file_sturct라는 자료구조를 가리킨다.

 

32. 가상 메모리는 실제 시스템에 존재하는 물리 메모리의 크기와 관계없이 가상적인 주소 공간을 사용자 태스크에게 제공한다.

 

33. 복수개의 CPU를 가지고 있는 컴퓨터 시스템 중 모든 CPU가 메모리와 입출력 버스 등을 공유하는 구조를 SMP(Symmetric Multiprocessing)라 부른다.

그런데 복수개의 CPU가 메모리 등의 자원을 공유하기 때문에 성능 상 병목 현상이 발생할 수 있다. 따라서 CPU들을 몇 개의 그룹으로 나누고 각각의 그룹에게 별도의 지역 메모리를 주는 구조가 생겨났는데 이러한 구조를 NUMA(Non-Uniform Memory Access)라 부르며, 이에 반해 기존 시스템을 UMA(Uniform Memory Access)라고 한다.

 

34. 버디 할당자를 이용하면 최대한 큰 연속된 공간을 유지하면서 효율적으로 메모리를 관리할 수 있다.

슬랩 할당자: 미리 할당 받아 분할하여 관리하고 있던 공간에서 메모리를 할당,해제 마치 일종의 캐시(cache)로 사용하는 것. 이러한 cache의 집합을 통해 메모리를 관리하는 정책.

 

35. 가상 메모리의 장/단점 정리

장점

  1. 물리 메모리의 크기와 관계없이 상당히 큰 주소 공간을 프로그래머에게 제공 할 수 있다. 물리메모리가 굳이 클 필요가 없다.
  2. 프로그램의 모든 페이지들을 물리 메모리에 적재할 필요 없이 페이지 폴트를 이용해 필요할 때 마다 페이지를 물리 메모리에 적재할 수 있어 (demand pagin) 메모리를 더욱 효율적으로 사용할 수 있다.
  3. 여러 태스크가 특정 영역을 공유하고 싶을 때 단지 페이지 테이블에서 같은 페이지 프레임을 가리키게 하는 것만으로(또는 페이지 테이블 자체를 공유하는 방법으로) 공유 메모리를 지원할 수 있다.

단점

  1. 물리 메모리를 접근하기 위해 주소변환 과정이 필요하다. 주소변환을 위해서는 페이지 디렉터리와 페이지 테이블을 탐색해야 하는데 이것이 프로그램의 수행 시간을 지연시킬 가능성이 있다.
  2. 메모리 접근시간에 대한 예측성을 떨어뜨린다.

 

35. 인터럽트란 주변 장치와 커널이 통신하는 방식 중의 하나로, 주변 장치나 CPU가 자신에게 발생한 사건을 리눅스 커널에게 알리는 매커니즘이다.

외부에서 네트워크를 통해 패킷이 도착했음을 알리기 위해 혹은 키보드가 눌렸음을 알리기 위해서 등 다양한 이유로 인터럽트가 발생.

인터럽그타 발생되면 운영체제는 왜 발생했는지를 살펴보고 적절한 처리를 해야한다. 이때 작업을 처리하는 함수를 인터럽트 핸들러 (interrupt handler)라고 부른다.

 

36. 시스템이 운영되는 도중 발생되는 인터럽트의 원인에 따라 크게 2가지로 구분할 수 있다.

1. 외부 인터럽트 : 현재 수행중인 태스크와 관련 없는 주변장치에서 발생된 비동기적인 하드웨어적인 사건을 의미

2. 현재 수행중인 태스크와 관련 있는 즉 동기적으로 발생하는 사건으로써 '트랩'이라고 부른다. 소프트웨어적 사건 예외처리(exception handling)라고도 한다.

 

37. 모든 CPU는 인터럽트가 발생하면 program counter(또는 instruction pointer) 레지스터의 값을 미리 정해진 특정 번지로 변경하도록 정해져 있다.

 

38. 리눅스는 외부 인터럽트와 트랩을 동일한 방식으로 처리한다.

구체적으로 '외부인터럽트'와 '트랩'을 처리하기 위한 루틴을 함수로 구현해 놓은 뒤 각 함수의 시작 주소를 리눅스의 IDT인 idt_table이라는 이름의 배열에 기록해둔다. 다양한 CPU에서도 커널 내부구조 수정 없이 인터럽트를 처리하기 위해 idt_table의 0~31까지 32개의 엔트리를 CPU의 '트랩' 핸들러를 위해 할당하고, 그 외의 엔트리는 '외부 인터럽트'의 핸들러를 위해 사용한다.

 

39. 커널이 인터럽트를 받으면 즉시 인터럽트 핸들러를 호출할 수 있는 것은 아니다.

그 전에 수행해야 할 일이 있는데 그것이 문맥 저장(Context save)이다. 또한 인터럽트 처리가 완료되면 저장되었던 문맥을 복원(Context resotre)해 주어야 한다.

 

40. 인터럽트 핸들러는 SAVE_ALL 매크로를 사용하여 인터럽트가 발생한 시점에 수행 중이던 태스크의 문맥을 저장하고 do_IRQ()함수를 호출한다.

do_IRQ() 함수에 의해 실제 인터럽트의 서비스가 수행되고, 서비스가 종료되고 나면 ret_from_intr를 호출하는데 이곳에서는 SAVE_ALL 매크로를 통해 저장했던 문맥을 RESTORE ALL 매크로를 통해 복원하는 등의 작업을 수행하게 된다.

 

41. 리눅스 커널은 트랩을 다시 세가지로 구분한다.

  1. fault : 리눅스 커널은 fault를 일으킨 명령어 주소를 eip에 넣어 두었다가 해당 핸들러가 종료되고 나면, eip에 저장되어 있는 주소부터 다시 수행을 시작한다. Page fault...
  2. trap : 리눅스 커널은 trap을 일으킨 명령어의 다음 주소를 eip에 넣어 두었다가 그 다음부터 다시 수행한다 System call...
  3. abort : 심각한 에러이므로 eip값을 저장할 필요가 없다. 현재 태스크를 강제 종료 시킨다. devide by zero...

+ Recent posts