-
tasklet 사용법개발/컴퓨터사이언스 2018. 6. 17. 09:48
tasklet을 사용하는 과정은 tasklet을 생성하는 작업과 스케줄하는 작업으로 구분된다.
1. tasklet 생성
include/linux/interrupt.h 라는 파일을 보면 tasklet_struct 라는 구조체가 존재한다. 구조체 내의 속성 값을 입력해서 tasklet이 수행할 작업을 설정 할 수 있는데 일일이 속성 값을 호출할 필요는 없이 tasklet_init 이라는 함수를 이용해서 간단히 선언 할 수 있다.
// include/linux/interrupt.h struct tasklet_struct { struct tasklet_struct *next; unsigned long state; atomic_t count; void (*func)(unsigned long); unsigned long data; }; // kernel/softirq.c void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data) { t->next = NULL; t->state = 0; atomic_set(&t->count, 0); t->func = func; t->data = data; }
tasklet_init 함수에서 짐작할 수 있듯이 속성은 다섯개지만 이중에서 사용자가 입력해야 할 값은 *func과 data다. *func는 tasklet이 실행해야하는 함수고 data는 function 실행시 이용 할 수 있는 argument다. data를 일반 상수 값을 전달하기도 하지만 주소 값을 전달해서 포인터로 받아 낼 수도 있다.
아래는 리눅스 드라이버에서 사용하는 예다. *func 함수에서 data 값을 주소로 보고 포인터를 이용해 구조체 값들을 읽는다. 여러 개의 데이터를 전달해야 하는 경우에 사용하면 유용하다.
// linux/driver/firewire/ohci.h static int ar_context_init(struct ar_context *ctx, struct fw_ohci *ohci, unsigned int descriptors_offset, u32 regs) { // ... ctx->regs = regs; ctx->ohci = ohci; tasklet_init(&ctx->tasklet, ar_context_tasklet, (unsigned long)ctx); // ... } static void ar_context_tasklet(unsigned long data) { struct ar_context *ctx = (struct ar_context *)data; //... }
2. tasklet 스케줄
tasklet_schedule, tasklet_hi_schedule이란 함수를 이용해서 앞서 선언한 tasklet 구조체를 실제 CPU에 스케줄링 할 수 있다. 두 함수는 매우 비슷하나 softirq number가 다르다. irq number가 낮을수록 더 우선적으로 처리되니 더 높은 우선적으로 처리하고 싶은 작업이라면 tasklet_hi_schedule을 사용하자
// kernel/softirq.c void __tasklet_schedule(struct tasklet_struct *t) { // ... raise_softirq_irqoff(TASKLET_SOFTIRQ); } EXPORT_SYMBOL(__tasklet_schedule); void __tasklet_hi_schedule(struct tasklet_struct *t) { // ... raise_softirq_irqoff(HI_SOFTIRQ); } EXPORT_SYMBOL(__tasklet_hi_schedule);
3. kill 또는 disable
tasklet을 이미 스케줄 했는데 사용해선 안되거나, 스케줄링 queue에서 빼야할 때가 있다. queue에는 남겨두나 사용하고 싶지 않은 경우에는 tasklet_disable 함수를 사용하면 되고 반대로 다시 살리고 싶으면 tasklet_enable을 쓰면 된다. 아예 queue에서 빼버리고 싶으면 tasklet_kill 함수를 사용한다.
// include/linux/interrupt.h static inline void tasklet_disable(struct tasklet_struct *t) static inline void tasklet_enable(struct tasklet_struct *t) extern void tasklet_kill(struct tasklet_struct *t);
4. 추가
- 설명한것 외에도 다른 함수가 있지만 이정도만 알아도 사용하는데는 지장이 없을 것 같다. 이외의 함수들은 드라이버 단에서는 거의 사용하지 않는다.
- 분석하면서 토발즈가 남긴 주석을 읽어봤는데 되도록이면 softirq를 새로 짜지 말고 가능한 tasklet을 사용하라고 한다. 기존 동작 루틴을 망칠까봐 그런건가. 하긴 모든 드라이버들이 너도나도 softirq 새로 추가하겠다고 하면 슬롯이 부족해서 안될 것 같기도 하다.
- 스케줄링 후 콜백 함수가 실행되기 까지 세부 동작 과정이 궁금하신 분은 아래의 링크를 참조하면 좋을 것 같다.
Schicao's Notes, IBM Developer 사이트 두 포스트의 내용이 비슷한데.. 누가 누구껄 보고 쓴건지 궁금하다.
'개발 > 컴퓨터사이언스' 카테고리의 다른 글
eventfd (0) 2018.07.18 workqueue 사용법 (0) 2018.07.16 tasklet과 workqueue의 차이점 (1) 2018.06.15 ARM64 리눅스 부팅 초기 어셈블리 코드 분석(head.S) (2/2) (0) 2018.01.27 ARM64 리눅스 부팅 초기 어셈블리 코드 분석(head.S) (1/2) (0) 2018.01.17