ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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 NotesIBM Developer 사이트 두 포스트의 내용이 비슷한데.. 누가 누구껄 보고 쓴건지 궁금하다.

    '개발' 카테고리의 다른 글

    workqueue 사용법  (0) 2018.07.16
    Kubernetes 소개  (0) 2018.06.23
    tasklet과 workqueue의 차이점  (1) 2018.06.15
    리눅스 디스크, 폴더 용량 확인하기  (0) 2018.05.26
    Ubuntu 16.04에서 Tensorflow GPU 지원 버전 설치하기  (2) 2018.05.01

    댓글

Designed by Tistory.