수안이의 컴퓨터 연구실

  • Mainpage
  • About Me
  • Tags
  • Metapage
  • Notice
  • Location
  • Keywords
  • Guestbook
  • Admin
  • Write an Article
  • Total | 1612154
  • Today | 89
  • Yesterday | 483

2 Articles, Search for 'hardware interrupt'

  1. 2007/05/10 리눅스 커널의 이해(2): 리눅스 커널의 동작
  2. 2007/05/10 리눅스 커널의 이해(1) : 커널의 일반적인 역할과 동작
Unix & Linux/Kernel2007/05/10 10:09

리눅스 커널의 이해(2): 리눅스 커널의 동작

저자: 서민우
출처: Embedded World

1. 리눅스 커널의 기본적인 동작

이제 리눅스 커널이 어떻게 동작하는지 들여다 보자.
리눅스 커널은 그 소스량은 엄청나지만 역시 커널의 기본적인 동작은 우리가 지금까지 보아온 커널의 동작과 별로 다르지 않다. 덧붙이자면 다른 RTOS도 역시 마찬가지다.

system call에 의해 시작하는 리눅스 커널의 일반적인 동작

[그림 1]은 system call에 의해 시작하는 리눅스 커널의 일반적인 동작이다.

사용자 삽입 이미지

[그림 1] system call에 의한 리눅스 커널의 일반적인 동작


[그림 1]에서 커널은 process의 system call에 의해 수행을 시작한다. 먼저 커널의 시작 부분에서는 현재 process의 사용자 영역에서의 register의 내용을 stack상에 저장한다. 다음은 커널에서 사용자 영역으로 빠져 나가기 바로 전에 커널의 시작 부분에서 stack상에 저장한 register의 내용을 다시 복구한다. sys_func(), sys_func()내의 schedule(), sys_func()를 수행하고 난 후에 수행하는 schedule()의 역할은 전 월호의 [그림 5]에서 이미 설명했다. 리눅스 커널에서는 어떤 process에서 또 다른 process로, 또는 interrupt handler에서 process로 signal을 보낼 수 있으며, do_signal()에서는 커널영역에서 사용자 영역으로 빠져 나가기 전에 현재 process에 도착한 signal이 있는지를 검사하고 도착한 signal이 있으면 적절히 처리하는 부분이다. 마지막으로 a와 b사이에서는 기본적으로 hardware interrupt를 허용하며, 이 구간에서 발생하는 hardware interrupt를 일반적으로 nested interrupt라 한다. nested interrupt에 의해 수행을 시작하는 커널을 우리는 nested interrupt routine이라고 하며, 일반적으로 nested interrupt routine에 의해 커널의 흐름은 상당히 복잡해지며, 여러 가지 동기화 문제가 발생한다. nested interrupt routine에 의해 발생하는 이러한 문제점과 그에 대한 해결책은 다음 기사에서 자세히 다루기로 하겠다.

hardware interrupt에 의해 시작하는 리눅스 커널의 일반적인 동작

[그림 2]는 hardware interrupt에 의해 시작하는 리눅스 커널의 일반적인 동작이다.

사용자 삽입 이미지

[그림 2] hardware interrupt에 의한 리눅스 커널의 일반적인 동작


[그림 2]에서 커널은 hardware interrupt에 의해서 수행을 시작한다. 리눅스 커널에서는 top_half()와 bottom_half()를 do_IRQ()라는 함수 내에서 차례로 수행한다. 다른 부분의 역할은 이미 전 월호의 [그림 2]와 앞의 [그림 1]에서 설명하였다. 한가지 짚고 넘어갈 점은 a와 b사이에서는 기본적으로 hardware interrupt를 허용한다. 따라서 이 구간에서도 역시 nested interrupt가 발생할 수 있다.

2. nested interrupt와 리눅스 커널의 동작

[그림 1]과 [그림 2]에서 우리는 리눅스 커널내에서 nested interrupt가 발생할 수 있는 영역을 보았다(각각 a와 b사이의 구간). nested interrupt에 의해 커널의 동작이 어떻게 바뀌는지 보기 전에 먼저 몇 가지 짚고 넘어갈 사항이 있다.

리눅스 커널내에서 각 영역의 속성과 우선 순위

[그림 1]에서 sys_func()은 커널이 process의 요청에 의해 수행하는 부분으로 process와 직접적으로 관련된 함수이다. do_signal()도 process와 직접적으로 관련된 함수이다. schedule() 역시, 새로 수행할 process를 runqueue로부터 뽑고(리눅스 커널에서는 ready queue를 runqueue라고 한다), 현재 process의 커널 영역에서의 register의 내용을 메모리에 저장하고, 새로 수행할 process의 커널 영역에서의 register의 내용을 메모리로부터 복구하는, process와 간접적으로 관련된 함수이다. save register와 restore registerprocess의 사용자 영역에서의 register의 내용을 메모리에 저장하고, 사용자 영역에서의 register의 내용을 메모리로부터 복구하는 동작으로 process와 관련된 부분이다.

[그림 2]에서 do_IRQ()는 커널이 device로부터 들어온 요청을 처리하는 부분이다. 그 중에 top_half()는, 예를 들어 device를 접근하는 등의, 시간상으로 신속히 처리해야 할 부분이며, bottom_half()는, 예를 들어 device로부터 메모리로 읽어온 data(top_half()에서 device로부터 메모리로 가져온)를 처리하는 등의, top_half()에 비해 비교적 천천히 처리해도 되는 부분이다. 나머지 schedule(), do_signal(), save register, restore register는 앞의 경우처럼 process와 관련된 부분이다.

이상에서 리눅스 커널 영역은 논리적으로 다음과 같이 세 부분으로 나눌 수 있다.

device와 직접적으로 관련된 top_half() 부분
device와 간접적으로 관련된 bottom_half() 부분
process와 직접적으로 또는 간접적으로 관련된
schedule(), sys_func(), do_signal(), save register, restore register 부분

처음에 리눅스 커널을 설계하는 과정에서 top_half(), bottom_half(), process와 관련된 함수들 순으로 우선 순위를 주었다. 우선 순위에 따라 커널 영역을 빨강, 녹색, 파랑으로 표시할 경우 [그림 3]과 같다. [그림 3]에서 save register와 restore register는 process와 관련된 부분이기는 하지만 nested interrupt가 발생할 수 없는 영역이므로 여기서는 색깔로 표시하지 않았다.

사용자 삽입 이미지

[그림 3] 리눅스 커널내에서 각 영역의 우선 순위


그러면 지금부터 nested interrupt에 의해 커널이 수행해야 할 동작이 어떻게 바뀌어야 할지 생각해 보기로 하자. 참고로 [그림 3]에서 커널 영역 중 색깔이 없는 부분에서는 interrupt를 허용하지 않는다고 가정하자.

top_half()와 nested interrupt routine

먼저 top_half() 부분에서 interrupt가 발생했을 경우를 생각해 보자. 리눅스 커널에서는 top_half() 부분에서 interrupt handler에 따라 interrupt를 막을 수도 있고 열어 놓을 수도 있다. 이 부분에서 interrupt를 열어 놓아 interrupt가 발생하였을 경우에 리눅스 커널은 [그림 4]와 같이 동작해야 한다.

사용자 삽입 이미지

[그림 4] top_half()와 nested interrupt routine


[그림 4]에서 A와 B의 우선순위는 같다 하더라도 A에서 interrupt를 허용하였기 때문에 A를 수행하는 중이라도 B는 수행이 될 수 있다. 그러나, C, D, E는 A보다 우선순위가 낮기 때문에 수행하지 않고 나가는 것이 논리적으로 맞다. 그럼 리눅스 커널은 C, D, E를 수행하지 않는가? 그건 아니다. C의 경우 F를 수행할 때 함께 처리한다. D의 경우는 B나 C에서 schedule을 요청할 경우 수행하는 부분으로 G에서 처리하면 된다. E의 경우는 현재 process에게 도착한 signal을 처리하는 부분이며, H와 중복된다. 따라서 H에서 처리하면 된다.

bottom_half()와 nested interrupt routine

다음은 bottom_half()에서 interrupt가 발생했을 경우를 생각해 보자. 앞에서 bottom half()에서는 기본적으로 interrupt가 열려 있다고 말한 바 있다. 이 부분에서 interrupt가 들어올 경우 커널은 [그림 5]와 같이 동작해야 한다. [그림 5]에서 B의 우선 순위는 F의 우선 순위보다 크다. 따라서, F를 수행하는 도중이라도 B를 수행할 수 있다. C의 경우는 F와 우선 순위가 같으므로 B 다음에 바로 처리하지 않고, F를 처리한 후에, F를 다시 수행하여 C를 처리한다. D, E에 대한 처리는 [그림 4]에서 이미 설명하였다.

사용자 삽입 이미지

[그림 5] bottom_half()와 nested interrupt routine


schedule()과 nested interrupt routine

다음은 schedule()을 수행하는 중에 interrupt가 발생했을 경우를 생각해 보자. 이 부분에서 interrupt가 들어올 경우 리눅스 커널은 [그림 6]과 같이 동작해야 한다.

사용자 삽입 이미지

[그림 6] schedule()과 nested interrupt routine


[그림 6]에서 A는 process와 관련된 부분으로 B와 C보다 우선순위가 낮다. 따라서 A를 수행하는 중이라도 커널은 B와 C를 당연히 수행해야 한다. D는 A와 같은 부분으로 A와 우선 순위가 같다. 따라서 A를 수행하고 나서, A를 다시 한 번 더 수행하면 된다. 즉 nested interrupt routine에서는 D를 수행할 필요가 없다. E는 앞에서 설명한 것처럼 H와 중복되므로 수행할 필요가 없다.

do_signal()과 nested interrupt routine 그리고 커널 preemption

다음은 do_signal()을 수행하는 중에 interrupt가 발생했을 경우를 생각해 보자. 이 부분에서 interrupt가 들어올 경우 리눅스 커널은 [그림 7]과 같이 동작하도록 설계되었다.

사용자 삽입 이미지

[그림 7] do_signal()과 nested interrupt routine


[그림 7]에서 A는 process와 관련된 부분으로 B와 C보다 우선순위가 낮다. 따라서 A를 수행하는 중이라도 커널은 당연히 B와 C를 수행해야 한다. B나 C에서 wait queue에 있던 process를 runqueue에 넣고, runqueue에 새로 들어간 process가 현재 process보다 우선 순위가 클 경우 process scheduling을 요청할 수 있다. 그러면 커널은 D를 수행하며 A를 수행중이었더라도 다른 process로 전환이 일어나게 된다. 이는 A가 커널의 한 영역이라도 process와 관련된 부분이므로, A와 관련된 현재 process보다 우선 순위가 큰 process가 B나 C에서 runqueue로 들어갈 경우 당연히 process 전환을 수행할 수 있다. 이는 리눅스 커널 2.5 이후에 새로이 추가된 기능으로 커널 preemption이라고 한다. 당연히 리눅스 커널 2.4에는 없는 기능이다. E는 A와 중복되므로 수행하지 않는다.

sys_func()과 nested interrupt routine 그리고 커널 preemption

다음은 sys_func()를 수행하는 중에 interrupt가 발생했을 경우를 생각해 보자. 이 부분에서 interrupt가 들어올 경우 리눅스 커널은 [그림 8]과 같이 동작하도록 설계되었다.
[그림 8]에서 A(sys_func())는 process와 관련된 부분으로 [그림 7]에서의 A(do_signal())와 같이 취급한다. 당연히 [그림 8]에서 A를 수행하는 도중이라도 B와 C를 수행해야 하며, 필요 시에는 D에 의해 다른 process로 전환할 수 있다. 이 역시 리눅스 커널 2.5 이후에 새로이 추가된 커널 preemption 기능이다. E는 F와 중복되므로 수행하지 않는다.

사용자 삽입 이미지

[그림 8] sys_func()과 nested interrupt routine


sys_func()내의 schedule()과 nested interrupt routine

다음은 sys_func()내에서 schedule()을 수행하는 중에 interrupt가 발생했을 경우를 생각해 보자. 이 부분에서 interrupt가 들어올 경우 리눅스 커널은 [그림 9]와 같이 동작하도록 설계되었다.

사용자 삽입 이미지

[그림 9] sys_func()내의 schedule()과 nested interrupt routine


[그림 9]에서 A는 process와 관련된 작업이다. 따라서 A를 수행하는 도중이라도 당연히 B와 C를 수행해야 한다. D는 A와 같은 부분으로 A와 우선 순위가 같다. 따라서 A를 수행하고 나서 수행해야 한다. 즉 nested interrupt routine에서는 수행할 필요가 없다. E는 앞에서 설명한 것처럼 F와 중복되므로 수행할 필요가 없다.

nested interrupt에 의한 커널의 동작

이상에서 우리는 nested interrupt에 의해 커널 수행해야 할 동작을 보았으며, [그림 10]과 같다.

사용자 삽입 이미지

[그림 10] nested interrupt에 의한 커널의 동작


지금까지 우리는 리눅스 커널이 실제로 어떻게 설계되었는지 보았다.

3. multi-tasking의 구현

다음은 간단한 scheduling과 context switching에 의해 multi-tasking이 어떻게 구현되는지를 보여주는 예다. 이 예를 통해서 마술 같은 multi-tasking을 구체적으로 이해해 보기로 하자. 지난 기사에서 설명한 부분에 대한 이해를 돕고자 이 부분을 추가하였다.

먼저 scheduling이란 현재 process를 어떤 이유에 의해서 잠시 멈출 때 새로이 수행할 process를 선택하는 커널의 동작을 말한다.

다음으로 context란 processor(CPU)가 어떤 process를 수행할 때의 processor의 상태를 말한다. processor의 상태란 구체적으로 processor 내의 여러 register의 어느 순간의 상태를 말한다. 따라서 context switching이란 현재 수행하던 process의 context를 그대로 메모리로 저장하며, scheduling을 통해 선택한 새로운 process의 context를 메모리로부터 processor의 여러 register로 복구하는 커널의 동작을 말한다.

다음의 예는 multi-tasking.s와 multi-tasking.c의 두 가지 파일로 구성된다. 그럼 구체적으로 구현 내용을 들여다 보자.





<1>에서 process_state는 하나의 process를 관리하기 위한 구조체이다. 이 구조체 내의 stack_top 변수는 stack pointer를 저장하기 위한 공간이고, stack 배열 변수는 256*4 byte 크기의 process stack이다.

<2>에서는 두 개의 process를 관리하기 위하여 process_state 구조체 두 개를 process 배열 변수로 선언하였다.

<3>에서는 scheduling과 context switching시 사용할 process_state 구조체를 가리킬 수 있는 pointer 변수 두 개를 선언하였다.

<4>에서 <5>까지는 process[OTHER]의 상태를 초기화 하며, process[OTHER]의 상태는 [그림 11]과 같아진다.
사용자 삽입 이미지
사용자 삽입 이미지

[그림 11] process[OTHER]의 초기화


<6>은 간단하지만 새로운 작업을 선택하는 scheduling 과정이다. 여기서는 새로운 작업으로 process[OTHER]를 선택한다.

<7>을 어셈블리어로 나타내면 다음과 같다.

사용자 삽입 이미지



여기서 과 , 과 부분에서 스택에 차례로 next, prev 값이 들어간다. 그리고, 부분에서 스택에 return address(0x0804852d) 값이 들어가며, multi-tasking.s 파일의 context_switch 함수로 뛴다. [그림 12]에서 ① 부분이 이 과정에서 만들어진다.

다음은 multi-tasking.s 파일의 context_switch 함수를 보자.

먼저 ⓐ에서 [그림 12]의 ② 부분이 만들어진다. 다음으로 ⓑ에서 processor의 esp register 값([그림 12]의 ③)을 process[MAIN]의 stack_top([그림 12]의 ④)에 저장한다. 이로써 지금까지 수행하던 process의 문맥 저장을 끝낸다.

다음은 ⓒ에서 process[OTHER]의 stack_top 값([그림 12]의 ⑤)을 processor의 esp register([그림 12]의 ⑥)에 저장한다. 이 부분에서 esp register는 process[OTHER]의 stack top을 가리킨다. process[OTHER]는 이전에 초기화 되었으며, 이미 [그림 11]에서 살펴 보았다. ⓓ에서 ⑦부분에 저장된 값들이 processor의 각 register로 채워진다. 이로써 새로 수행할 process의 문맥을 복구하였다. 마지막으로 ret 명령에 의해 [그림 12]의 ⑧에서 ra의 값이 eip로 들어가면서 other 함수를 수행하기 시작한다. 이 때 esp register는 [그림 12]의 ⑨를 가리킨다.

사용자 삽입 이미지

[그림 12] process간 전환


multi-tasking의 동작 방식을 이해했으면 마지막으로 두 파일을 컴파일 하여 실행해 본다.

이상에서 우리는 multi-tasking이 어떻게 구현되는지를 보았다. 비록 짧은 소스이기는 하지만 중요한 개념들이 많이 들어가 있으며, 커널의 핵심적인 부분만을 떼내어 이해할 수 있다.

마무리

지금까지 우리는 리눅스 커널이 어떻게 설계되었는지 보았다. 또한 multi-tasking이 어떻게 구현되는지 보았다. 이 과정에서 scheduling과 task 초기화도 들여다 보았다. 다음 기사에서는 리눅스 커널이 구체적으로 어떻게 구현되었는지 소스 수준에서 살펴 보기로 하자.
"Kernel" 카테고리의 다른 글
  • 리눅스 커널의 이해(4): Uni-Processor & Multi-Pr... (0)2007/05/10
  • 리눅스 커널의 이해(3): 리눅스 디바이스 작성시... (0)2007/05/10
  • 리눅스 커널의 이해(2): 리눅스 커널의 동작 (0)2007/05/10
  • 리눅스 커널의 이해(1) : 커널의 일반적인 역할과... (0)2007/05/10
  • Kprobes를 이용한 커널 디버깅 (0)2007/05/04
2007/05/10 10:09 2007/05/10 10:09
Posted by webdizen
Tags hardware interrupt, multi tasking, nested interrupt, system call, 리눅스 커널
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2915

Leave your greetings.

[로그인][오픈아이디란?]

Unix & Linux/Kernel2007/05/10 09:56

리눅스 커널의 이해(1) : 커널의 일반적인 역할과 동작

저자: 서민우
출처: Embedded World

본 기사는 리눅스 커널 2.6이 hardware interrupt와 system call을 중심으로 어떻게 설계되었고, 구현 되었는지 살펴본다. 이 과정에서 리눅스 커널 2.6에 새로이 추가된 커널 preemption 기능을 자세히 살펴보기로 한다. 또한 커널의 동기화 문제와 이에 대한 해결책 등을 커널 source 내에서 찾아보기로 하고, 후에 device driver등을 작성할 때 이러한 해결책을 어떻게 적용할 수 있을지도 생각해 본다. 다음으로 리눅스 커널 2.6에 추가된 O(1) scheduler를 소스 수준에서 자세히 살펴 보기로 한다. 또한 task queue의 변형된 형태인 work queue의 사용법을 알아보기로 한다. 마지막으로 리눅스 커널 2.6에서는 어떻게 device driver를 작성해야 할지 구체적인 예를 보기로 한다.

이번 기사에서는 리눅스 커널을 소스 수준에서 구체적으로 들여다 보기 전에 일반적인 커널의 동작을 살펴보고, 이를 바탕으로 리눅스 커널의 전체적인 동작을 살펴보기로 한다.

1. 일반적인 커널의 동작

여기서는 process와 device 사이에서 커널이 수행해야 할 구체적인 역할을 몇 가지 살펴보고, 이를 기본으로 해서 일반적인 커널의 동작을 이해해 보기로 한다.

system call에 의한 커널의 구체적인 동작 1
일반적으로 process는 system call을 통해 커널에게 device로부터 data를 읽기를 요청한다. 그러면 커널은 device로부터 data를 읽기를 요청하고 현재 수행중인 process를 잠시 멈추기 위해 wait queue에 넣는다. 왜냐하면 device로부터 data가 도착해야 그 process를 다시 진행할 수 있기 때문이다(wait, sleep, block과 같은 용어는 이러한 상황에서 쓰인다). 그리고 새로운 process를 적절한 기준에 의해 선택해 수행하기 시작한다. 새로운 process를 선택하고 그 process로 전환하는 과정을 process scheduling이라고 한다. 그 이후에 몇 번의 process scheduling이 더 있을 수 있다.

이 과정을 processor의 관점에서 다시 보자.
processor가 process의 사용자 영역(응용프로그램 영역)을 수행하는 중에 system call 명령을 만나면 커널 영역으로 뛰어 들어간다. 커널 영역에는 device로부터 data를 읽기를 요청하고 현재 수행중인 process를 잠시 멈추기 위해 wait queue에 넣고 새로운 process를 선택해 수행하는 일련의 명령들이 있다(이러한 일련의 명령들을 process 또는 커널이 수행할 작업이라고 하자). 이러한 명령들에 따라 결국 processor는 새로운 process를 선택해 수행하기 시작한다. 그 이후에 몇 번의 process scheduling이 더 있을 수 있으며, processor는 임의의 시간에 임의의 process를 수행하고 있다.

hardware interrupt에 의한 커널의 구체적인 동작
processor가 임의의 process를 수행하는 동안에 device에는 data가 도착한다. device는 data의 도착을 물리적인 신호를 통해서 processor에게 알린다(이를 우리는 hardware interrupt라고 한다). 그러면 processor는 이 신호를 감지하고 커널 영역으로 뛰어 들어간다. 커널 영역에는 device에 도착한 data를 메모리로 읽어 오고, 그 data를 사용할 process에 맞게 적절하게 형태를 바꾸어, data를 기다리는 process에게 전달하고, 그 process를 wait queue에서 꺼내 ready queue로 집어넣은 후, ready queue로 들어간 process의 우선순위가 현재 수행 중이던 process의 우선순위보다 클 경우 process scheduling을 요청한 후 process scheduling을 수행하는 일련의 명령들이 있다. 이러한 명령들에 따라 결국 processor는 device로부터 data를 읽기를 요청한 process를 다시 선택해 수행하기 시작한다. processor는 다시 시작한 process의 커널 영역에서 사용자 영역으로 빠져 나가 사용자 영역을 계속해서 수행한다.

[그림 1]을 보면서 좀 더 구체적으로 이해해 보자. process P1은 사용자 영역의 A 부분에서 system call을 통해 커널 영역으로 들어간다. 커널 영역의 B 부분에서 device로부터 data를 읽기를 요청한 후 현재 수행중인 process를 wait queue에 넣는다. 그리고 C 부분에서 process scheduling을 수행한다. 이 부분을 좀 더 자세히 들여다보면 C 부분에서 시작한 process scheduling은 D 부분에서 끝나지 않고 process P2의 E 부분에서 끝난다. 즉, c 지점으로 들어가서 e 지점으로 나온다.

process scheduling
여기서 process scheduling의 동작을 좀 더 구체적으로 살펴보자. process scheduling은 크게 두 동작으로 나뉜다. 처음 동작은 새로 수행할 process를 선택하는 부분이다. 두번째 동작은 현재 수행하고 있는 process의 상태를 저장한 다음 새로 수행할 process의 상태를 복구하는 것이다. 이 동작을 우리는 문맥 전환이라고 한다. processor는 내부에 여러 개의 register를 가지고 있으며, 이 register를 이용해 process를 수행해 나간다. register는 memory와 같이 data를 저장하는 기능을 하지만, 접근 속도가 memory보다 빠르다. 따라서 비용상 그 개수가 많지는 않다. register는 processor architecture에 따라 R0, R1, ... 또는 EAX, EBX, ... 등의 이름을 가지며, 32 bit RISC processor의 경우 일반적으로 32 bit의 크기를 갖는다. processor는 register와 memory 또는 I/O device내의 register간에
data를 옮겨가면서 procss를 수행해 나간다. 따라서 process를 수행해 나감에 따라 register의 내용은 계속 바뀌게 된다. 문맥 전환 부분을 좀 더 자세히 들여다 보면 processor가 현재 process를 수행해 나가다 어느 순간에 register의 내용을 그대로 메모리에 저장한다. 새로 수행할 process의 경우도 현재 process처럼 이전에 저장한 register의 내용이 메모리에 있으며, 따라서 그 메모리에 저장한 register의 내용을 다시 processor의 register로 복구 시킨다. 그리고 새로운 process를 계속 수행해 나간다.

문맥 전환(context switching)
문맥 전환 부분을 좀 더 구체적으로 이해하기 위해 process scheduling의 동작을 다음과 같이 가정해 보자. 처음 동작에서 새로 수행할 process를 뽑았는데 그 process가 현재 수행하고 있던 process였다. 그러면 두 번째 동작은 다음과 같이 될 것이다. processor가 현재 process의 register의 내용을 메모리에 저장한다. 그리고 방금 전에 메모리에 저장한 register의 내용을 다시 processor의 register로 복구 시킨다. 그리고 현재 process를 계속 수행한다. 이럴 경우 [그림 1]에서 C 부분에서 시작한 process scheduling은 D 부분에서 끝나며, 논리적으로 process scheduling을 수행하지 않은 것과 같다. process scheduling의 본래 목적은 process간의 전환이며 따라서 현재 process와 새로 수행할 process가 있어야 그 본래 기능을 수행할 수 있다. 여기서는 문맥 전환의 동작을 이해하기 위하여 이와 같은 가정을 한 것이다.

사용자 삽입 이미지

[그림 1] system call과 hardware interrupt에 의한 커널의 구체적인 동작


그러면 process scheduling의 본래 기능으로 다시 돌아가 문맥 전환을 생각해 보자.
[그림 1]에서 현재 process를 P1, 새로 수행할 process를 P2라 하자. 그러면 process P1의 C 부분에서 시작한 process scheduling이 논리적으로 D 부분에서 끝나야 하는 것처럼(여기서는 실제로 J 부분에서 끝난다) 이전에 process P2의 F 부분에서 시작한 process scheduling은 논리적으로 E 부분에서 끝나는 것이다. 그러나 시간상으로는 process P1의 C 부분에서 시작한 process scheduling은 process P2의 E 부분에서 끝난다. 즉, c 지점으로 들어가서 e 지점으로 나온다.

이후에 process P2에서 process P3로(f에서 g로), process P3에서 process P4로, 몇 번의 process scheduling이 더 있을 수 있으며(h에서 … i로), 어느 순간 임의의 process Pn이 수행 중일 수 있다. [그림 1]에서 process Pn을 수행하는 중에 G 부분에서, process P1의 B 부분에서 data를 읽기를 요청한 device로부터, hardware interrupt가 들어올 수 있다. 그러면 process Pn은 G 부분에서 커널 영역으로 들어간다. 커널은 H 부분에서 device에 도착한 data를 메모리로 읽어 오고, 그 data를 사용할 process P1에 맞게 적절히 형태를 바꾸어 process P1에게 전달하고, process P1을 wait queue에서 꺼내 ready queue로 넣은 후, 새로이 ready queue로 들어간 process P1의 우선순위가 현재 수행 중인 process Pn의 우선순위보다 클 경우 process scheduling을 요청한다. 그러면 I 부분에서 process scheduling을 수행한다. process Pn의 I 부분에서 시작한 process scheduling은 process P1의 J 부분에서 끝난다. 덧붙이자면, process P1의 C 부분과 J 부분은 시간적으로는 연속이지 않지만 논리적으로는 연속이다.

hardware interrupt에 의한 커널의 일반적인 동작
이제 hardware interrupt에 의해 시작한 커널의 일반적인 동작을 정리해 보자.
[그림 1]에서 process Pn을 수행하는 중에 들어온 hardware interrupt에 의해 시작한 커널의 동작은 다음과 같다.

1. device에 도착한 data를 메모리로 읽어 온다.
2. data를 사용할 process에 맞게 적절하게 형태를 바꾼다.
3. data를 기다리는 process에게 전달하고 process scheduling 요청
4. process scheduling을 수행

여기서 커널의 동작은 크게 세 부분으로 나눌 수 있으며, 그 처음 부분은 다음과 같다.

1. device에 도착한 data를 메모리로 읽어 온다.

이 부분은 hardware interrupt를 처리하는 부분으로써 신속하게 device로부터 data를 읽어냄으로써 빠른 시간 내에 device가 외부로부터 다시 data를 받을 수 있게 한다. 일반적으로 이 부분에서는 또 다른 device로부터 오는 hardware interrupt를 허용하지 않음으로써 신속하게 device로부터 data를 읽어낸다. 리눅스 커널에서는 이 부분을 top half라고 하기도 하고 interrupt handler라고도 한다.

다음으로 두 번째 부분은 다음과 같다.

2. data를 사용할 process에 맞게 적절하게 형태를 바꾼다.
3. data를 기다리는 process에게 전달하고 process scheduling 요청

이 부분은 기본적으로 hardware interrupt를 허용함으로써 응답성을 좋게 한다. 이 과정은 device에서 읽어온 data를 적당하게 처리해 그 data를 기다리는 process에게 전달하고 필요시 process scheduling을 요청한다. 리눅스 커널에서는 이 부분을 bottom half라고도 하고, deferred work라고도 하고, softirq라고도 한다. 덧붙이자면 3번 동작을 리눅스 커널에서는 wake_up이라고 한다.

마지막으로 세 번째 부분은 다음과 같다.

4. process scheduling을 수행

이 부분은 두 번째 부분에서 process scheduling을 요청할 경우 수행한다. 리눅스 커널에서는 이 부분을 schedule이라고 한다.

이상에서 hardware interrupt에 의한 커널의 동작은 [그림 2]와 같다.

사용자 삽입 이미지

[그림 2] hardware interrupt에 의한 커널의 일반적인 동작



[그림 1]에서 한 가지 주의할 점은 process Pn의 사용자 영역을 수행하는 중에 들어온 hardware interrupt에 의해 시작한 커널의 동작은 process Pn과 논리적으로 관련이 없다. 따라서 앞에서 설명한 처음 동작과 두 번째 동작을(top half와 bottom half를) 수행하는 중에 현재 process Pn은 논리적으로 멈출 일이 없으며, 따라서 wait queue에 들어갈 일은 없다.

top_half, bottom_half와 system call function간의 통신
마지막으로 한 가지만 더 짚고 넘어가면, [그림 1]에서 process P1의 system call에 의해 시작한 커널과 process Pn의 사용자 영역 수행 중에 발생한 hardware interrupt에 의해 시작한 커널은 각각 논리적으로 독립된 흐름을 가지며 B 부분과 H 부분에서 통신을 한다. 즉, H 부분에서 data를 공급하며, B 부분에서 data를 소비한다. [그림 3]은 [그림 1]의 system call에 의한 커널과 hardware interrupt에 의한 커널간에 data를 주고 받는 상황을 논리적으로 표현한 것이다.

사용자 삽입 이미지

[그림 3] top_half, bottom_half와 system call function간의 통신


system call에 의한 커널의 구체적인 동작 2
[그림 4]를 보면서 다음의 내용을 이해해 보자.
P1, Pn이라 하는 두 process가 있다고 가정하자. process P1는 system call([그림 4]의 A 부분)을 통해 커널에게 process Pn으로부터 data를 받기를 요청할 수 있다. 그러면 커널은 Pn으로부터 P1에게 도착한 data가 있는지 검사한다([그림 4]의 B 부분). P1에게 도착한 data가 없을 경우 커널은 현재 수행중인 process P1을 잠시 멈추기 위해 wait queue에 넣는다([그림 4]의 B 부분). 왜냐하면 process Pn으로부터 data가 도착해야 process P1을 다시 진행할 수 있기 때문이다. 그리고 새로운 process를 선택해([그림 4]의 C 부분) 수행하기 시작한다. 이 동작을 우리는 앞에서 process scheduling이라 했다. 그 이후에 몇 번의 process schduling이 더 있을 수 있다. ([그림 4]에서 process P2에서 process P3로)

사용자 삽입 이미지

[그림 4] system call에 의한 커널의 구체적인 동작


system call에 의한 커널의 구체적인 동작 3

어느 순간 process Pn은 process scheduling에 의해 다시 시작하며([그림 4]의 D 부분) 사용자 영역을 수행하다 system call을 통해([그림 4]의 E 부분) 커널에게 process P1에게 data를 보내기를 요청할 것이다. 그러면 커널은 process Pn으로부터 process P1으로 data를 전달하고([그림 4]의 F 부분), process P1을 wait queue에서 꺼내 ready queue로 넣은 후, ready queue로 새로이 들어간 process P1의 우선순위가 현재 수행 중이던 process Pn의 우선순위보다 클 경우 process scheduling을 요청한 후([그림 4]의 F 부분) process scheduling을 수행한다. process scheduling은 [그림 4]의 G 부분에서 시작해 H 부분에서 끝난다. 즉, process scheduling이 끝나면 process P1이 수행을 다시 시작한다.

system call에 의한 커널의 일반적인 동작
이제 system call에 의해 시작한 커널의 일반적인 동작을 정리해 보자. 먼저 system call은 software interrupt라고도 한다. 주의할 점은 software interrupt는 리눅스 커널내의 bottom half의 또 다른 이름인 softirq와는 관련이 없다.

[그림 1]에서 process P1을 수행하는 중에 들어온 system call에 의해 시작한 커널의 동작은 다음과 같다.

1. device로부터 data를 읽기를 요청한다.
2. 현재 수행중인 process를 wait queue에 넣는다
3. process scheduling을 수행

이 부분은 process의 요청에 의해 커널이 수행하는 영역이며, 상황에 따라 현재 process를 논리적으로 더 이상 진행시킬 수 없는 경우 현재 process를 wait queue에 넣고 process scheduling을 수행할 수 있다. 이 부분은 system call 함수의 일부분이다. 2, 3번 항목은 리눅스 커널의 sleep_on 또는 wait_event와 대응한다.

[그림 4]에서 process P1을 수행하는 중에 들어온 system call에 의해 시작한 커널의 동작은 다음과 같다.

1. process Pn으로부터 도착한 data가 있는지 검사한다.
2. 현재 수행중인 process를 wait queue에 넣는다.
3. process scheduling을 수행

이 부분도 process의 요청에 의해 커널이 수행하는 영역이며, 상황에 따라 현재 process를 논리적으로 더 이상 진행할 수 없는 경우 현재 process를 wait queue에 넣고 process scheduling을 수행한다. 이 부분도 system call 함수의 일부분이다. 여기서도 2, 3번 항목은 리눅스 커널의 sleep_on 또는 wait_event에 대응한다.

[그림 4]에서 process Pn을 수행하는 중에 들어온 system call에 의해 시작한 커널의 동작은 다음과 같다.

1. process P1에게 data를 전달하고 process scheduling 요청
2. process scheduling을 수행

여기서는 커널의 동작을 두 부분으로 나눌 수 있으며, 처음 부분은 다음과 같다.

1. process P1에게 data를 전달하고 process scheduling 요청

이 부분은 process의 요청에 의해 커널이 수행하는 영역이며, system call 함수의 일부분이다. 이 부분은 리눅스 커널의 wake_up에 대응한다.

두 번째 부분은 다음과 같다.

2. process scheduling을 수행

이 부분은 처음 부분에서 process scheduling을 요청할 경우 수행한다.

이상에서 system call에 의한 커널의 동작은 [그림 5]와 같다.

사용자 삽입 이미지

[그림 5] system call에 의한 커널의 일반적인 동작


[그림 5]에서 process scheduling(1)은, 현재 process P의 요청에 따라 커널이 process P와 관련된 작업을 수행하는 도중에 어떤 조건이 맞지 않아, 예를 들어 필요로 하는 data가 없어서, 더 이상 현재 process P의 작업을 진행할 수 없을 경우, 필요로 하는 조건이 맞을 때까지 현재 process P를 wait queue에 넣어 기다리게 하고 나서 수행하는 process scheduling이며, system call function내에서 수행을 한다. 한 가지 기억해야 할 점은 [그림 1]에서 C와 J 부분이 일반적으로 논리적으로는 연속이지만 시간상으로는 연속이 아니듯이 [그림 5]의 process scheduling(1)도 일반적으로 논리적으로는 연속이지만 시간상으로는 연속이 아니다. 후에 process P가 필요로 하는 조건이 맞으면, process P는 논리적인 흐름이 다른 커널(예를 들어, [그림 1]의 H 부분과 같은)에 의해 ready queue로 옮겨지며, 역시 논리적인 흐름이 다른 커널에서 시작한 process scheduling(예를 들어, [그림 1]의 I 부분과 같은)에 의해 [그림 5]의 process scheduling(1)로 나와 system call function의 나머지 부분을 수행한다. system call function 내에서는 이후에도 필요에 따라 process scheduling이 더 있을 수 있다. 이와는 달리 process scheduling(2)는 커널이 process P의 요청에 의해 system call function을 수행하는 도중에 wait queue에서 기다리던 임의의 process를 ready queue로 넣고, 그 ready queue에 넣은 process의 우선 순위가 현재 process P의 우선 순위보다 클 경우(예를 들어 [그림 4]의 F 부분과 같은)에 수행하는 process scheduling이다. 이 경우 현재 process P는 ready queue에 그대로 남아 있다.

system call function과 system call function간의 통신
마지막으로 한 가지만 더 짚고 넘어가면, [그림 4]에서 process P1의 system call에 의해 시작한 커널과 process Pn의 system call에 의해 시작한 커널은 각각 논리적으로 독립된 흐름을 가지며 B 부분과 F 부분에서 통신을 한다. 즉, F 부분에서 data를 공급하며, B 부분에서 data를 소비한다. [그림 6]은 [그림 4]의 system call에 의한 커널간에 data를 주고 받는 상황을 논리적으로 표현한 것이다.

사용자 삽입 이미지

[그림 6] system call function과 system call function간의 통신


process scheduling의 시작과 끝
우리는 [그림 2]와 [그림 5]에서 커널의 일반적인 동작과 process scheduling이 언제 수행되는지 보았다. 아래 [그림 7]에서 process scheduling이 시작되는 부분과 끝나는 부분이 어떻게 연결될 수 있는지 자세히 살펴 보자.
어떤 process의 a 부분에서 시작한 process scheduling은 임의의 다른 process의 b, d, f부분에서 끝날 수 있다. 또 어떤 process의 c 부분에서 시작한 process scheduling도 임의의 다른 process의 b, d, f 부분에서 끝날 수 있다. 마지막으로 어떤 process의 e 부분에서 시작한 process scheduling 역시 임의의 다른 process의 b, d, f 부분에서 끝날 수 있다.

사용자 삽입 이미지

[그림 7] process scheduling의 시작과 끝


지금까지 우리는 커널이 수행해야 할 일반적인 동작이 무엇인지 살펴 보았다. 즉, system call을 통해 시작한 커널의 동작, hardware interrupt에 의해 시작한 커널의 동작을 보았다. 이 과정에서 process와 device 사이에서 커널이 수행해야 할 역할이란 것이 우리가 모르는 그 어떤 것이 아니란 점도 느꼈을 것이다. 의외로 커널의 역할이 지극히 당연한 것들이라고 느꼈을 수도 있다. 또 hardware interrupt에 의해 시작한 커널과 system call에 의해 시작한 커널간의 통신, system call에 의해 시작한 커널과system call에 의해 시작한 커널간의 통신을 보았다. 이 과정에서 논리적으로 서로 독립적인 커널의 동작간에 통신이 어떻게 이루어지는지 구체적으로 알았을 것이다.


http://network.hanbitbook.co.kr/view.php?bi_id=1058
"Kernel" 카테고리의 다른 글
  • 리눅스 커널의 이해(3): 리눅스 디바이스 작성시... (0)2007/05/10
  • 리눅스 커널의 이해(2): 리눅스 커널의 동작 (0)2007/05/10
  • 리눅스 커널의 이해(1) : 커널의 일반적인 역할과... (0)2007/05/10
  • Kprobes를 이용한 커널 디버깅 (0)2007/05/04
  • KernelAnalysis-HOWTO (0)2007/04/30
2007/05/10 09:56 2007/05/10 09:56
Posted by webdizen
Tags hardware interrupt, process scheduling, processor, system call, 리눅스 커널
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2914

Leave your greetings.

[로그인][오픈아이디란?]

«Prev  1  Next»

RSS HanRSS
Blog Image
webdizen
이 곳은 컴퓨터에 대해 연구하고, 공유하고, 소통하기 위한 연구실입니다. 개인적으로는 OLAP, Data Mining, Semantic Web, Data Modeling에 대해서 연구하고 있습니다.

Categories

전체 (2998)
Webdizen (134)
Life (6)
Diary (16)
Blog (9)
IDEA (1)
Travel (10)
Book (14)
Photo (7)
Movie (7)
Music (13)
Leisure Sports (10)
Funny (5)
Hardware (119)
Software (120)
Windows (5)
Unix & Linux (119)
Installation (4)
Kernel (10)
System (34)
Develop (22)
X-Window (0)
Applicaton (31)
Security (4)
Framework (2)
Hadoop (2)
Programming (805)
Algorithm & Data Structure (1)
Assembly (38)
UNIX/Linux C (95)
C++ (128)
STL (4)
Java (38)
Win32 API (92)
ATL/COM (44)
MFC (151)
.NET (26)
WCF/WPF (4)
C# (28)
Network Programming (17)
Database Programming (12)
OpenGL / DirectX (13)
Multimedia Programming (0)
Game Programming (21)
Parallel Distributed Progra... (0)
Reverse Engineering (0)
Debugging (9)
Python (1)
Ruby (1)
Ruby on Rails (1)
QT (4)
GTK (0)
JSP (0)
PHP (6)
ASP.NET (6)
ASP (3)
Development (28)
Useful Library (2)
Data Modeling (0)
Database (105)
Oracle (4)
MSSQL (41)
MySQL (2)
Data Warehouse (2)
Data Mining (3)
Network (66)
Web (78)
DHTML (4)
XHTML (1)
Javascript (1)
CSS (1)
AJAX (9)
XML (11)
Flex (1)
Silverlight (3)
Security (91)
DoS (1)
Kernel (10)
Scanning (3)
Sniffing (0)
Spoofing (4)
Overflow (28)
Web (11)
Shell (10)
Format String (14)
Window (2)
Embedded (70)
Multimedia (27)
Mobile (14)
Graphic (24)
Management (633)
Knowledge (581)
Hadoop (0)

Notice

  • 메타 블로그 사이트에 등록
  • 새해 맞이 블로그의 변화
  • 블로그 명칭 변경
  • 도메인(www.webdizen.net) 구...
  • TEXTCUBE 1.6.1로 업그레이드...

Tags

  • TEXTCUBE
  • 점유율
  • String Conversion
  • Google api
  • 아키텍처
  • SOA
  • Migration
  • 삼척캠퍼스
  • CEdit
  • 웹 호스팅
  • MIB
  • SendMessage
  • 리스트
  • Error Code
  • XML Web Service
  • Scanner
  • stringstream
  • Browser detection
  • 데이터와 형
  • 신발

Recent Articles

  • ASCII Code의 CRLF 제거 방법.
  • Hadoop 에서 c++ API 이용시....
  • Ubuntu Linux에서 Hadoop 구....
  • 내 심장을 한껏 뛰게한 "국가....
  • 스타 스키마 데이터베이스 설....

Recent Comments

  • 그리고 혹시 해외여행자보험....
    kim 11/05
  • ★★실제 바다게임장과 똑같....
    asdf 11/04
  • sbsyama.co.to← 짱5000만당....
    asdf 11/04
  • ♡KicaZ??o(???) 바카라사....
    fdsf3fass 11/03
  • 온라인카지노 ▶ http://usa8....
    asdfdfe 11/03

Recent Trackbacks

  • 파일 열기/저장하기 CFileDialog.
    은마군의 나태블록 02/11
  • World IT Show 2008.
    상우 :: Oranzie's BLOG 2008
  • cvs서버 설치하기.
    3인3색 2008
  • 속속 공개되는 Google Chart....
    PHP와 Web 2.0 2007
  • 마방진을 구하는 프로그램.
    Oranzie's BLOG 3 2007

Archive

  • 2009/09 (3)
  • 2009/08 (1)
  • 2009/03 (1)
  • 2009/02 (9)
  • 2009/01 (13)

Calendar

«   2009/11   »
일 월 화 수 목 금 토
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30          

Bookmarks

    • Administration
      • IIS.NET
      • NTFAQ
      • OS의 모든 것
      • 리눅스포털
    • Database
      • SQL Server Central
      • SQL Team
    • Development
      • .NET Heaven
      • ASP Alliance
      • ASP.NET 2.0
      • Bullog.net
      • C# Corner
      • C++ (C PlusPlus.com)
      • C++ Reference
      • CodeGuru
      • CodePlex
      • DebugLab
      • Dev Articles
      • Devpia
      • DotNet Junkies
      • DotNet Zone
      • Driver Online
      • GOSU.NET
      • HOONS 닷넷
      • Joinc 팀블로그
      • KOSR
      • MSDN Home Page
      • OSR Online
      • Sky.ph - 개발자 커뮤니...
      • TAEYO.NET
      • The Code Project
      • WindowsClient.net
      • 김상욱의 개발자 Side
      • 조인시 위키
    • Human Networks
      • belief21c's e-space
      • I think I can
      • Invisible Rover's Blog :D
      • Rodman®
      • ■ Feel So Good~! ■
      • 까만 나비
      • 나를 가꾸는 시간.
      • 나만의 즐거움~~!
      • 단녕
      • 상우 :: Oranzie's BLOG
    • Information Technology
      • Microsoft TechNet
      • 지디넷코리아 - 글로벌...
    • Security
      • FoundStone
      • milw0rm
      • NewOrder
      • OpenRCE
      • Phrack.org
      • Reverse Engineering b1...
      • Reverse Engineering Team
      • RootKit
      • SecurityFocus
      • SecurityXploded by Nag...
      • Wow Hacker
      • Zone-H
Textcube
Louice Studio Inc.
Powered by Textcube. Original designed by Tistory.