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, 쓸 수 있는 거 많음
- 비교
'OS, Kernel' 카테고리의 다른 글
Kern Koh Kernel of Linux Scheduling 정리 (0) | 2022.07.20 |
---|---|
Kern Koh Kernel of Linux Process Management 정리 (0) | 2022.07.19 |
Kern Koh Kernel of Linux System Call 정리 (0) | 2022.07.18 |
Kern Koh Kernel of Linux Introduction 정리 (0) | 2022.07.18 |