안녕하세요? 허니입니다. 운영체제에서 가장 중요하다고 말할수 있는 인터럽트에 대해 리눅스 기반으로 자세히 포스팅 하려고 합니다. 예전 포스팅에서도 인터럽트에 대한 설명이 있었습니다. 간단하게 인터럽트가 뭐지? 정도만 알고 싶으면 이전 포스트를 보시면 더 편하실꺼라고 생각합니다. 학생이나 연구원분들에게 많은 도움이 될 것이라고 생각하며 언제든지 질문은 환영입니다.


오늘의 주제

 

 인터럽트(Interrupt)

 인터럽트 컨트롤러(Controller)

 인터럽트 벡터(Vector)

 CPU 보호(Protection)

 

 

 
현대 컴퓨터는 대부분 Interrupt-driven 기법으로 인터럽트를 활용합니다. 이 기법은 정상적인 프로그램의 실행 도중 발생한 사건을 해결하기 위해 잠깐 다른 부분을 실행한 이후에 다시 원래 실행하던 것을 계속해서 실행합니다. 대표적으로 I/O처리로 외부 장치들과 의사소통하기 위해서 Interrupt라는 방식을 사용합니다. 이 Interrupt는 CPU가 정신없이 일하고 있을 때, IO 장비들이 당장 작업이 필요할 때 Interrupt라는 신호를 줌으로써 CPU에게 알리는 것입니다. 이와 대조적으로 예전 Apple같은 경우 Polling이라는 방식을 썼었습니다. 이 방식은 CPU가 한 명령어(Instruction)이 끝날 때마다 I/O 적업을 검색하여 작업할 I/O가 있는지를 살펴보는 기법이었습니다. 매우 비효율적이라고 할 수 있습니다. 당연히 Interrupt 방식이 효율적입니다. 그 반대급부로 Interrupt는 구현이 복잡하다는 단점이 있습니다. 구현이 간단한 Polling의 경우, 먼저 스캔하는 IO 슬롯이 자연히 높은 우선 순위를 가지게 됩니다. Interrupt의 경우 우선순위는 하드웨어적으로 어떻게 Interrupt를 구현하느냐에 달려있습니다. CPU의 pin들중에 하나에 interrupt request있습니다. I/O 장비들 중에서 interrupt가 일어나면 이 line에 신호가 걸리게 되고, CPU는 machine cycle을 돌던중에 마지막에 이러한 신호를 체크하게 됩니다. 이때 interrupt가 있다면 interrupt handler로 제어를 옮깁니다.

 
Interrupt가 들어왔을 때 처리해주는 코드를 interrupt handler라고 합니다. CPU는 interrupt가 들어오면 이 interrupt handler를 실행한후에 언제 그랬냐는 듯이 다시 이전 프로그램을 실행합니다. wmr, 현재 실행중인 프로그램을 방해하지 않으면서 효과적인 I/O 성능을 달성합니다. real mode에서는 상대적으로 이러한 interrupt의 처리가 간단하였습니다. CPU는 interrupt가 오면 주소 0번에서부터 시작하는 interrupt vector table을 참조하여 해당 주소로 점프하기만 하면 되었습니다. 이와같이 I/O device가 필요할 때 CPU에게 interrupt를 걸 수 있지만, 때로는 이러한 interrupt들을 무시해야할 때가 있습니다. 이럴 때 cli 와 같은 instruction을 사용하면 IO장비들로부터 오는 interrupt를 무시할 수 있습니다. 이것을 interrupt disable한다고 합니다. 반대로 sti instruction에 의해서 다시 interrupt enable할 수 있습니다. 그러나 이 명령들이 모든 종류의 interrupt들을 무시하게 해주는 것은 아닙니다. instruction에 의해서 무시될 수 있는 interrupt들을 maskable interrupt라고 하고, 그렇지 않는 것들을 non-maskable interrupt라고 합니다. 인텔 CPU에는 INTR pin말고도 NMI pin이 있습니다. 이것은 nonmaskable interrupt의 신호가 들어오는 pin입니다. power failure같은 interrupt는 매우 중요한 interrupt이기 때문에 NMI에 속합니다. 즉, 무시할 수 없는, cli/sti instruction에 영향을 받지 않는 interrupt입니다. Maskable interrupt와 NMI외에도 CPU내부에서 발생하는 예외처리가 있으며 외부에서 발생하는 interrupt들과는 달리 CPU내부에서 발생하는 신호들입니다. instruction을 실행하다가 만나게 되는 문제점들에 대해서 CPU가 스스로 발생시키는 신호인 것입니다. 즉, 예외처리는 항상 instruction과 동기화되어 발생한다는 점 때문에 synchronous interrupt 라고 부르기도 하고, 반대로 NMI와 maskable interrupt를 IO장비에서 아무때나 전달되어오는 신호이기 때문에 asynchronous interrrupt라고 부르기도 합니다.

 Asynchronous interrupt

 Maskable interrupt

 INTR pin으로 들어옴.

cli/sti 로 금지시킬 수 있다.

IO장비에서 오는 모든 인터럽트들.

 NMI

NMI pin으로 들어옴.

 Synchronous interrupt

 exception

CPU내부에서 발생. page fault등.

 
interrupt라는 용어가 어떤 경우에는 이 3가지 종류의 신호들을 모두 가리키기도 하고, 때로는 exception을 강조하여 interrupt는 maskable interrupt와 NMI만을 뜻하기도 합니다. 주로 과거에 interrupt라는 단일 용어로 쓰였었는데, 386이후부터는 VM등의 영향으로 CPU의 control unit이 내부적으로 처리해야할 상황이 많아지면서 (각종 fault들) 최근에는 exception과 interrupt를 구분해서 쓰이는 경향이 있습니다. 이 포스팅에서도 exception와 interrupt를 구분하여 쓰도록 하겠습니다만, 가끔 그렇지 못한 경우도 있지만 이전 포스팅을 보셨다면 어렵진 않으니 문맥을 보고 잘 판단하시기 바랍니다. 예외처리나 인터럽트가 걸리면 기본적으로 CPU는 자신이 현재 실행중이던 곳의 주소인 EIP를 스택 (커널 모드 스택)에 저장하고, 해당 인터럽트나 예외를 처리합니다. 인텔 매뉴얼에서는 exception을 이 저장되는 EIP의 값에 따라서 다음과 같이 정의하고 있습니다.
 
 - fault : fault란 발생한 사건을 복구하고 다시 재시작할 수 있는 상황들입니다. 따라서 이 경우 스택에 저장된 EIP에는 fault를 발생시킨 해당 instruction을 가리키고 있습니다. 따라서 fault handler가 끝나고 복귀할 때는 해당 instruction을 다시 실행하게 됩니다. 현대의 CPU들은 이러한 이유로 실행하다가 중지된 instruction을 undo 하는 기능을 가지고 있습니다. (나중에 더 자세히 살펴볼 기회가 있을지...) 대표적으로 page fault를 생각할 수 있습니다.
 - trap : trap은 해당 instruction이 종료되어서 다시 실행될 필요가 없는 경우, 그 다음 instruction의 주소를 스택에 넣게 됩니다. 따라서 이 trap을 처리한후 돌아와서는 그 다음 instruction을 실행하는 것입니다. 대표적인 용도로 디버깅을 들 수 있습니다. 매 instruction이 끝나고나서 그 결과를 보기 위해서 사용될 수 있습니다. 또는 breakpoint의 설정등에 사용됩니다.
 - abort : 이것은 심각한 에러로 인하여 더 이상 진행이 될 수 없는 상황에서 발생합니다. 이때는 스택의 eip에는 의미없는 값이 저장될 수도 있고, 프로세스가 종료되어야만 하는 상황입니다.
 
이외에 INT instruction에 대해서도 알 필요가 있습니다. INT(interrupt)라는 명령어는 소프트웨어에서 직접 예외처리하거나 interrupt를 일으킬 수 있게 해주는 명령입니다. 이것은 system call을 구현할 때와 같은 경우에 필수적으로 필요한 기능입니다. 이런 경우를 인텔 매뉴얼에서는 Software-generated interrupts라고 하고 있습니다. 이 부분에 대해서는 뒤에서 좀더 자세히 살펴보도록 하겠습니다.
 
 Interrupt controller
PC에서는 interrupt의 구현을 위해서 인텔 8259A 칩을 사용합니다. 이러한 칩을 PIC (programmable interrupt controller) 라고 하는데, 이 PIC의 역할은 다른 device controller로부터 interrupt신호를 받아 우선순위가 높은 신호를 CPU에게로 전달해주는 것입니다. 키를 눌렀다든지 타이머 시간이 다 되었다든지, DMA전송이 끝났다거나 패킷이 도착하는 등 인터럽트로 들어옵니다. device가 IRQ선에 신호를 주면 PIC은 vector를 I/O port에 쓰고, INTR핀을 통해 CPU에 인터럽트를 보냅니다. 그리고 CPU가 ack하기를 기다렸다가 INTR선을 clear합니다.


original PC나 XT에서는 하나의 8259칩을 사용하였는데, 위의 그림에서 보시다시피, 최대 8개까지의 장치를 연결할 수 있었습니다. 8259A 는 이들을 직렬연결(cascade)을 통해 최대 64개의 장치까지를 연결할 수 있게 되어있습니다. AT이후부터는 이 8259A 2개를 연결하여 총 16개의 interrupt를 처리하고 있습니다. 여기서 눈여겨볼 pin은 IRQ0부터 IRQ7까지의 외부 device controller와 연결되는 IRQ선과, CPU의 INTR pin에 연결되어 interrupt를 요청하는 INT선, 그리고 CPU로부터 interrupt에 대한 ack를 받는 INTA pin입니다. 개념적으로 나타내면 다음과 같이 그릴 수 있습니다.


위에서 PIC 2개를 직렬연결(cascade)하였음을 볼 수 있습니다. 8259A 칩은 priority에 따라서 IRQ선으로 오는 신호를 처리하기 때문에, 위의 예에서의 우선순위는 0,1,8,9,10,..14,15,3,4,5,6,7 임을 알 수 있습니다. 또 다른 NMI pin은 nonmaskable interrupt를 받는 pin입니다. PIC은 또한 CPU에게 어느 장치가 interrupt를 일으켰는지를 알려줄 interrupt vector를 넘겨주어야 합니다. 이것을 위해서 PIC는 들어온 선의 신호를 미리 지정된 번호(interrupt vector)로 바꾸어 IO공간에 써넣게 됩니다. 보통 많은 OS들이 PIC을 초기화할때 보통 IRQ선 번호+32 를 씁니다. 즉, IRQ0번은 32번 interrupt vector에 해당합니다. 0에서 31번까지는 인텔이 exception을 위한 번호로 쓰기때문에 충돌을 피하기 위함이죠. 이러한 IRQ와 vector간의 mapping은 PIC에 입출력 명령을 써서 programming할 수 있습니다. 이제 8259A의 동작을 살펴봅시다. 8259A는 IRR(Interrupt Request Register) 라는 레지스터를 가지고 있습니다. 이 레지스터는 8bit로 이루어져있으며, 각 bit는 각 IRQ선에 대응됩니다. 어느 한 IRQ선에서 신호가 들어올 때, 정확히는 신호의 rising edge가 파악되었을 때 해당 bit는 1이 됩니다. 또다른 8bit의 IMR(Interrupt Mask Register) 라는 레지스터는 각 IRQ선에 대해서 개별적으로 masking을 할 때 사용됩니다. IRR과 not(IMR)을 AND시킴으로써 masking이 이루어 집니다. 또한 ISR(In Service Register)라는 8bit의 레지스터가 있습니다. 이 register는 들어온 interrupt가 CPU에게 전달되었을 때 (CPU에서 INTA선을 타고 ack가 왔을 때) 1이 되고, CPU가 EOI (End of Interrupt)신호를 보내올 때 0이 됩니다. IRR에 있는 요청들중에서 우선순위가 가장 높은 것이 ISR에 전달되는것입니다. 즉, ISR에 있는 '1'은 해당 interrupt를 CPU가 처리중임을 표시합니다. 따라서 우선순위가 낮은 IRQ선에서 신호가 들어올 때, ISR의 그보다 높은 bit들중 '1'이 있을 때 그 interrupt의 처리는 미루어집니다.
 
1. ISR, IRR, IMR 이 모두 0입니다.
2. IRQ3에 신호가 실립니다.
3. IRR의 3번째 bit가 '1'이 됩니다.
4. IMR의 3번째 bit가 0이므로, IRR의 3번째 bit는 다음 회로의 input으로 들어갑니다.
5. ISR의 모든 bit가 0이므로, 즉, 처리중인 더 높은 우선순위의 interrupt가 없으므로, INT선에 신호를 줍니다. 즉, CPU의 INTR선에 신호가 들어갑니다.
6. CPU는 INTR의 신호를 감지하고 INTA신호를 줍니다.
7. IRR중 가장 높은 우선순위가 3번 bit이므로 ISR의 3번째 bit를 set합니다.
8. CPU가 두 번째 INTA신호를 줍니다.
9. ISR의 가장 높은 3번 bit에 해당하는 interrupt vector를 IO공간에 씁니다.
10. INT신호를 끄고, IRR의 3번째 bit는 0으로 reset합니다.
11. 이후에, CPU는 처리를 마친후 EOI 신호를 주고, 이것은 ISR의 3번째 bit를 reset합니다.
 
예를 들어보면 IRQ 1,2,5번 이 발생했을때 IRR은 00100110 이 되고 이중 우선순위가 가장 높은 1번 IRQ가 IMR에도 안걸리기때문에 ISR로 전달됩니다. 만약 IMR에 걸렸더라면 IRQ2번이 전달되겠죠. 만약 ISR에 0번 IRQ가 처리중이라면 ISR로 전달되지 못하겠지만, 그렇지 않다면 ISR로 전달됩니다. 예를들어 현재 ISR이 01000000 로서 6번 IRQ를 처리중에 있다면 1번IRQ가 전달된후에 01000010 이 됩니다. 물론 PIC에서 이루어지는 masking은 각 IRQ선에 대해서 개별적으로 이루어질 수 있습니다. 이 masking은 cli에 의한 CPU의 interrupt disable과는 별개입니다. 또한 EOI역시 주의하세요. 인터럽트 핸들러는 EOI를 전달하여야 합니다. 그렇지 않다면 ISR에 여전히 처리중으로 남게되고 IRR의 그보다 우선순위가 낮은 인터럽트들은 ISR로 전달되지 못하게 됩니다. 보통 IRET전에서 EOI를 보내주죠. 더 자세한 내용은 8259A data sheet를 참조하시기 바랍니다. 요즘같은 MP환경에서는 인터럽트 처리를 위해서 APIC을 사용합니다. APIC은 멀티코어등의 mp머신들을 위한 인터럽트 처리 시스템입니다. 인텔환경에서는 각 코어는 LAPIC (local apic)을 가지고, 전체 시스템은 외부 인터럽트를 처리하는 하나이상의 IO apic을 가집니다. 외부 인터럽트는 IO apic을 통해서 local apic으로 전달되는 구조죠. 특이할만한 사실은 local apic은 각 CPU별로 접근이 된다는것인데, 즉 해당 물리메모리주소가 CPU-local이라는 얘깁니다. (디볼트로 0xFEE00000 에서 시작하는 local apic영역) 그래서 이 같은 주소에 접근하더라도 각 CPU는 자신만의 CPU번호를 가져오게되고, 이것이 CPU번호로 쓰일수 있습니다. 물론 IO apic은 일반적인 다른 메모리와 같이 모든 코어에 대해 global합니다. 참고로 첫번째 IO apic의 주소가 0xFEC00000 입니다. apic은 IO port를 가지지 않으며 이 영역에 직접 값을 읽고써서 접근합니다. 8259A는 이제 IO apic에 연결되어서 인터럽트를 처리하게 됩니다. IO apic은 전달받은 인터럽트를 어느 CPU에 전달할지등을 결정하게 되는 것이죠. lapic은 또한 IPI를 처리하는데도 쓰이고, 타이머도 가지고 있지요.
 
 
 Interrupt vector
이러한 모든 interrupt나 exception들은 0에서 255까지의 숫자로 구분됩니다. (할당됩니다.) 이러한 숫자를 interrupt vector라고 부릅니다. PC에는 interrupt vector table이 있어서 이 table에서 각 interrupt가 들어올 때 그것들에 대한 vector번호를 가지고 처리할 handler의 주소를 얻을 수 있게 되어있습니다. (386이후에는 기본적으로 같지만 좀더 복잡합니다.) 따라서 모든 interrupt나 exception들은 vector값을 가지고 있으면서, 해당 interrupt나 exception이 발생하면 vector table에서 handler의 주소를 찾아서 실행하게 되는 것입니다. 다음 테이블은 각 interrupt나 exception에 vector번호가 어떻게 할당되어있는지를 보여줍니다.


여기서 0부터 31번까지의 vector 번호가 예약되어 있음을 볼 수 있습니다. 이중 2번에 NMI가 할당되어 있음을 알 수 있습니다. 즉 NM와 exception은 0~31번에 할당되어 있습니다. 따라서 maskable interrupt들은 32번 이후로 매핑이 가능합니다. 이러한 매핑은 APIC을 통해서 변경할 수 있게 됩니다. 따라서 OS에 따라서 매핑은 차이가 날 수도 있는 것입니다. Linux의 경우 IRQ번호+32번 vector에 각 IRQ들을 할당하고 있습니다. 즉 32번은 IRQ 0 번에 할당되어있는 것입니다. x86에서는 예외를 20여개정도 일으키는데, 각 경우마다 CPU의 동작이 조금씩 차이가 나기도 합니다. 이를 좀 살펴보면, 위의 표에서 알아두어야할 주요 exception은 #GP, #PF 정도입니다. 13번 #GP의 경우 intel의 protected mode에서의 보호정책을 위반하였을 때 일어나는 exception으로 이 표에 들어가지 못한 온갖 잡다한 에러들의 경우가 모두 포함되는, 쉽게말해 그외의 모든경우들..이라고 할수 있겠습니다. 윈도우에서 자주보던 General Protection Violation입니다. :-P 또 14번 #PF는 우리 눈에 익은 page fault입니다. 가상메모리에 관련하여 이 page fault와 그 handler를 잘 이해하는 것이 중요합니다. #SS는 원래 세그멘테이션 관련하여 자라나는 스택에 대한 exception인데 리눅스와 같이 세그멘테이션을 쓰지 않는다면 볼일이 없겠군요. 리눅스에서는 그런 경우 역시 모두 #PF로 처리됩니다. 하지만 이와 같이 스택이 넘치는 경우, 혹은 자라나는 스택에 대한 처리등을 통해서 VMA를 더 잘 이해할 수 있을 것입니다. #GP를 비롯해 많은 예외들이 SIGSEGV로 처리되는것을 볼수 있습니다. 디버깅 관련해서는 #DB와 #BP가 있습니다. 각각 보통 single-step exception 과 break point exception 이라고도 불리는데, 둘다 SIGTRAP이라는 시그널로 전달되는것을 알수 있죠. 이전에 하드웨어적인 지원이 없을때 int3 명령으로 (#BP) 디버거를 구현했었습니다. (물론 지금도 유효) int3이 한바이트짜리 명령이라 해당 지점의 명령의 op코드를 이 int3로 바꿔치기해넣는 방식이었는데, 이후에 HW지원이 들어오면서 새롭게 #DB가 추가되었습니다. 그래도 호환성이나 유용성등의 이유로 여전히 #BP도 많이 쓰죠. 이 두 예외와 다른 디버깅 레지스터들이 디버거에 의해서 사용됩니다. 이런 디버깅 레지스터들이 instruction breakpoint와 data breakpoint를 둘다 지원하며 이런 지원이 없을때 instruction breakpoint구현하기는 좀 곤란한데, 코드를 수정해야 하기 때문이죠. 그런 방법으로도 불가능한 것이 ROM과 같은 영역에 대한 instruction breakpoint인데, 그래서 또한 이러한 하드웨어 지원이 필요합니다. 4개까지의 주소를 지원하는데, 가상주소를 가지는 DR0부터 DR3까지 레지스터가 debug address register, DR4, DR5는 reserved, DR6는 status register, DR7은 control register입니다. 그외의 자원으로는 EFLAGS의 Single-step flag (TF), Resume flag(RF), 그리고 잘 안쓰지만 TSS의 Trap bit도 있습니다. 물론 이런 자원들은 모두 privileged입니다. breakpoint와 별도로 single-stepping을 지원하는 것을 볼수 있네요. 아직 많은 디버거들이 op-code를 patch하는 방식으로 int3을 끼워넣어서 instruction breakpoint를 구현하는것 같습니다. 이 방법으로 하면 모든 쓰레드가 해당 포인트에 hit한다는점이나 watchpoint는 구현할수 없다는것등이 제약점이 됩니다. 반면 HW를 활용한다면 기본적으로 쓰레드당 break point를 설정하게 된다는점과 watchpoint의 구현이 쉽다는것등이 좋은점입니다. 이러한 exception들은 리눅스 등 유닉스 시스템에서는 보통 현재 process에게로 전달됩니다. exception의 경우 현재 실행중이던 process에서 발생한 것이기 때문에 interrupt와 달리 현재 process에게 signal을 보내는 것으로 처리할 수 있습니다. 그렇게 함으로써 해당 process가 처리하도록 하기 때문에 커널입장에서는 exception은 손쉽고 빠르게 처리할 수 있습니다. 이처럼 예외의 경우, 커널은 signal로 해당 process에게 전달해주기 때문에 상대적으로 쉽고 빠르게 처리할 수 있지만, interrupt의 경우는 그렇지 않습니다. 왜냐하면 exception과는 달리 interrupt는 일어난 시점의 실행중이던 process와 아무런 관련이 없고, 전달된 interrupt를 해당 process에게 전달해주어야 하기 때문입니다. 이런 이유로 interrupt처리는 좀 더 복잡해집니다.
 

 CPU Protection
이러한 인터럽트중에서 중요한 것으로 timer interrupt가 있습니다. timer정도가 뭐가 중요하냐고 반문하실지 모르겠지만, 이 timer interrupt 기능은 multitasking을 위한 기본적인 조건으로 HW는 일정한 시간간격마다 interrupt를 발생시킬 수 있어야 합니다. 그래야만 time sharing system 을 구현할 수 있기 때문입니다. 즉 우리는 OS가 항상 control 을 가질수 있게끔해야하는데, 그래야 user가 무한루프에 빠졌을때도 OS가 시스템을 유지할수 있으니까요, 이를 위해 필요한것이 timer 입니다. 이를 통해 CPU를 보호하는거죠. 당연히 타이머를 조작하는 명령도 priviledged입니다. 보통 타이머가 0번 IRQ죠. 그만큼 중요합니다. 타이머얘기가 나온 김에, 쉬운듯 쉽지않은 타이머의 세계를 들여다 봅시다. 먼저 HW적인 clock source를 살펴봅니다. (1) Real-time clock (RTC) 오래된 방식이죠. 전원이 나가있을때도 동작하고 물론 wall-clock입니다. 그리고 Programmable Interval Timer (PIT), (2) HPET, RTC보다 높은 resolution을 제공 (3) APIC, APIC 타이머 (4) time stamp counter (TSC)는 cpu cycle을 이용한 방식 등이 있습니다.  TSC 경우 cpu안에 cycle을 세는 64bit register가 있고 이 값을 읽어오게 됩니다. 가장 resolution이 높다는 장점이 있는 반면, 멀티코어나 SMP와 같은 현대의 환경에서는 각 코어별로 skew가 존재한다는 점때문에 각 코어간의 시간을 비교할때에는 보정이 필요합니다. 또한 DVS와 같은 cpu frequency를 바꾸는 기술을 쓴다면 역시 그것도 고려한 보정이 필요합니다. 또한 타이머는 아닌 단순 clock source입니다. 이처럼 각 소스는 각각 장단점과 resolution, 그리고 SW에게까지 전달되는 방식이 있습니다. 이렇게 전달된 타이머 이벤트가 실제로 응용프로그램에 전달되기까지 여러 계층을 거쳐서 올라가게 됩니다. 당연히 여기에서 delay가 발생합니다. 그렇기 때문에 여러 계층에 따라서 자신이 가질수 있는 timer resolution이 달라지게 됩니다. 물론 커널은 (혹은 hypervisor)는 직접적으로 타이머를 이용할수 있지만 어플리케이션은 다릅니다. 예를들어 리눅스에서 setitimer로는 HRT없이는 jiffie의 한계 아래로는 내려갈수가 없습니다. 또한 심지어 이벤트가 사라질수도 있습니다. 가상화환경이라면 물론 운영체제 조차 timer resolution이 커지게 됩니다. hypervisor가 event를 전달하는 과정이 끼어들기 때문이죠. 따라서 컴퓨터가 가진 모든 종류의 timer event라는것들은 정확히 그 시간에 발생하는것이 아니며, 단지 지정된 시간이 지난후에 발생한다는점만이 보장될뿐입니다. 따라서 유저레벨에서 여러 타이머 이벤트의 skew나 lost를 경험하는 것은 흔한 일입니다. 이런 논의에서 볼수 있듯이, 기계가 시간을 다룬다는 것은 무척 어려운 문제가 됩니다. 따라서 real-time과 같은 특수한 영역이 생깁니다.

 

Vector no.

Mnemonic

Linux의 handler

설명

Signal

0

#DE

divide_error()

DIV나 IDIV가 0으로 나누려고 하거나 결과값이 표현하기에 너무 클 때 발생

SIGFPE

1

#DB

debug()

eflags의 TF 플래그가 설정되는등의 디버깅을 위한 exception조건들이 있을 때 발생합니다.

SIGTRAP

2

 

nmi()

NMI

 

3

#BP

int3()

INT3 명령으로 발생하는데, 보통 디버거가 breakpoint를 만들기 위해 삽입해 넣습니다.

SIGTRAP

4

#OF

overflow()

INTO명령은 EFLAGS의 OF플래그가 켜져있을 때 overflow가 발생하면 이 exception을 발생시킵니다.

SIGSEGV

5

#BR

bounds()

BOUND명령이 operand가 주소 범위를 벗어났을 때 발생시킵니다.

SIGSEGV

6

#UD

invalid_op()

잘못된 op-code일때.

SIGILL

7

#NM

device_not_available()

x87 FPU, MMX,등의 장비가 사용준비가 되지 않았을때

SIGSEGV

8

#DF

double_fault()

CPU가 예외를 처리하는 중인데 예외가 다시 발생했을 경우. 보통 이런 경우 둘을 serial하게 처리할 수 있지만, 간혹 그럴 수 없는 경우가 발생하는데, 이런 경우에 발생.

SIGSEGV

9

 

coprocessor_segment_overrun()

최근 인텔의 프로세서에서는 발생하지 않지만, 예전 386에서만 387에서 문제가 발생했을 때 발생

SIGFPE

10

#TS

invalid_tss()

TSS가 잘못되었을 때.

SIGSEGV

11

#NP

segment_not_present

segment descriptor나 gate descriptor의 present flag가 꺼져있는 경우. 즉 존재하지 않는 세그먼트를 참조하는 경우에 발생

SIGBUS

12

#SS

stack_segment()

존재하지 않는 stack segment를 SS레지스터에 load하려는 경우거나 스택 세그먼트의 한계를 넘어서는 경우.

SIGBUS

13

#GP

general_protection()

보호모드에서 보호 규약을 어겼을때.

SIGSEGV

14

#PF

page_fault()

주로 참조하는 주소에 대한 페이지가 없거나 하는등의 paging 매커니즘의 규약을 어겼을때

SIGSEGV

15

 

 

(인텔이 예약)

 

16

#MF

coprocessor_error()

CR0의 NE flag가 켜져있을 때 발생하는데, x87 FPU가 에러를 발견했을 때 발생.

SIGFPE

17

#AC

alignment_check()

operand의 주소가 정렬되어 있지 않을때

SIGSEGV

18 - 31

 

 

(인텔이 예약)

 

 

 

목 차

 운영체제를 시작하는 분들을 위해

 운영체제의 역사와 구체적으로 어떤 일들을 수행하나요?

 운영체제의 목표와 역할이 미래에도 계속 증대될까요?

 운영체제를 이해하기 위해 필요한 기본 개념들은 무엇인가요?

 운영체제 프로세스 생애주기(Process Lifecycle)는 무엇인가요?

 운영체제 프로세스 관리(Process Management)는 어떻게 하나요?

 운영체제 프로세스(Process)의 처리 속도는 어떻게 높일까?

 운영체제 스레드(Thread)는 무엇인가요?

 운영체제 스케줄링 (Scheduling)은 어떻게 하나요?

 운영체제 스케줄링 알고리즘의 비교 기준이 있나요?

 운영체제 프로세스 동기화가 무엇인가요?

 운영체제에서 세마포어(Semaphore)란?

 운영체제 메모리 관리(Memory Management)는 어떻게 하나요?

 운영체제 메모리 분할 방법은 어떻게 하나요?

 운영체제 가상메모리(Virtual Memory)는 무엇인가요?

 운영체제 Page in/out, Swapping 등이 Page Table Entry와 어떤 관계인가요?

 운영체제 메모리 관련해서 알아야 할 개념은 어떤것이 있나요? (TLB, Locality, Working Set, Overlay)

 운영체제 Page replacement (페이지 대체) 알고리즘이란?

 운영체제 파일시스템(File System)은 어떻게 운영되나요?

 운영체제 파일시스템 내부구조는 어떻게 되나요?

 운영체제 디스크 공간 할당(Disk Space Allocation) 알고리즘과 효과적 알고리즘의 판단 기준은?

 운영체제 파일시스템에서 접근 시간, 디스크 스케줄링을 위한 알고리즘, I/O 시스템이란?

 리눅스 인터럽트 (Interrupt)에 대해 자세히 설명해 주세요.

 인텔 구조에서 운영체제 가상 메모리 (Virtual Memory)는?

 운영체제 아키텍처의 종류는 얼마나 있나요?

 

 

+ Recent posts