task_struct 구조체, thread_info 구조체

하나의 thread_info 구조체는 하나의 task_struct 구조체와 연결되어 있다. 둘다 하나의 태스크를 표현하기 위해 존재한다.

.1 task_struct 구조체

task_struct 구조체의 정의

.2.1 thread_info 구조체

.2.1.1 thread_info 구조체의 정의, 커널스택, slab cache

thread_info 구조체는 특정 태스크의 low level task data를 담고 있는 자료구조다. 이 구조체는 아래와 같이 정의되어 있다.

arch/arm64/include/asm/thread_info.h

struct thread_info {
        unsigned long           flags;          /* low level flags */
        mm_segment_t            addr_limit;     /* address limit */
        struct task_struct      *task;          /* main task structure */
        int                     preempt_count;  /* 0 => preemptable, <0 => bug */
        int                     cpu;            /* cpu */
};

thread_info 구조체는 시스템에 생성되는 태스크 개수만큼 사용되므로 slab cache로 만들어져 있다. 정확히 얘기하면 커널스택 크기에 따라 slab cache를 만들어 쓸수도 있고 page할당으로 처리할 수도 있다.

slab cache가 나온 이유가 페이지보다 작은 크기의 할당,해제가 반복되면서 발생하는 defragmentation을 방지하고자 하기 때문이다.

kernel stack size 크기와 thread_info 구조체는 무슨 관련이 있을까? 바로 커널 스택과 thread_info 구조체가 같은 메모리공간을 공유해서 쓰고 있다. 이런 이유로 thread_info의 slab cache 크기는 thread_info 구조체보다 훨씬 큰 커널스택의 크기와 동일하다.

thread_info가 사용하지 않는 나머지 영역은 높은 주소영역부터 사용을 시작하는 커널스택으로 쓰인다. 아래 그림을 살펴보자.

ARM64의 커널스택의 크기는 8k 혹은 16k이고 ARM32에서는 8K이다.

arch/arm64/include/asm/thread_info.h

#ifdef CONFIG_ARM64_4K_PAGES
#define THREAD_SIZE_ORDER       2
#elif defined(CONFIG_ARM64_16K_PAGES)
#define THREAD_SIZE_ORDER       0
#endif

#define THREAD_SIZE             16384
#define THREAD_START_SP         (THREAD_SIZE - 16)

커널스택의 크기는 페이지크기와 THREAD_SIZE_ORDER를 조합해서 구할 수 있다. 그리고 실제 커널스택을 할당할때도 이 조합을 이용해서 구한다. 하지만 이유는 알 수 없지만 별도로 커널스택의 크기값을 담고 있는 THREAD_SIZE라는 값도 유지하고 있다.

커널스택의 크기(THREAD_SIZE)가 PAGE_SIZE보다 클 경우에는 thread_info에 대한 slab cache를 만들지 않는다.

init/main.c

# if THREAD_SIZE >= PAGE_SIZE
void __init __weak thread_info_cache_init(void)
{
}
#endif

slab cache에서 할당받는 대신 page 할당으로 처리한다.

# if THREAD_SIZE >= PAGE_SIZE
static struct thread_info *alloc_thread_info_node(struct task_struct *tsk,
                                                  int node)
{
        struct page *page = alloc_kmem_pages_node(node, THREADINFO_GFP,
                                                  THREAD_SIZE_ORDER);
        if (page)
                memcg_kmem_update_page_stat(page, MEMCG_KERNEL_STACK,
                                            1 << THREAD_SIZE_ORDER);
        return page ? page_address(page) : NULL;
}
# else
...
#endif

thread_info 구조체를 사용하는 API - current, get_current

커널코드에서 current라는 매크로를 많이 보았을 것이다. 이 매크로는 현재 코드를 수행하는 태스크의 task_struct 구조체를 리턴해준다. 이 매크로의 구현에는 thread_info 구조체가 쓰이고 있다.

include/asm-generic/current.h

#define get_current() (current_thread_info()->task)
#define current get_current()

current_thread_info()는 arm64에서는 아래와 같이 구현되어 있다.

arch/arm64/include/asm/thread_info.h

static inline struct thread_info *current_thread_info(void)
{
        unsigned long sp_el0;

        asm ("mrs %0, sp_el0" : "=r" (sp_el0));

        return (struct thread_info *)sp_el0;
}

arm32에서는 아래와 같이 구현되어 있다.

static inline struct thread_info *current_thread_info(void)
{
        return (struct thread_info *)
                (current_stack_pointer & ~(THREAD_SIZE - 1));
}

둘다 stack pointer가 가리키는 주소를 이용해서 현재 실행중인 태스크의 thread_info 구조체를 리턴해준다.

thread_info 구조체를 사용하는 API - raw_smp_processor_id()

raw_smp_processor_id()는 현재 코드를 수행하는 processor id를 구한다. processor id는 현재 수행중인 태스크의 thread_info 구조체의 cpu변수를 참고해서 구한다.

arch/arm{64,}/include/asm/smp.h

#define raw_smp_processor_id() (current_thread_info()->cpu)

thread_info 구조체를 사용하는 API - preempt_count()

현재 수행중인 태스크가 사용중인 CPU를 다른 태스크가 선점할 수 있는지는 thread_info 구조체의 preempt_count 변수를 확인하면 알 수 있다.

이 변수값이 0일때는 선점이 가능하고 0보다 큰 경우에는 선점이 불가능하다.

preempt_count()는 preempt_count를 리턴해준다.

thread_info 구조체의 preempt_count는 실행을 중지하면 어떻게 될까

results matching ""

    No results matching ""