인터럽트 핸들러는 빠르게 실행되어야 하면서도 동시에 많은 양의 작업을 처리해야 한다. 이 2가지 목표는 서로 충돌하기 때문에, 리눅스 커널에서는 인터럽트 핸들러의 동작을 각각의 목표를 위한 2가지 파트, halves 로 분리하여 구현한다.
즉, 인터럽트 핸들러는 top half 와 bottom half 로 구분되는데, top half 에선 빠르게 처리되어야 하는 작업이 수행되며, bottom half 에서는 비교적 느리게 수행되어도 괜찮은 작업들이 지연되어 처리된다.
어떤 작업이 top half, bottom half 에서 처리되는지 감을 잡기 위해 NIC에 패킷이 수신된 상황을 예시로 들어보자. 먼저 NIC에 패킷이 수신되면 그에 따라 네트워크 디바이스 드라이버에 의하여 인터럽트가 생성될 것이다. 이와 관련한 작업 처리를 top half 와 bottom half 로 구분하면 다음과 같다.
Top half:
- 인터럽트를 감지한다. 이를 통해 NIC는 자신이 보낸 인터럽트가 시스템에서 인지했다는 것을 알 수 있다.
- 패킷을 읽는다.
- 네트워크 디바이스와 관련한 통계를 업데이트한다. 받은 패킷 수 등을 설정한다.
Bottom half:
- 패킷과 관련한 동작을 처리한다. TCP/UDP 에 따라 다른 처리한다.
- 이를 목적지 어플레키이션, 혹은 상위 네트워크 계층으로 보내는 기능을 수행한다.
- 패킷과 관련한 리소스를 할당하거나, 할당 해제하며 패킷과 관련한 작업을 처리한다.
top half 작업은 빠르게 처리되고 시스템의 반응속도에 영향을 미치지 않는 간단하지만 필수적인 기능만 수행한다. 그 후에 bottom half 에선 리소스 관련 처리나 복잡한 작업을 수행하며, 필요한 기능을 모두 수행한다. 이렇게 분리하여 인터럽트를 처리함으로써 시스템의 응답성과 성능을 개선할 수 있다.
추가적으로, Top half 에서 Bottom half 에서 수행할 작업을 지연 등록한다고 볼 수 있다.
다음으로 리눅스 커널의 인터럽트 서비스 루틴, 그 중 bottom half 에 해당하는 메커니즘 3가지에 대하여 파악해 보려고 한다.
SoftIRQs
가장 낮은 레벨의 bottom half 메커니즘에 해당하며, top half 수행 직후에 수행된다. Interrupt context 에서 동작하여 sleep 되거나 block 되지 않는다.
Softrqs 는 정적으로 고정된 우선순위를 할용한다.
같은 handler function 을 공유하며, 멀티 코어 환경에서 핸들러가 동시에 수행될 수 있다. 동시에 수행될 수 있다는게 무슨 의미인지 싶다. 이는 tasklet과 workqueues까지 살펴본 후 마지막에 이해해 보자.
Softirqs 는 주로 bottom half 에서 상대적 우선순위가 높은 작업을 수행하는데 사용된다. 예를들어 네트워크 패킷 처리나,블록 디바이스 I/O 가 있다.
Tasklets
- 태스크릿은 Softirqs 위에 지어진 단순화된 인터페이스라고 볼 수 있다. Softirqs 와 마찬가지로 interrupt context 에서 동작하며, sleep 되거나 block 되지 않는다.
- 태스크릿에는 2가지 우선순위 단계가 있다. 위 표에서 HI_SOFTIRQ 와 TASKLET_SOFTIR 가 이에 해당하며, HI 레벨의 태스크릿이 항상 먼저 수행된다.
- 태스크릿은 동시에 멀티 코어 환경에서 같은 핸들러를 동시에 수행할 수 없다. 하지만, 서로 다른 태스크릿이라면 (다른 handler function 을 사용) 동시에 수행될 수 있다.
따라서 이는 동시성을 요구하지 않는, 주로 간단한 non-blocking 태스크에 적합하다.
Work Queues
- Workqueus 는 위 2가지 메커니즘과는 분리되어 process context 에서 동작을 수행하는 메커니즘이다.
- process context 에서 동작하기 때문에 sleep, wait for resource, blocking operations 이 모두 가능하다.
- workqueue 의 태스크들은 멀티 코어환경에서 동시에 수행될 수 있다.
- 이는 커널 쓰레드(각 코어마다 할당)로 스케쥴링되며, 더 높은 우선순위에 태스크에 의하여 선점될 수 있다.
결론적으로, Workqueue는 sleeping, blocking operation 을 요구하는 보다 복잡한 태스크에 사용하기 적합하다.
Relationship
결국 3가지 모두 리눅스 커널의 인터럽트 메커니즘 중 bottom halves 에 속하여 인터럽트 핸들러 이후에 지연된 작업을 처리하도록 설계되었다. Softirq는 가장 낮은 수준의 메커니즘이고, 태스크릿은 이 위에서 다른 특징(멀티 코어 동시 수행 불가, 차례로 수행됨)을 가지며 동작한다. workqeues 는 process context 에서 실행되어 보다 복잡한 작업을 수행한다.
앞서 언급한 멀티코어의 동시성과 관련한 설명은 다음과 같다.
예를 들어보겠다. 두 개의 개별 카운터를 업데이트 하는 유사 작업을 수행하는 경우가 있을 것이다. 이 경우 각 카운터 업데이트에 대하여 별도의 함수를 작성하는 대신 카운터를 인자로 받아 업데이트 하는 핸들러 함수를 작성할 수 있을 것이다. 두가지 태스크릿은 동일한 핸들러 함수를 공유하지만 서로 다른 카운터에서 작업을 수행하게 된다.
위와 같은 핸들러 함수를 잘만 사용한다면 동시성에 영향을 미치진 않을 것이다. 그럼에도 태스크릿은 전달되는 인수와 관계없이 공유 핸들러를 사용한다면 멀티 코어 시스템일지라도 이를 순차적으로 수행하도록 설계되어 있다. 공유 핸들러가 동시 실행을 처리하도록 설계되지 않았을 경우에 잠재적인 race condition 이나 데이터 손실을 방지하기 위한 것이다.
핸들러 함수를 공유하는 작업에 동시성이 필요 하고, 동시성 관련 문제가 발생하지 않을 것이라고 확신하는 경우, 대신 workqueue 를 사용할 수 있다. workqueue 를 사용하면 동일한 여러 프로세서에서 같은 핸들러를 동시에 실행할 수 있으므로 병렬성을 향상시킬 수 있다.
'Linux kernel' 카테고리의 다른 글
Dynamic Timer (0) | 2023.04.19 |
---|---|
sysfs (0) | 2023.04.17 |
Device Driver (0) | 2023.04.14 |
virtual file system (VFS) (0) | 2023.04.09 |