1. Interrupt
저번시간의 마지막 부분을 이어서 설명하겠다. 그전에 우선 IRQ 라인에 의해서 CPU가 인터럽트에 걸리는 3가지 경우가 존재한다는걸 알고가자. 이미 설명한 케이스이다.
1.
만약 현재 요청들어온 IRQm 라인에 일을 해주고 있는 CPU가 없으면 그냥 CPU0 가 처리해준다.
2.
CPU0가 IRQm 라인을 처리하고 있는 도중에 동일한 IRQm라인의 요처이 들어와서 CPU1이 처리해주려고한다. IRQm라인의 status는 현재 IRQ_Inprogress 비트가 세팅되어있기 때문에 이를 확인하고 IRQ_pending bit을 추가로 세팅한다음 종료된다.
3.
CPU1이 IRQm 의 handle_IRQ_event() 함수를 수행하고 이는 do-while이다. 이게 끝나면 for문에서 다시한번 IRQ_pendding bit을 체크한다. 분명 그전에 끄고 왔지만 이게 세팅되었다는 뜻은, 동일한 라인에서 또 요청이 들어왔다는 놈이고, 다른 CPU가 나에게 처리부탁을 요청했다는 의미이다. 그럼 다시 for문으로 돌아가 handle_IRQ_event() 를 수행한다.
이제 위 과정을 정리해보자.
1.
APIC의 IRQ lines에 여러 디바이스들이 물려있다. IRQm 라인을통해 특정 디바이스가 인터럽트 요청을 보낸다.
2.
APIC에서는 현재 CPI(i)와 CPI(k) 둘중 CPU(i) 를 선택한다. 따라서 CPU(i)의 counter는 0가 된다. (counter에 대한 설명은 이전 강의를 참고바람)
3.
CPUi는 irq_desc[m].status의 waiting 필드를 지우고 pennding으로 업데이트한다.
4.
이제 어떤 CPU가 해당 인터럽트를 처리할지 선택한다. 이는 위에서 설명한 케이스에 따라서 달라진다.
•
Case A : 어떠한 CPU도 IRQm을 처리하고 있지 않으면, CPU(i)가 바로 해줌. ⇒Let CPU(i) handle IRQm
•
Case B : 다른 CPU(k)가 이미 IRQm을 처리하고 있으면, irq status에 Inprogress status를 추가하고, CPU(k)에게 요청을 넘긴다. ⇒ Let CPU(k) handle IRQm
2. Race Condition
이러한 do_iqr() 함수 로직은 많은 민폐를 끼치게 된다. 여기서 말하는 민폐는 요청에 대한 ACK가 오기 전까지 PIC는 block이되고, 또 IRQ 라인은 공유 메모리로 사용되기 때문에 lock이걸리면 다른 CPU는 사용하지 못하는 것들을 말한다.
이렇게 민폐를 끼치는 영역은 주로 Critical_section 에서 발생한다. 따라서 해당 영역은 매우 신속한 처리를 필요로한다. 이러한 신속한 처리를 필요로 하는 영역을 Top-Half 영역이라고 부른다. 이는 하드웨어에 시작되는 영역이다
이에 반해 Non-Critical_section Top-Half 영역보다는 block되는 리소스같은게 적긴하다. 하지만 이 역시도 민폐를 키칠수 있는 상황이 존재한다.
만약 현재 handle_IRQ_event() 에서 처리해야하는 작업이 디스크에서 200TB를 가져와라! 라는 요청이면 어떨까?. 매우 사이즈가 큰 작업이므로 신속한 처리를 하지 못한다. 따라서 한번에 처리하지 못하는 작업을 후에 다시 처리할수 있도록 soft-iqr pending bit 를 세팅한다.
해당 비트가 설정되어 있으면 후에 do_softirq() 에 의해서 남은 작업을 다시 수행하도록 소프트웨어적으로 구현되어있다. 이러한 메커니즘은 Bottom Half 라고 부른다.
즉 Top Half는 하드웨어의해 실행되고, Bottom Half는 소프트웨어에 의해 실행되는 로직을 갖는다. Bottom Half의 실 예를 한번 봐보자.
현재 NIC 즉 네트워크 인터페이스 카드에서 인터럽트 요청이 들어왔다. 무선 랜카드 같은곳에서 현재 어떠한 패킷을 받고 해당 패킷을 처리하기 위한 인터럽트 요청일것이다.
그럼 PIC는 특정 CPU를 선택하고 CPU에서는 do_IRQ()를 호출한다. do_IRQ()에서는 초기에 ack를 PIC에 보내 다른 인터럽트를 처리할수 있도록 하고, irq_desc[IRQm] status를 업데이트한다. 메모리에 복사된 패킷을 확인하여 재조립후 하나의 스트림으로 하여 패킷 요청이 실행된다. 만약 ftp 관련 패킷이면 ftp 관련 작업을 할것이다.
위 과정은 하나의 Top-Half에서 다 수행되는게 아니다.
NIC로부터 요청이 오고, do_irq() 즉, 초기엔 Top-Half가 수행된다. Critical Top half가 수행되면서, ACK를 보내고, NIC의 패킷을 저장한 버퍼를 만들고, 거기에 패킷을 복사한다. 실제 패킷의 동작 처리는 bottom-half에서 진행되게끔 soft-iqr pending bit 를 세팅한다.
그 이후 정상적으로 다른 인터럽트들을 쭉 실행한다. 그러면서 중간중간 CPU가 처리할 작업을 우선순위를 보고 선택하게 되는데, 그때 soft-irq-pending-bit 가 세팅되어있는 놈을 보면, do_softiqr()를 실행시킨다. 여기서 실제 패킷에 대한 처리가 수행된다.
이러한 일련의 과정을 다음 그림으로 정리할수 있다
3. 정리
ISR 루틴이 수행될때 부하가 큰 작업은 신속한 처리를 위해 do_softirq() 함수를 이용해서 처리를 한다. 따라서 Top-half, Bottom-half 가 무엇인지 이제 구분할수 있다. 다음에는 Bottom-half 를 중점적으로 설명하겠다.