OS, Kernel

Kern Koh Kernel of Linux Interrupt 정리

msh1307 2022. 8. 6. 21:39

Hardware Issues

  • 기본적으로 CPU는 instruction fetch, decode, execute, write back 네가지 상태? 로 처리함
  • PC 증가시키면서 처리
  • 보통 명령 4바이트라서 PC+4로 표현했음
  • interrupt request bit이 set 되면 register save 하고 PC에 interrupt handler를 집어넣음
  • 즉 지금하던거 중단하고 interrupt 먼저 처리해주는것

  • IO devices가 많을때 Interrupt Controller를 사용하면 교통정리를 해줄 수 있음
  • 시도때도 없이 막 interrupt request를 보내면 CPU가 효율적인 동작을 해줄 수 없음

  • 프로그래밍 가능한 인터럽트 컨트롤러

  • 대충 IMR에서 걸러진 IRQ들이 우선순위대로 잘 맞춰져서 교통정리 이후 CPU가 처리완료(ACK)라는 신호를 보내면 그 다음거 보냄

  • PIC가 vector number를 IRQ line에 따라 할당해줌
  • vector number가 정확히 어떤 뜻인지는 모르겠지만 대충 어떤 라인인지 알려주는 역할인듯?

  • 앞에서 나온거에 vector 추가된 모습
  • vector가 어떤 irqline인지를 받음
  • I/O 할때 여러 프로세스들이 중복으로 건들면 안되서 마스킹이 필요한것 같음

Interrupt Handling in Multiprocessor

  • multi-APIC에 모든 I/O devices가 몰려있음
  • multi-APIC은 IRQ signal을 bus로 보냄
  • Local APIC에는 timer와 bus가 연결되어있음

  • CPU가 address 보내면 MMU가 잘 바꿔줌
  • I/O bus에 I/O Interface가 연결되어있음
  • I/O Interface는 address register, data register, contorl register, status register들이 있고 ROM에 vendor id나 device id 같은 정보들이 있음 
  • I/O Interface는 직접 꼽힐 수도 있고 PIC에 꼽힐 수도 있음

  • IRQ signal을 잘 분배해야됨

  • distribute 하는 건 CPU 마다 다름

  • IRQ 분배 알고리즘은 static과 Dynamic 크게 두 가지가 있음 
  • Static은 테이블보고 정해진대로 보내는 것
  • Dynamic은 그때그때 우선순위대로 

  • Dynamic IRQ distribution 예시
  • IRQ signal을 lowest priority process를 돌리고 있는  CPU한테 줌
  • 만약 priority 같으면 arbitration algorithm
  • arbitration algorithm은 counter value가 가장 높은 CPU한테 IRQ 보냄
  • counter는 IRQ delivered 된 CPU 제외하고 다 증가

  • Master CPU가 Kernel이 올라와있는 Local Memory를 가짐
  • I/O instruction도 Master CPU만 갖고 있고 모든 I/O devices가 연결되어있음
  • 다른 slave CPU가 I/O를 하려면 Master CPU에게 signal을 보내야 됨
  • 이렇게 되면 모든 syscall이 Master CPU에서만 선형적으로 일어나게 되니까 Mutual exclusion 문제가 일어나지 않음
  • reliability가 낮고 performance bottle neck 발생할 수 있음
  • 요즘엔 안 씀

Data Structure for Interrupt Handling

  • 하나의 IRQ line의 Status는 네 가지로 나눌 수 있음
  • IRQ_DISABLED는 masked out인지 아닌지를 나타냄
  • IRQ_WAITING은 interrupt가 아직 안 온 거 
  • IRQ_PENDING은 interrupt가 떴는데 kernel이 못 돌리고 있는 상태
  • IRQ_INPROGRESS는 kernel이 ISR(Interrupt Service Routine)을 돌리고 있는 상태

  • irq descriptor라는 array가 있음
  • action을 따라가면 handler, name, area, next가 있음
  • interrupt건 device가 아니면 next로 다음 거 확인해가면서 동작
  • handler는 local APIC과 APIC 중 어디서 왔는지 확인하려고 존재함
  • SMP라서 CPU들이 동시에 shared variable인 irq_desc[]에 접근할 수 있어서 Mutual exclusion 때문에 LOCK

Functions for Interrupt Handling

  • IRQn_interrupt()라는 function이 call 됨
  • 간단한 작업을 함
  • Assembler code function이 동작하고 나서 do_IRQ를 call 함 
  • Ack를 PIC에 보내고 kernel table을 update 하고 CPU한테 ISR를 할당해줌
  • ISR는 장치 종속적인 동작을 수행

  • regs를 파라미터로 받음
  • regs.orig_eax & 0xff로 1byte를 뽑아냄
  • 그게 irq number역할을 하는 irq가 됨
  • desc에 irq_desc의 starting address에 irq를 더함 
  • pointer arithmetic에서 +1은 자료형만큼이니 struct 크기만큼 내려감
  • 더해진 irq가 irq vector
  • spin_lock으로 lock을 체크함
  • desc -> handler -> ack(irq)로 handler가 local PIC이 요청했는지 APIC인지 찾고 ack를 보냄
  • ack를 보냈으니 다른 IRQ line에서 오는 interrupt도 처리 가능
  • 그리고 status에서 IRQ_WAITING이랑 IRQ_REPLAY를 빼고, IRQ_PENDING을 set 함 
  • signal이 오고 ack만 했으니 아직 ISR까지 안 가서 PENDING임
  • handle_IRQ_event가 handler를 돌면서 action이 NULL 될 때까지 돌면서 해당 IRQ line에 연결되어 있는 devices의 per device function을 처리하면서 Interrupt 처리함
  • 만약에 다른 cpu가 처리하고 있는 ISR과 동일한 IRQ line에서 또 IRQ 오고 IRQ_INPROGRESS가 set 되어 있으면 종료시킴

  • CPUi가 IRQm에 대한 처리를 해야 될 때 3 cases가 있음

1) 어떤 CPU도 IRQm에 대한 처리를 하고 있지 않을 때

- CPUi가 IRQm을 처리함

2) CPUk가 현재 IRQm의 handler를 돌리는 중일 때

- CPUi가 IRQm의 status를 PENDING으로 바꾸고 goto out;

- 아까 사진 오른쪽 상단 for문을 보면 desc -> status & IRQ_PENDING을 확인함

- IRQ_PENDING이 set 되면 저기서 루프를 한번 더 돌게 됨

- 저 IRQ_PENDING은 아까 CPUi가 set 한 비트임

3) CPUk가 IRQm handler를 끝내는 중일 때

- ISR를 exit 하기 전에 CPUk는 IRQm의 state를 다시 확인함

- 이게 바로 위에서 말한 for문에서 검사하는 거 

 

  • CPUi랑 IRQm 관점에서 아까 위에서 설명한 거 다시 정리

1) IRQm line에 대한 interrupt request가 옴

2) PIC이 IRQ line을 CPU에 올려줌

- 이때 최대한 공평하게 분배하려고 counter based

3) CPUi가 IRQm을 처리해야 됨 이때 IRQ_PENDING status

4) CPUi가 ISR을 누가 처리해야 되는지 정함

- 이때 고려 case는 두 개임

- case A : 어떤 CPU도 IRQm 처리를 안 하고 있음

-> CPUi가 IRQm 처리

- case B : 다른 CPUk가 처리 중 

-> CPUk가 IRQm을 처리하도록 내버려 둠

- IRQm에 대한 data access는 serialize 돼야 돼서 그냥 처리하도록 내버려 두는 게 좋음

  • Case (1)
  • 아무 CPU도 IRQm 처리 안 할 때 CPUi가 처리하는 거 

  • if에서 IRQ_INPROGRESS가 아닌지 체크함

  • handle_IRQ_event() 함

  • 여기서 처리함

  • Case (2)
  • 딴 CPU가 돌리고 있을 때 PENDING bit만 켜놓고 그 CPU 마저 시키는 거 

  • goto out;으로 빠짐

  • 나감

  • Case (3)
  • CPUk가 IRQm handler exit 할 때 re-examining
  • IRQm IRQ_PENDING 확인해서 new request가 오면 다시 한번 더 돌리기

  • IRQ 처리 끝내고 

  • IRQ_PENDING 확인
  • IRQ_PENDING 아니면 break 
  • IRQ_PENDING 이면 IRQ_PENDING bit 끄고 한번 더 루프 
  • 위에서 lock 다시 거는 이유는 pending bit을 다시 확인해봐야 돼서 걸어야 됨

Race Condition

  • ISR 처리는 크게 Top Half와 Bottom Half가 있음
  • Top Half는 빠르게 처리해야 돼서 바로 처리하는 방식임
  • Bottom Half는 오래 걸리니 나중에 미뤄서 처리하는 방식임
  • H/W interrupt가 발생했을 때, 그 interrupt 처리시간이 길수록 컴퓨터가 멈춰있는 것처럼 보임
  • 그래서 그거 막으려고 당장 시급하고 빨리해야 되는 일만 Top Half로 빨리 처리하고 나중에 해도 되는 것들을 Bottom Half로 나중에 처리

  • H/W가 걸어서 Top Half
  • 노란색은 shared variable 접근해야 됨 - critical
  • 하얀색은 lock 풀고 들어감 - Non-critical
  • IO, CPU's interrupt, 현재 process들이 다 block 되니 빨리 해야 됨

  • S/W가 걸어서 Bottom Half
  • ISR을 끝내기 위해서 시간이 많이 필요할 때, 여기서 bit를 set 해줌
  • 이 bit는 나중에 이 device에 대한 처리를 마저 하라는 의미임
  • 아까 set 한 bit는 soft-irq pending bit
  • 나중에 do_softirq가 처리해줌

  • TCP/IP를 예시로 든 모습
  • 이렇게 많은 처리를 해야 되는데 이걸 Top Half에서 다 하기에는 힘듦

  • NIC가 packet을 받고 CPU한테 interrupt를 검
  • Top Half에서 do_IRQ로 CPU 할당해주고 ISR 돌림
  • 이때 Ack 보내고 sk_buff를 할당하고 packet을 sk_buff로 복사함
  • 나머지는 Bottom Half로 처리해달라고 bit set
  • Bottom Half는 나머지를 처리함

  • Top Half

1) Device가 interrupt 보내서 PIC이 cpu한테 signal 보냄 - 이때 ack를 받기 전까지 disasbled 상태

2) CPU한테 interrupt가 걸리고 do_IRQ를 돌림 - ack를 보내고, 어떤 CPU한테 assign 해야 할지 결정함 

3) assigned CPU가 ISR을 돌림 여기서는 NIC의 데이터를 커널로 옮기는 거

4) bottom half bits 세팅

  • Bottom Half

5) kernel이 bottom half bit 보고 돌리기 시작

6) IP handler가 packet data routing

7) TCP handler가 data 조립하고 socket으로 데이터 쏘기

Bottom Halves

  • goals 두 개가 있음

- 빠르게 동작해서 최대한 남들 block 안 하는 거

- 많은 작업도 해야 됨

  • 그래서 두 개로 나눔 

- Top : time-critical work, quick/simple -> interrupt disabled

- Bottom : less critical work, large amount of work -> interrupt enabled

  • 아까 위에서 말한 거

  • softirq_pending array에 bit 1이면 그거에 맞는 softirq_vec의 action data 돌림
  • Interrupt Handler return 하거나, low priority Kernel thread가 돌아갈 때, 혹은 network subsystem 같은 것들이 do_softirq를 invoke

Softirq

  • 전에 배우던 hardware device가 거는 IRQ와 다르게 software가 걸어서 softirq임

  • Linxu에서는 entries 얼마 안 쓰임

  • do while로 돌면서 pending bit를 확인함
  • h가 1씩 증가하고 pending 하나씩 right shift 하면서 루프를 돔

  • Concurrent execution of softirq

1) CPUi가 do_IRQ 하고 softirq bit 세팅

2) CPUi가 do_softirq invoke 하고 bit 확인하고 맞는 softirq handler invoke

3) IRQm은 이미 do_IRQ에서 ack 받았으니 또 interrupt가 걸릴 수 있음

4) CPUk가 선택되고 softirq bit set

5) CPUk가 do_softirq invoke 하면 softirq bit 확인하고 맞는 softirq handler invoke

  • 아무 때나 돌려도 돼서 concurrency도 높은데, 그만큼 mutual exclusion과 reentrant code 작성에 신경 써야 됨

Tasklet

  • softirq

- handler는 서로 다른 CPU가 동시에 돌릴 수 있음

- throughput이 높음

  • tasklet

- 특별한 softirq

- 서로 다른 CPU들이 동시에 돌릴 수 없음

- 다른 tasklet 끼리는 돌릴 수 있음

  • concurrent execution 막으려고 lock이 있음

Work Queue

  • softirq, tasklet, workqueue를 bottom half에 등록할 수 있음
  • Softirq와 tasklet은 function으로서 등록하는 것
  • work queue는 independent program을 등록하는 것
  • Softirq

- fastest, code with great care

  • Tasklet

- sacrifice performace, easier to code

  • Work queues

- process context라서 overhead, 쓸 수 있는 거 많음

 

  • 비교