[Kernel of Linux] 10. Interrupt (3)
Kernel/Theory

[Kernel of Linux] 10. Interrupt (3)

반응형

지난 강의 요약 - Interrupt (2)

Interrupt를 처리할 때 발생 가능한 문제점이 있다는 것을 배웠다. mth IRQ line을 처리하는 CPU가 아무도 없다면 그냥 들어가서 처리하면 되고, 반대로 이미 처리하고 있는 CPU가 있을 수도 있다. 이럴 때는 뒤늦게 들어온 CPU 입장에서는 line state를 확인하고 PENDING bit을 설정하고 나가면 된다. 왜냐하면 이미 처리 중인 CPU에게 처리할 것이 더 있으니 해당 라인에 요청된 ISR 마치고 오라고 말하는 것과 같기 때문이다. 이미 ISR을 처리 중이던 CPU 입장에서 보면 handle_IRQ_event() 함수를 마치고 돌아왔더니 분명히 없애고 갔던 PENDING bit이 설정되어 있으니 다시 ISR을 처리하러 가면 된다.

이렇게 IRQ를 처리하는 일은 CPU의 interrupt를 disable 시키고, I/O devices의 interrupt를 block 시키고(PIC이 ACK 받기 전까지), 기존에 CPU가 처리 중이었던 process의 순서까지 뺏은 셈이다. 간단히 말해 시스템에 많은 민폐를 끼치고 있는 것이다. 그렇기 때문에 IRQ는 되도록 빠른 시간 내에 마치고 자원을 다시 돌려주어야 한다. 물론 lock을 얻고 state 같은 shared variable에 접근하는 동작을 수행하여 실질적인 부하를 주는 부분을 Critical, 그렇지 않은, 즉 lock을 돌려준 상태에서 진행하는 handle_IRQ_event() 함수를 실행하고 있는 동안은 non-Critical 부분으로 나누긴 한다.

하드웨어가 보낸 interrupt signal을 처리하는 과정을 Top-Half라고 하고, 처리 도중 소프트웨어가 후속 처리를 위해 요청한 interrupt를 처리하는 과정을 Bottom-Half라고 한다. 소프트웨어에서 후속 처리를 원하는 이유는 아무리 부하가 많이 걸리지 않는 non-Critical Top-Half 부분이라고 하더라도 ISR을 처리하는 handler의 처리 시간이 오래 걸려서야 Critical이 빨리 끝나더라도 의미가 없어지기 때문이다. 그래서 소프트웨어가 처리하려 설정한 interrupt를 SoftIRQ라고 한다.

Top-Half가 끝나고 CPU는 평소와 같이 스케줄러로부터 run queue에 존재하는 tasks 중에 priority 순으로 할당받는다. run queue 안에는 SoftIRQ를 처리하려는 process도 존재하므로 IRQ처럼 자원을 뺏은 상태에서 동작하지 않으므로 충분한 시간을 두고 처리가 가능하다.


1. Bottom-Half

Top-Half와 같이 엮어서 Bottom-Half를 보면 위의 시간축에 표시된 것과 같다.

 

Top-Half를 진행하면서 후속 처리가 필요한 ISR을 위해 SoftIRQ pending bit을 설정하고 끝낸다고 했었다. 그래서 나중에 Bottom-Half를 진행하면서 do_softirq() 함수를 호출하게 되면 함수 내에서 그 bit를 보고 거기에 대응하는 vector의 handler를 실행한다.

softirq pending bit에는 미리 약속된 순서로 역할을 정해놓았다. 새로운 것을 추가하고 싶다면 앞이든 뒤든 추가해서 구현해주면 된다.

unsigned 32bit integer인 pending 변수에 softirq_pending_bit이 설정된 값을 저장한다. 그리고 파란 박스에 있는 것처럼 비트 하나씩 검사하면서 만약 1로 세팅되어 있다면 거기에 등록된 handler를 실행하게 만들어놓았다.

SoftIRQ가 실행되어 다른 process들과 마찬가지로 실행되는 상황을 살펴보자. CPU[i]가 do_softirq() 함수를 실행해서 비트가 설정된 곳을 보니 IP() 함수를 부르도록 해서 이를 실행하는 도중이었다. 그런데 다른 CPU[k]가 같은 IRQ line에서 발생한 같은 SoftIRQ를 처리하게 되어 똑같이 IP() 함수를 호출하게 되는 상황이 발생했다.

예상했다시피 여기서 발생가능한 문제점은 IP() 함수 내부에서 사용되는 shared variable이 있다면 이는 mutual exclusion을 고려해서 작성돼야 한다는 것이다. 또한 reentrant code의 형태로 작성되어야 할 것이다. 이는 프로그래밍할 때 고려해야 할 사항이 많은 피곤한 작업이다. 그렇지만 장점도 존재하는데 바로 parallelism이 좋게 동작할 수 있다는 것이다. 아무 CPU나 softirq handler 함수를 언제든지 호출할 수 있기 때문이다.

 

2. Tasklet

먼저 Softirq와 Tasklet이 어떻게 다른지를 살펴보자.

  • softirq
    - 다른 CPU들이 동시에 handler들을 실행할 수 있음
    - 최대 처리율, 하지만 어려운 코딩 (reentrant, data access)
    - 시스템 소프트웨어 같은 것에 좋음
  • tasklet
    - softirq의 특별한 타입
    - 일반적인 softirq와는 달리 다른 CPU들에게서 같은 tasklet은 동시에 실행될 수 없음
    - 그렇기 때문에 코딩하기 쉬움 (no shared data, no reentrant)
    - 하지만 그만큼 concurrency는 떨어짐

tasklet이 동시에 실행될 수 없는 이유는 tasklet의 자료구조를 보면 이해된다. tasklet function을 실행하기 위해서는 lock을 얻어야만 가능하기 때문이다. lock 역할은 state flag가 하고 있다.

 

3. Workqueue

Bottom-Half를 다룰 handler로 3가지 옵션이 존재한다. Softirq, Tasklet, Workqueue.

Softirq, Tasklet은 handler로 function을 등록한다. 하지만 Workqueue는 독립적인 program을 등록한다.

그렇다면 어떤 것을 이용하면 좋을까? 각자의 장단점을 살펴보자.

  • softirq
    - 가장 빠름. timing critical & frequent 유저가 사용하기 좋다.
    - 하지만 코딩하기 매우 어렵다.
  • tasklet
    - softirq보다는 사용이 용이하지만, 그만큼 performance는 희생해야 한다.
    - 같은 건 안되지만 다른 종류의 tasklet은 다른 CPU들이 동시에 실행하는 것이 가능하다.
  • workqueue
    - process context(kernel thread; kthread)로 돌아간다.
      └ sleep & schedulable 가능
      └ semaphore, block I/O, 많은 메모리 사용, ... 등등 가능
    - 컨텍스트 스위치를 하는 오버헤드가 존재한다.
    - 셋 중에 가장 느린 대신에 재량권(할 수 있는 일)이 제일 많다.

각자의 장단점을 잘 파악하고 프로그래머는 상황에 맞춰 선택한 후에 디자인을 잘하면 될 것이다.

반응형