user mode,kernel mode,process에 관해서...
너무나 혼돈이 되어서, 혼자 공부하나, 계속 돌고 돌 뿐입니다.
도와주십시요...
user mode에서 실행중인 application program(APP1이라 칭함)이 있습니다.
그리고, 이 APP1이 호출하는 device driver module program(DD1이라 칭함)이 있습니다.
그리고, 이 DD1에서 등록한 interrupt handler routine(=ISR1이라 칭함)이 있습니다.
(1)상황1: DD1을 커널에 등록후에, APP1이 실행중입니다.
(2)상황2: APP1에서 write()명령어를 사용해서, system call을 하여, DD1이 실행됩니다.
(3)상황 3: DD1이 실행되는 도중 외부에서 hardware interrupt가 걸려서 ISR1이 실행됩니다.
(4)상황 4: DD1,APP1,ISR1가 실행을 전환하는 중간에 커널의 스케쥴러가 실행됩니다.
여기서 process와 쓰레드라는 개념이 매우 혼돈이 됩니다.
상기의 4가지 상황에서, APP1,DD1,ISR1,스케쥴러중 process는 어떤 것인가요 ?
상기의 4가지 상황에서, APP1,DD1,ISR1,스케쥴러중 쓰레드는 어떤 것인가요 ?
물론, (1)의 상황에서 APP1이 user mode에서 실행되며, 명확하게 process라는 것을 알겠습니다.
그렇다면, (2),(3),(4)의 상황에서 DD1,ISR1,스케쥴러가 kernel mode에서 실행되는데,
이러한 ”DD1,ISR1,스케쥴러”도 각각 하나의 process라고 할 수가 있는지요 ? 아니면, 쓰레드라고 할 수가 있는지요?
그리고, 위에서 스케쥴러뿐만 아니라, 커널의 여거가지 객체
(or service or module => 즉... 스케쥴러/가상파일시스템/메모리관리시스템/기타 등등)들은
커널의 일부분(커널이라는 큰 program의 sub routine)입니다.
이 커널의 일부분이 실행될 때, 커널전체를 1개의 process(또는 쓰레드)라고 하나요 ?
아니면, 각각 실행되는 sub routine(스케쥴러 등)을 하나의 process(또는 쓰레드)라고 하나요 ?
답변주시면 정말로 감사하겠습니다.
스케줄러는 timer interrupt에 의해
(1), (2)는 process context이고요
(3), (4)는 interrupt context입니다.
process가 system call을 실행하면 kernel mode에서 process context상태를 유지하는겁니다.
스케줄러는 timer interrupt에 의해
불리우므로(보통 1초에 1000번) interrupt context입니다.
그리고 Kernel은 process를 관리하는 프로그램이라고 보시면 됩니다.
/***************************************
Being the one is just like being in love.
***************************************/
/***************************************
Being the one is just like being in love.
***************************************/
커널 책 읽다보면
커널 책 읽다보면 이런 식의 문제로 가끔 헷갈리고는 하는데, 제가 이해한 대로 한 번 적어 보겠습니다.
먼저 제가 처음에 매우 헷갈려 했던 부분인데, 커널에서 무슨 일이 벌어지건, 항상 "물리적 메모리"와 "가상 메모리"의 상태가 어떤 식으로 바뀌는 지에 대해 생각하고 있어야 합니다. 스케줄러가 실행 되었거나 인터럽트 서비스 루틴이 불렸다고 해서 갑자기 가상 주소 공간이 바뀌거나 하는 것은 아니라는 점을 염두에 두셔야 합니다.
프로세스는 자원 할당의 단위이고, 쓰레드는 실행의 단위 입니다.
프로세스에 대해서 먼저 보면,
APP1 이 실행 파일이라고 한다면,
APP1 이 실행되면 프로세스가 만들어지는데 그 프로세스에 할당되는 자원을 생각하면 덜 헷갈립니다. 여러 다른 부분을 제외하고 메모리에 관해서만 생각해 보면. 독립적인 유저 주소 공간이 할당되고 커널 주소 공간이 할당 됩니다. 커널 주소 공간은 어떤 프로세스든 같은 "물리적" 메모리 주소 공간을 차지합니다. 유저 주소 공간은 당연히 서로 다른 "물리적" 메모리 주소 공간을 차지하겠죠.
따라서 각 프로세스는 똑같은 코드의 커널 영역과 다른 코드의 유저 영역을 가지고 있게 됩니다.
'APP1 이라는 프로그램의 프로세스는 독립적으로 할당 된 유저 주소 공간 + ISR1, DD1, 스케줄러 를 포함한 커널 주소 공간'이 되고,
'APP2 라는 프로그램의 프로세스는 독립적으로 할당 된 유저 주소 공간 + ISR1, DD1, 스케줄러를 포함한 커널 주소 공간' 이 됩니다.
프로그램이 실행 중 일 때 메모리 접근은 모두 가상 주소 공간으로 이루어 지겠지만, 결국 자신에게 할당된 물리적 주소 공간으로 맵핑 됩니다. 그리고 모든 프로세스는 동일한 커널 주소 공간을 가지게 됩니다. 즉 각 프로세스가 자신의 유저 주소 공간을 건드린다면 다른 프로세스는 그것을 못 보겠지만, 커널 주소 공간이 바뀌면 그 변화를 알 수 있습니다.
APP1이 실행되는 순간 만들어진 프로세스는 커널 주소 공간을 자원으로 가지고 있으므로 ISR1, DD1 , 스케줄러 그리고 커널 코드 전체가 프로세스에 할당되어 있습니다. 여기서 유저 모드로 실행 되느냐 혹은 커널 모드로 실행 되느냐는 프로세스와는 관계가 없습니다. APP1이 가지고 있는 유저 모드에서 실행되는 코드와 DD1, ISR1, 스케줄러 그리고 커널 전체 코드는 APP1 의 프로세스가 사용할 수 있는 자원이므로, DD1, ISR1, 스케줄러 자체가 하나의 프로세스가 되는 것은 아닙니다.
예를 들어 Process ID 가 1인 A 프로세스가 Thread ID가 각각 1, 2 인 a, b 쓰레드를 가지고 있다고 합시다.
커널이 a를 스케줄 해서 a의 유저 주소 공간의 내용을 실행 하다가 시스템 콜을 만났습니다. 그렇다면 트랩이 걸릴 것이고 그에 해당하는 ISR 을 통해, 커널 모드로 진입해서 커널 주소 공간에 있는 코드를 실행합니다. 여기서 일어나는 일을 좀 더 컴퓨터의 관점에서 써 보면, 트랩을 일으키면 CPU 에 있는 인터럽트 핀에 신호가 가고, 커널 모드로 전환되면서 특정 주소로 jump 합니다. 그리고는 그곳에 있는 코드를 실행하기 시작합니다. 이런 과정을 기술한 것이 ISR1, DD1, 스케줄러 등의 커널 코드이고, 보시다시피 a 라는 하나의 쓰레드의 실행 흐름에서 일어났습니다.
커널 입장에서 a 를 실행하다가 타이머 인터럽트에 의해 스케줄링이 일어난다면, 커널 모드로 전환되고 정해진 ISR 주소로 jump 가 됩니다. 그리고 주어진 시간 할당량이 지났다면 다시 스케줄링이 있는 곳으로 jump 가 일어납니다. 그러면 커널은 다음 스케줄링의 단위가 b 인 것을 알고 b로 context switch 하는 코드로 다시 jump 를 하고, 프로세스 자체는 바뀌지 않았으므로 자원 할당은 그대로 둔 채, 사용할 스택만 살짝 바꾸게 됩니다.
이 전체의 과정에서 할당된 자원이 변하는 경우는 한 군데도 없습니다. 즉 모두 같은 프로세스 내에서 일어나는 일입니다. 스케줄러가 호출된다고 해서 주소 공간에 없던 스케줄러가 갑자기 로딩 되거나 하는게 아닙니다.(물론 페이징 가능한 커널이라면 이야기는 달라질 수 있습니다만.. ;; ) 원래부터 jump 할 수 있는 주소에 있었던 것이죠. 다만 트랩을 걸지 않고 jump 한다면 보호 오류에 걸릴 뿐입니다.
쓰레드는 실행의 단위 다시 말해서 스케줄링의 단위입니다. 커널이 스케줄링 할 수 있는 것은 쓰레드(좀 더 정확하게는 커널 쓰레드)라고 보면 됩니다. ISR1, DD1, 스케줄러 등등의 코드는 커널이 스케줄링하는 단위로 볼 수 없습니다. 대부분의 경우 일반적인 프로세스에 한 개 혹은 그 이상의 쓰레드가 할당 되겠죠. 커널 코드(ISR1, DD1, 스케줄러 등등)는 쓰레드가 실행중일 때 실행 될 수도 있는 코드일 뿐입니다. 경우에 따라서 쓰레드가 유저 영역 코드는 전혀 사용하지 않고 커널 주소 공간에서만 왔다갔다 할 수 있는데 이런 게 커널 프로세스 혹은 커널 쓰레드 입니다. 커널의 경우는 실행 코드가 단일하므로 프로세스와 쓰레드 구분이 애매 합니다. 여기서 커널 쓰레드라고 하는 것은 유저 쓰레드와 커널 쓰레드 맵핑을 이야기 할 때랑은 또 다른 의미입니다. 전자의 커널 쓰레드는 실행되는 코드가 전부 커널 주소 공간 내에 있고 따라서 유저 모드에서 실행 되는 코드가 없는 실행의 단위입니다. 후자의 경우는 커널의 입장에서 스케줄링 가능한 실행의 단위를 뜻합니다. 유저 모드 프로그램에서 보이는 쓰레드와 커널 모드 프로그램에서 보이는 쓰레드가 1:1로 정확하게 맵핑하지 않는 디자인이 가능합니다. 이런 경우에 두 가지 종류의 쓰레드르 구분하기 위해서 두 용어를 도입한 것 뿐입니다. 리눅스의 경우는 현재 1:1 맵핑 이므로, 그냥 유저 레벨에서의 쓰레드 각각이 커널의 입장에서 스케줄링 가능한 단위입니다.
간단하게 요약하자면 님께서 질문하신 그 어떤 것도 자원 할당의 단위나 스케줄링의 단위로 볼 수 없으므로 쓰레드도 프로세스도 아닙니다. 다만 ISR1 이 하나의 쓰레드가 될 수는 있습니다. 예를 들어서 급하지 않은 인터럽트가 들어 왔을 때 ISR 에서 직접 처리하지 않고, 그냥 처리해야 될 것을 하나의 커널 쓰레드로 만들어서 스케줄링 해 버릴 수는 있습니다. 이런 기법은 실제로 많이 쓰입니다. 정확히 말하자면 ISR1 코드가 커널 쓰레드를 만든다라고 보는게 맞겠지요.
제가 처음 bach 책 읽을 때 헷갈려 했던 것을 헷갈려 하시는 것 같아서 나름대로 제가 이해한 과정을 적어 봤는데 오히려 더 헷갈리게 할 공산이 큰 것 같군요 ㅜㅜ 이거 쓰느라 꽤 오래 걸렸는데, 실제 코드가 있는 책을 보면 이런 모호함이 덜 하다는 말씀을 드리고 싶습니다. 리눅스에 대해 코드단으로 언급하는 책을 보면 이런 류의 오해는 잘 안생긴다고 생각합니다.
친절한 답변
친절한 답변 너무나도 감사드립니다.
일부는 이해했고, 일부는 님의 말씀처럼...좀 더 혼돈이 되네요...^^
암튼...더 공부해 보겠습니다.
참...
혹시...윗글에서 님께서 말씀하신...
"이러한 개념을 위해서 실제 code가 나와 있는 책"이 있나요 ?
감사합니다.
친절한 답변 너무나도 감사드립니다.
일부는 이해했고, 일부는 님의 말씀처럼...좀 더 혼돈이 되네요...^^
암튼...더 공부해 보겠습니다.
참...
혹시...윗글에서 님께서 말씀하신...
"이러한 개념을 위해서 실제 code가 나와 있는 책"이 있나요 ?
Daniel Bovet, Marco Cesati
Daniel Bovet, Marco Cesati "Understanding the Linux Kernel 3rd ed." O'Reilly Media
Robert Love "Linux Kernel Development 2nd ed." Novell Press
제가 본 것은 이런 것들이 있는데, 꽤 괜찮았습니다. 맘 편하게 보시려면, 밑의 책이 더 나은 듯 하구요. 둘 다 번역서 있는 것으로 압니다. 첫번째 책은 2장인가 3장인가에서 괜히 x86 어드레스 모드를 설명하는데 너무 많은 지면을 투자해서 사람 지치게 만드는 느낌이 있었습니다.
현제 제가 보고 있는
Claudia S. R., Gordon F., Steven S. "The Linux Kernel Primer: A Top-Down Approach for x86 and PowerPC Architectures" Prentice Hall
는 유저 레벨 프로그램의 소스에서부터 커널 내부 코드 소스까지 따라가는 방식이네요.
개인적으로 리눅스와는 관련 없지만,
김범준 "만들면서 배우는 OS 커널의 구조와 원리" 한빛미디어
를 보면서 직접 해 보는 것도 "취미생활" OS 공부에는 딱 좋은 것 같습니다. 재미도 있구요. x86 아키텍쳐를 low-level 하게 아는 것이 사는데 무슨 도움이 될까라는 생각이 든다면, 애시당초 안보시는게 나을 것 같구요 ^^
저도 잘 모르면서 막 추천해도 될런지 모르겠네요 ㅜㅜ
댓글 달기