수업 정리/임베디드시스템소프트웨어

[embedded system software/9주차] 이론

gyuun365 2026. 4. 29. 21:24

해당 글은 건국대학교 진현욱 교수님의 임베디드 시스템 소프트웨어 수업 내용을 정리한 글입니다. 

 

Execution Modes

CPU는 권한에 따라 2가지 모드로 나뉜다. 바로 User mode와 Kernel mode이다.

User mode

일반적인 애플리케이션이 실행되는 모드로서 프로세서가 하드웨어의 직접 접근하는 것이나 메모리에 허가 받지 않은 접근을 막는다.

Kernel mode

전체 프로세서 명령어와 모든 메모리, I/O space에 제한 없이 접근할 수 있는 권한을 가지며 만약 user mode process가 이 권한을 가지고 싶다면 System call을 통해서만 가능하다. 정확히 말하면 system call시 cpu의 권한 등급을 올려서 허용해주는 것

 

그러면 권한 올리는게 어려운건 아니네요? -> 이 질문엔 권한 올리는 것은 System call으로만 가능하다고 생각하면 된다. 마치 공항에서 무조건 입국심사를 받고 가야하는 것처럼

 

Execution Contexts in the kernel

Process Context 

  • 왼쪽 그림처럼 애플리케이션이 호출한 시스템 콜을 처리할 때나 오른쪽 그림처럼 커널 스레드를 실행할 때의 상태를 말한다.
  • 특징으로는 Preemptible하다는 것이다. 자세히 말하자면 선점 가능 즉, 실행 중에 잠들 수(sleep) 있다는 뜻이다.

Interrupt Context

  • 인터럽트 핸들러(ISR)가 실행될 때의 상태이다.
  • 특징으로는 Not preemptible하다는 것이다. 자세히 말하자면 선점 불가 즉, 절대로 잠들 수(sleep) 없다는 뜻
    • 인터럽트 핸들러는 자기만의 스택이 아니라 현재 실행 중인 프로세스의 스택에 "기생"해서 돌아가기 때문에, 여기서 잠들면 어떤 프로세스를 재웠는지 알 수 없게 되어 시스템이 돌아오지 못할 위험이 있다!!

추가적으로 드라이버 내의 함수가 시스템 콜에서도 호출되고 인터럽트 처리 시에도 사용된다면 꼭 sleep를 그 안에 넣으면 안된다.

위의 이유처럼 어떤 stack에서 기생하는지를 모르기 때문이다. 

 

Interrupts

항상 다짐해야할 것은 interrupts는 예측할 수 없다는 점이다. 

하드웨어 기기는 CPU와 독립적으로 작동하기 때문에, 자신의 상태 변화를 비동기적으로 알릴 방법이 필요하다.  

또한 프로세서와 비교해 상대적으로 느린 I/O devices 속도 차이를 해결해줄 방법 또한 필요하다.

그것을 cpu는 Interrupts로 해결하고 있는 것이다.

 

예를 들어 network device를 들자면 네트워크 프레임 수신, DMA 전송 완료, 장치의 메모리 여유 공간 확보 등이 있겠다.

 

IRQ Numbers

Interrupts는 하드웨어 기기가 자신의 상태 변화를 CPU에게 알릴 방법이라고 하였다. 그 하드웨어 기기들은 각각 Interrupt request number 라는 식별자를 할당받게 되는데 그것이 바로 IRQ number라는 것이다. 일반적으로 IRQ number는 임베디드 시스템에서 고정되어 있다. BSP의 부분으로서 (BSP는 앞에서 나온 개념으로 GPIO와 하드웨어를 매핑시켜주는 것이라고 하엿음)

 

리눅스에서는 cat /proc/interrupts 명령어를 통해 각 CPU에 전달된 인터럽트 횟수와 장치 정보를 확인할 수 있다.

이 정보에서는 cpu에 전달된 횟수를 알 수 있는데 cpu 개수마다 있으므로 참고하자

 

Interrupt Vector Table

인터럽트 번호를 인덱스로 사용하는 함수(핸들러)의 배열이다. 인터럽트가 발생하면 CPU는 이 테이블을 참조해 실행할 함수를 찾는다

즉 IRQ number가 이 table의 특정 칸에 연결되어 있다는 소리이다. 

CPU가 하드웨어 신호를 감지하면, 하던 일을 멈추고 이 테이블에서 해당 번호에 적힌 주소로 점프하여 ISR(Interrupt Service Routine)을 즉시 실행한다.

 

이러한 ISR은 시스템의 반응성을 높이기 위해 2단계로 나눠진다.

단계 명칭 특징
1단계 Top-half (Fast),
Interrupt handler,
fast interrupt handler
하드웨어 인터럽트에 즉각 응답하는 최소한의 작업만 수행한다.
 

2단계 Bottom-half (Slow)
slow interrupt handler
deferrable function
복잡하거나 시간이 오래 걸려 지연 처리가 가능한 작업을 수행한다.
 

 

Stack

stack은 User mode와 Kernel mode에 둘 다 존재한다.

그림처럼 User mode stack은 각각 스레드마다 존재하지만 Kernel mode stack은 유저 모드처럼 프로세스마다 독립된 가상 메모리 공간을 가지는 것이 아니라, 하나의 공통된 커널 메모리 영역 안에 모여 있다. 하지만 그 공통된 공간 안에서, 커널은 각 스레드가 생성될 때마다 딱 그 스레드만 쓸 수 있는 작은 조각(32비트 8KB, 64비트 16KB)을 딱 붙여서 떼어준다.

 

중요한 사실은 ISR은 그들만의 Stack이 없다는 것이다.

인터럽트가 발생한 그 순간, CPU가 사용 중이던 그 스레드의 커널 스택 조각을 그대로 타고 올라가서 사용한다.

파랑색 그림처럼 현재 사용중인 user stack, kernel stack에 대한 정보가 cpu안에 있을 것이다. 그 정보를 재사용해서 기생한다고 생각하면 된다. 


gpio_to_irq()

특정 gpio에 할당된 IRQ num을 구하는 함수이다. return값으로 irq number를 반납해준다. 

request_irq()

특정 IRQ number에 interrupt hander function을 등록하는 함수이다. 매개변수로 당연히 2개의 값이 들어간다.

 

주로 드라이버 초기화 시점이나, 공유 인터럽트의 경우 장치를 처음 열 때(first open) 호출하는 것이 효율적이다.

 

free_irq()

더 이상 인터럽트를 처리할 필요가 없을 때 등록된 핸들러를 해제한다.

해당 IRQ를 처리 중인 인터럽트가 모두 완료될 때까지 반환되지 않는다. 따라서 절대 인터럽트 컨텍스트 내에서 호출하면 안 된다.

 

irqreturn_t handler()

실제 인터럽트가 발생했을 때 실행되는 함수의 형태이다.

 

parameter

  • int irq: 발생한 인터럽트 번호.
  • void *dev_id: request_irq 호출 시 전달했던 데이터로, 보통 장치의 데이터 구조체를 가리킨다.
  • struct pt_regs *regs: 인터럽트 발생 직전의 프로세서 컨텍스트 스냅샷.

return: 해당 장치의 인터럽트가 맞으면 IRQ_HANDLED, 아니면 IRQ_NONE을 반환한다.

 

disable_irq() & enable_irq()

특정 irq number의 인터럽트 발생을 막거나 허용한다.

local_irq_disable() & local_irq_enable()

로컬 cpu core 전체를 제어하며 현재 프로세서의 모든 인터럽트를 끄고 켠다.

하지만 이 2개의 함수는 만약 여러 개가 사용했을 때의 race condition이나 의도와 다르게 사용될 수 있어서 

보통 local_irq_save()와 local_irq_restore()를 사용한다. 이 2개의 함수는 현재 interrupt의 disable여부를 추가로 저장해서 쓰기 전 상태로 돌려놓기 까지 하기 때문이다.


Interrupt handler cannot

  • 프로세서 양보 금지 (Relinquish the processor):
    • schedule_timeout() 사용 불가: 일정 시간 동안 현재 작업을 재우는 함수를 사용할 수 없다.
    • kmalloc()GFP_KERNEL 사용 금지: 메모리 부족 시 프로세스를 잠재울 수 있는 플래그이기 때문.
    • 뮤텍스(Mutex) 사용 금지: 뮤텍스는 락을 얻지 못하면 잠들 수 있으므로, 대신 잠들지 않는 스핀락(Spinlock)을 사용해야 한다.
  • 유저 공간과 직접 데이터 교환 불가:
    • 인터럽트는 어떤 프로세스의 맥락(Context)에서 발생했는지 알 수 없다.  따라서 유저 랜드와 직접 통신할 수 없다.

Interrupt handler can

  • 비선점 실행 (Non-preemptible): 인터럽트 핸들러가 실행되는 동안에는 다른 프로세스가 끼어들 수 없다.
  • 해당 IRQ 비활성화: 핸들러가 실행되는 동안, 해당 인터럽트 번호(IRQ)는 핸들러가 반환될 때까지 자동으로 비활성화되어 중복 실행을 방지.
  • 중첩 인터럽트 가능 (Nested Interruption): 현재 실행 중인 핸들러보다 우선순위가 높은 IRQ는 실행 중인 핸들러를 끊고 들어올 수 있다.
    • 이를 막으려면 로컬 프로세서의 모든 인터럽트를 비활성화해야 한다.

Simple devices that don't use interrupts

 

  • 폴링(Polling) 방식: 인터럽트 기능이 없는 장치들은 소프트웨어가 주기적으로 상태를 확인해야 한다.
  • 처리 주체:
    • 시스템 콜을 통한 유저 프로세스
    • 혹은 커널 스레드가 이 역할을 수행한다.