8장 후반부 처리와 지연된 작업 Sang-bok, Heo
인터럽트 전반부 처리 후반부 처리 실행 시간에 민감한 작업 하드웨어와 관련된 작업 다른 인터럽트가 방해해서는 안 되는 작업 인터럽트 핸들러가 처리하지 않은 모든 관련 처리 수행 거의 모든 일을 후반부 처리에서 하는 것이 이상적 전반부 처리 – 하드웨어의 데이터를 메모리로 복사 후반부 처리 – 데이터를 처리 하는 것
왜 후반부 처리를 하는가? 왜 뒤로 미루는가? 미루는 시기가 언제인가? 인터럽트 라인을 모든 프로세서에 대해 비활성화 시킴 인터럽트 핸들러에서 처리할 작업의 양을 제한할 필요 인터럽트 비활성화 시간 최소화는 시스템 반응속도 및 성능에 영향을 미침 미루는 시기가 언제인가? 네트워크 트래픽 수신을 처리하는 동안에도 키 입력처리를 해야함 일부 작업을 ‘나중’으로 지연 ‘나중’ : 지금이 아닌 시점 시스템이 덜 바쁜, 인터럽트가 활성화된 미래의 어떤 순간으로 미룸
후반부 처리기 BH 태스트 큐(task queue) bottom half – 일반적인 후반부 처리를 나타내는 용어 정적으로 만들어진 32개의 후반부 처리기 전반부 처리에서 32비트 정수의 각 비트를 후반부처리기 지정 BH는 전역으로 동기화 서로 다른 프로세스에서도 동시에 BH 실행 불가 구조는 간단, 병목현상이 발생 태스트 큐(task queue) 커널에 몇 개의 큐를 만듦 큐에는 실행할 함수가 연결 리스트 형태로 저장 각 큐별로 정해진 특정 시간에 대기 중인 함수를 실행 BH 인터페이스를 대체하기에 유연성 부족 덩치가 너무 큼 네트워크처럼 성능이 중요한 서브시스템에서
후반부 처리기 softirq 태스트릿 워크 큐 모든 프로세서에서 동시에 실행할 수 있는 정적으로 정의된 후반부 처리기의 모음 모든 프로세서에서 동시에 실행할 수 있는 정적으로 정의된 후반부 처리기의 모음 네트워크처럼 성능이 아주 중요한 경우에 유용 커널 컴파일 시에 정적으로 등록 태스트릿 softirq 기반, 동적으로 생성 가능한 유연한 후반부 처리기 다른/같은 유형의 태스크릿 프로세서에서 동시에 실행 가능/불가 커널 컴파일시 동적으로 등록 워크 큐 프로세스 컨텍스트에서 나중에 처리할 작업을 관리하는 방법 간단, 유용
softirq softirq 구현 softirq 실행 컴파일 시에 정적으로 할당(동적으로 등록 및 제거 불가) <linux/interrupt.h> softirq 구현 컴파일 시에 정적으로 할당(동적으로 등록 및 제거 불가) softirq에 따라서 NR_SOFTIRQS 개수만큼 등록 가능 softirq 실행 하드웨어 인터럽트 코드가 반환되는 경우 ksoftirqd 커널 스레드 내에서 네트워크 서브시스템처럼 명시적으로 코드에서 지연 상태인 softirq를 확인하고 실행하는 경우
softirq softirq 사용 인덱스 할당 시스템에서 가장 실행시간에 민감하고 중요한 후반부 처리 네트워크와 블록 장치 태스크릿 – softirq기반으로 만듦 자체적으로 효율적인 락 관리 가능 인덱스 할당 태스크릿 우선순위 softirq 설명 HI_SOFTIRQ 높은 우선순위 태스크릿 TIMER_SOFTIRQ 1 타이머 NET_TX_SOFTIRQ 2 네트워크 패킷 송신 NET_RX_SOFTIRQ 3 네트워크 패킷 수신 BLOCK_SOFTIRQ 4 블록 장치 TASKLET_SOFTIRQ 5 일반 우선순위 태스크릿 SCHED_SOFTIRQ 6 스케줄러 HRTIMER_SOFTIRQ 7 고해상도 타이머 RCU_SOFTIRQ 8 RCU 락
softirq 핸들러 등록 softirq 올림 최적화 sofrirq 인덱스와 핸들러 함수 softirq 올림 인터럽트를 비활성-> softirq 올림 -> 인터럽트 상태를 복원 최적화 인터럽트가 이미 비활성상태라면 약간의 최적화 가능 인터럽트 핸들러 – 하드웨어 작업 수행 - softirq 올리고 종료 – do_softirq() 함수 호출 – softirq 실행
태스크릿 구현 태스크릿 구조체 softirq 기반으로 만들어짐 핸들러 함수 : data 인자를 하나 받음 HI_SOFTIRQ, TASKLET_SOFTIRQ 두 가지 softirq 사용 태스크릿 구조체 핸들러 함수 : data 인자를 하나 받음 state : 0, TASKLET_STATE_SCHED, TASKLET_STATE_RUN SCHED : 태스크릿이 실행을 기다리는 중 RUN : 태스크릿 실행 중 최적화를 위해 다중 프로세서 시스템에서만 사용 count : 태스크릿 참조 횟수 값이 0이 아니면 태스크릿은 비활성, 실행되지 않음 값이 0이면 활성화 상태, 실행 대기 표시가 있으면 실행
태스크릿 태스크릿 스케줄링 핸들러 함수 sofrirq 올림과 같은 개념 tasklet_action(), tasklet_hi_action()
태스크릿 태스크릿 사용 태스크릿 선언 후반부 처리는 태스크릿을 이용해 구현하는 편이 좋음 동적/정적으로 생성 가능 사용하기 편하고 빠름 태스크릿 선언 직접 참조 DECLARE_TASKLET(name, func, data) count 값이 0인 활성화 상태의 태스크릿 생성 DECLARE_TASKLET_DISABLED(name, func, data) count 값이 1인 비활성화 상태의 태스크릿 생성 간접 참조 tasklet_struct 구조체 t 초기화 : tasklet_init() 함수 이용
태스크릿 Ksoftirq softirq/태스크릿 처리는 프로세서 별로 존재하는 커널 스레드의 도움을 받음 sofrirq의 문제 프로세서 시간을 얻지 못하는 일이 발생 softirq 발생 빈도, softirq 스스로 다시 등록할 수 있다는 점 해결 재등록된 softirq 즉시 처리하지 않는 방법 커널 스레드를 작동(낮은 우선 순위) softirq 너무 많아지면
워크 큐 워크 큐 지연 작업을 커널 스레드 형태로 처리 프로세스 컨텍스트에서 실행 스케줄링이 가능하며, 휴면 상태로 전환 될 수 있음 많은 양의 메모리 할당, 세마포어 할당 작업의 필요한 경유 유용 지연되는 작업이 휴면 상태 전환이 필요한 경우 워크 큐 사용 커널 스레드가 필요하지 않은 경우 태스크릿 사용 검토
cpu_workqueue_struct 워크 큐 구현 최상위 계층에 작업 스레드가 있음 여러 유형의 작업 스레드 특정 유형의 스레드는 프로세스당 하나씩 작업 스레드 cpu_workqueue_struct workqueue_struct 구조체 work_struct 구조체 … 프로세서 당 하나씩 유형마다 하나씩 지연 작업 함수마다 하나씩
워크 큐 워크 큐 사용 작업 생성 워크 큐 핸들러 작업 스케줄링 작업 비우기 새로운 워크 큐 만들기 DECLARE_WORK(name, void (*func)(void *), void *data) INIT_WORK(struct work_struct *work, void (*func)(void *), void *data) 워크 큐 핸들러 void work_handler(void *data) 작업 스케줄링 scedule_work(&work) schedule_delayed_work(&work, delay) 작업 비우기 int cancel_delayed_work(struct work_struct *work) 새로운 워크 큐 만들기 struct workqueue_struct *create_workqueue(const char *name)
후반부 처리 후반부 처리 작업 사이의 락 후반부 처리 비활성화 태스크릿 sofrirq 내부의 동시성과 관련된 문제를 신경 쓰지 않아도 됨 외부의 동시성 문제를 해결하려면 적절한 락 필요 sofrirq 모든 공유 데이터에 대해 적절한 락 필요 후반부 처리 비활성화 공유 데이터를 안전하게 보호 하려면 비활성화 + 락 함께 핵심 커널 코드를 작성하는 경우-후반부 처리만 비활성화 시킬수 있음 후반부 처리 작업 활성화 – local_bh_disable() 후반부 처리 작업 비활성화 – local_bh_enable()