이 페이지에서는 kthread_create() 함수와 kthreadd() 함수에 대해 분석해본다.
kthreadd
생성 요청한 커널 스레드를 fork 한다.
사용 APIs
- kthread_create()
- kthread_run()
- kthread_bind()
- kthread_stop()
- kthread_should_stop()
- kthread_data()
kthread_create()
include/linux/kthread.h
#define kthread_create(threadfn, data, namefmt, arg...) \ kthread_create_on_node(threadfn, data, -1, namefmt, ##arg)
threadfn 함수를 엔트리 진입점으로 하여 커널 스레드를 만든다. 곧 바로 동작이 필요한 경우 후속 호출로 wake_up_process() 함수를 사용한다.
kthread_create_on_node()
kernel/kthread.c
/** * kthread_create_on_cpu - Create a cpu bound kthread * @threadfn: the function to run until signal_pending(current). * @data: data ptr for @threadfn. * @cpu: The cpu on which the thread should be bound, * @namefmt: printf-style name for the thread. Format is restricted * to "name.*%u". Code fills in cpu number. * * Description: This helper function creates and names a kernel thread * The thread will be woken and put into park mode. */ struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data), void *data, unsigned int cpu, const char *namefmt) { struct task_struct *p; p = kthread_create_on_node(threadfn, data, cpu_to_node(cpu), namefmt, cpu); if (IS_ERR(p)) return p; set_bit(KTHREAD_IS_PER_CPU, &to_kthread(p)->flags); to_kthread(p)->cpu = cpu; /* Park the thread to get it out of TASK_UNINTERRUPTIBLE state */ kthread_park(p); return p; }
threadfn 함수를 엔트리 진입점으로 하여 지정된 cpu에 커널 스레드를 만든다. 만들어진 스레드는 곧 바로 park 상태로 바꾸어 둔다.
kthread_create_on_node()
kernel/kthread.c
/** * kthread_create_on_node - create a kthread. * @threadfn: the function to run until signal_pending(current). * @data: data ptr for @threadfn. * @node: memory node number. * @namefmt: printf-style name for the thread. * * Description: This helper function creates and names a kernel * thread. The thread will be stopped: use wake_up_process() to start * it. See also kthread_run(). * * If thread is going to be bound on a particular cpu, give its node * in @node, to get NUMA affinity for kthread stack, or else give -1. * When woken, the thread will run @threadfn() with @data as its * argument. @threadfn() can either call do_exit() directly if it is a * standalone thread for which no one will call kthread_stop(), or * return when 'kthread_should_stop()' is true (which means * kthread_stop() has been called). The return value should be zero * or a negative error number; it will be passed to kthread_stop(). * * Returns a task_struct or ERR_PTR(-ENOMEM) or ERR_PTR(-EINTR). */ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data), void *data, int node, const char namefmt[], ...) { DECLARE_COMPLETION_ONSTACK(done); struct task_struct *task; struct kthread_create_info *create = kmalloc(sizeof(*create), GFP_KERNEL); if (!create) return ERR_PTR(-ENOMEM); create->threadfn = threadfn; create->data = data; create->node = node; create->done = &done; spin_lock(&kthread_create_lock); list_add_tail(&create->list, &kthread_create_list); spin_unlock(&kthread_create_lock); wake_up_process(kthreadd_task);
- 코드 라인 30~42에서 kthread_create_info 구조체를 할당받고 요청 인수 내용으로 채운 후 kthread_create_list에 추가한다.
- 코드 라인 44에서 kthread_create_list에 담은 내용으로 커널 스레드를 생성시키는 “kthreadd” 라는 이름의 최상위 커널 스레드를 깨운다.
/* * Wait for completion in killable state, for I might be chosen by * the OOM killer while kthreadd is trying to allocate memory for * new kernel thread. */ if (unlikely(wait_for_completion_killable(&done))) { /* * If I was SIGKILLed before kthreadd (or new kernel thread) * calls complete(), leave the cleanup of this structure to * that thread. */ if (xchg(&create->done, NULL)) return ERR_PTR(-EINTR); /* * kthreadd (or new kernel thread) will call complete() * shortly. */ wait_for_completion(&done); } task = create->result; if (!IS_ERR(task)) { static const struct sched_param param = { .sched_priority = 0 }; va_list args; va_start(args, namefmt); vsnprintf(task->comm, sizeof(task->comm), namefmt, args); va_end(args); /* * root may have changed our (kthreadd's) priority or CPU mask. * The kernel thread should not inherit these properties. */ sched_setscheduler_nocheck(task, SCHED_NORMAL, ¶m); set_cpus_allowed_ptr(task, cpu_all_mask); } kfree(create); return task; } EXPORT_SYMBOL(kthread_create_on_node);
- 코드 라인 6~19에서 “kthreadd” 커널 태스크를 통해 사용자가 요청한 커널 스레드가 생성될 때까지 대기한다. 만일 낮은 확률로 에러(-ERESTARTSYS)가 발생하는 경우에는 sigkill 시그널이 와서 종료처리를 하는 경우에는 -EINTR 결과로 함수를 빠져나가고 그렇지 않은 경우 다시 한 번 기다린다.
- 참고: wait_for_completion() | 문c
- 코드 라인 20~36에서 생성이 완료된 경우 생성된 커널 태스크를 반환한다. 커널 태스크가 정상적으로 생성된 경우 normal 태스크로 스케줄러 속성을 변경하고 모든 cpu에서 동작하도록 설정한다.
- 커널 스레드를 normal 정책을 사용하는 cfs 태스크로 만들 때 sched_priority는 항상 0으로 세팅하여야 한다.
kthreadd()
kernel/kthread.c
int kthreadd(void *unused) { struct task_struct *tsk = current; /* Setup a clean context for our children to inherit. */ set_task_comm(tsk, "kthreadd"); ignore_signals(tsk); set_cpus_allowed_ptr(tsk, cpu_all_mask); set_mems_allowed(node_states[N_MEMORY]); current->flags |= PF_NOFREEZE; for (;;) { set_current_state(TASK_INTERRUPTIBLE); if (list_empty(&kthread_create_list)) schedule(); __set_current_state(TASK_RUNNING); spin_lock(&kthread_create_lock); while (!list_empty(&kthread_create_list)) { struct kthread_create_info *create; create = list_entry(kthread_create_list.next, struct kthread_create_info, list); list_del_init(&create->list); spin_unlock(&kthread_create_lock); create_kthread(create); spin_lock(&kthread_create_lock); } spin_unlock(&kthread_create_lock); } return 0; }
커널 스레드를 생성시킬 때 사용하는 최상위 커널 스레드이다. 무한 루프를 돌며 커널 스레드 생성 요청이 있을 때 마다 이를 수행한다.
- 코드 라인 6~11에서 태스크의 이름을 “kthreadd”로 설정하고, 시그널을 받을 수 없게 한다. 이 커널 스레드는 모든 cpu와 모든 메모리 노드에서 동작할 수 있게 설정한다. 또한 freeze되지 않게 한다.
- 참고: set_mems_allowed() | 문c
- 코드 라인 13~17에서 무한 루프를 돌며 thread_create_list에 생성할 내용이 없으면 interruptible 상태로 변경하고 sleep 한다. 깨어나면 runnng 상태로 변경한다.
- 코드 라인 19~32에서 kthread_create_list에 등록된 내용으로 커널 스레드를 생성하면서 순회한다.
create_kthread()
kernel/kthread.c
static void create_kthread(struct kthread_create_info *create) { int pid; #ifdef CONFIG_NUMA current->pref_node_fork = create->node; #endif /* We want our own signal handler (we take no signals by default). */ pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD); if (pid < 0) { /* If user was SIGKILLed, I release the structure. */ struct completion *done = xchg(&create->done, NULL); if (!done) { kfree(create); return; } create->result = ERR_PTR(pid); complete(done); } }
커널 스레드를 생성한다. 만일 실패하는 경우 에러 결과로 completion 처리를 한다.
- kthread() 함수를 fork하고 argument로 요청한 함수 포인터를 넘겨준다.
- kthread() 함수 내부에서 현재 fork된 태스크를 sleep 시킨다. 깨어날 때 곧바로 인수로 넘겨 받은 함수를 호출한다.
kernel_thread()
kernel/fork.c
/* * Create a kernel thread. */ pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) { return do_fork(flags|CLONE_VM|CLONE_UNTRACED, (unsigned long)fn, (unsigned long)arg, NULL, NULL); }
fn 함수를 엔트리 시작으로 fork 하여 커널 스레드를 생성한다.
- fn 함수들:
- kthread()
- kernel_init()
- kthreadd()
- wait_for_helper()
- ____call_usermodehelper()
kthread()
kernel/kthread.c
static int kthread(void *_create) { /* Copy data: it's on kthread's stack */ struct kthread_create_info *create = _create; int (*threadfn)(void *data) = create->threadfn; void *data = create->data; struct completion *done; struct kthread self; int ret; self.flags = 0; self.data = data; init_completion(&self.exited); init_completion(&self.parked); current->vfork_done = &self.exited; /* If user was SIGKILLed, I release the structure. */ done = xchg(&create->done, NULL); if (!done) { kfree(create); do_exit(-EINTR); } /* OK, tell user we're spawned, wait for stop or wakeup */ __set_current_state(TASK_UNINTERRUPTIBLE); create->result = current; complete(done); schedule(); ret = -EINTR; if (!test_bit(KTHREAD_SHOULD_STOP, &self.flags)) { __kthread_parkme(&self); ret = threadfn(data); } /* we can't just return, we must preserve "self" on stack */ do_exit(ret); }
fork 되어 실행된 이 함수는 스레드 생성되었음을 알리는 completion 처리를 한 후 sleep 한다. 이 후 wakeup 요청을 받으면 인수로 받은 함수를 호출한다.
- 코드 라인 18~22에서 SIGKILL에 의해 스레드 생성 요청이 없어진 경우 현재 스레드를 종료시킨다.
- 코드 라인 24~27에서 스레드 생성 요청에 따라 wait_for_completion() 함수에서 대기중인 스레드를 계속 진행할 수 있도록 complete() 함수를 사용한다. 그런 후 sleep 한다.
- 코드 라인 31~34에서 스레드 종료 요청을 받은 경우가 아니면 인수로 받은 함수를 호출한다.
- 코드 라인 36에서 수행이 완료된 경우 스레드를 종료시킨다.
참고
- Kernel Thread | Flatinum
- Kthreads – 다운로드 pdf