内核进程粗解
谨以此文纪念过往的岁月。
在内核中可以通过kthread_run来创建一个新的线程
一.kthread_run
这个实际上说不上是函数其实是一个宏定义。其定义如下:
#define kthread_run(threadfn, data, namefmt, ...) \
({ \
struct task_struct *__k \
= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
if (!IS_ERR(__k)) \
wake_up_process(__k); \
__k; \
})
以下所描述的函数均在/kernel/kthread.c中
kthread_create函数实现
struct task_struct *kthread_create(int (*threadfn)(void *data),void *data,const char namefmt[],...)
{
struct kthread_create_info create;
create.threadfn = threadfn;
create.data = data;
init_completion(&create.started);
init_completion(&create.done);
spin_lock(&kthread_create_lock); --kthread_create_lock为当前文件全局变量,锁定自旋锁
list_add_tail(&create.list, &kthread_create_list); --将其加入kthread_create_list的链表中,真正的kthread创建在kthreadadd中
spin_unlock(&kthread_create_lock); --解锁
wake_up_process(kthreadd_task); --唤醒kthread线程,kthreadd_task是根据rest_init中创建线程时返回的pid得到的线程信息。
wait_for_completion(&create.done); --等待创建完成
if (!IS_ERR(create.result)) {
va_list args;
va_start(args, namefmt);
vsnprintf(create.result->comm, sizeof(create.result->comm),namefmt, args);
va_end(args);
}
return create.result; --返回创建线程的信息
}
下面的函数其实在rest_init中就被运行,创建了新的线程。这个函数在系统启动时就会成为后台程序运行。
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_user_nice(tsk, KTHREAD_NICE_LEVEL);
set_cpus_allowed_ptr(tsk, CPU_MASK_ALL_PTR);
current->flags |= PF_NOFREEZE | PF_FREEZER_NOSIG;
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)) { --当kthread_create_list有值时
struct kthread_create_info *create;
create = list_entry(kthread_create_list.next,struct kthread_create_info, list); --container_of
list_del_init(&create->list); --将create->list从kthread_create_list中删除并从新初始化。
spin_unlock(&kthread_create_lock);
create_kthread(create); --创建一个线程
spin_lock(&kthread_create_lock);
}
spin_unlock(&kthread_create_lock);
}
return 0;
}
static void create_kthread(struct kthread_create_info *create)
{
int pid;
/* We want our own signal handler (we take no signals by default). */
pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD); --创建一个新的kthread线程,调用threadfn
if (pid < 0) {
create->result = ERR_PTR(pid);
} else {
struct sched_param param = { .sched_priority = 0 };
wait_for_completion(&create->started); --等待kthread进程中完成
read_lock(&tasklist_lock);
create->result = find_task_by_pid_ns(pid, &init_pid_ns); --根据pid返回thread的信息
read_unlock(&tasklist_lock);
/*
* root may have changed our (kthreadd's) priority or CPU mask.
* The kernel thread should not inherit these properties.
*/
sched_setscheduler(create->result, SCHED_NORMAL, ¶m);
set_user_nice(create->result, KTHREAD_NICE_LEVEL);
set_cpus_allowed_ptr(create->result, CPU_MASK_ALL_PTR);
}
complete(&create->done); --创建完成
}
static int kthread(void *_create)
{
struct kthread_create_info *create = _create;
int (*threadfn)(void *data);
void *data;
int ret = -EINTR;
/* Copy data: it's on kthread's stack */
threadfn = create->threadfn; --真正需要实现的函数
data = create->data;
/* OK, tell user we're spawned, wait for stop or wakeup */
__set_current_state(TASK_UNINTERRUPTIBLE); --设置当前进程睡眠
complete(&create->started);
schedule(); --调度其他进程,即唤醒父线程,新建的线程只运行到此,需要在其后wake_up_process中唤醒
if (!kthread_should_stop()) --如果函数没有返回或者没有调用kthread_stop来结束线程,则会一直运行下去
ret = threadfn(data);
/* It might have exited on its own, w/o kthread_stop. Check. */
if (kthread_should_stop()) { --只是判断是否stop kthread
kthread_stop_info.err = ret;
complete(&kthread_stop_info.done);
}
return 0;
}
int kthread_should_stop(void)
{
return (kthread_stop_info.k == current);
}
int kthread_stop(struct task_struct *k)
{
int ret;
mutex_lock(&kthread_stop_lock);
/* It could exit after stop_info.k set, but before wake_up_process. */
get_task_struct(k);
trace_sched_kthread_stop(k);
/* Must init completion *before* thread sees kthread_stop_info.k */
init_completion(&kthread_stop_info.done);
smp_wmb();
/* Now set kthread_should_stop() to true, and wake it up. */
kthread_stop_info.k = k;
wake_up_process(k);
put_task_struct(k);
/* Once it dies, reset stop ptr, gather result and we're done. */
wait_for_completion(&kthread_stop_info.done);
kthread_stop_info.k = NULL;
ret = kthread_stop_info.err;
mutex_unlock(&kthread_stop_lock);
trace_sched_kthread_stop_ret(ret);
return ret;
}
以上的函数其实很好理解。
以主进程A(调用kthread_run的进程),守护进程B(kthread)
A调用kthread_run,仅仅将create->list加入kthread_create_list中,然后等待create.done,唤醒进程B。B进程一直查询kthread_create_list是否为空,如果不为空调用
create_thread在create_thread新建一个进程C(kthread),同时等待create->started.在C进程中并没有直接运行A进程传替的函数,而是complete(create->started)
之后睡眠进程C,同时唤醒B进程。在B进程中实现complete(&create->done),唤醒A进程同时将进程B睡眠。在进程A中会调用wake_up_porcess将进程C唤醒。这样一个
进程就开始运行了。在进程A中调用kthread_stop可以将进程C结束。
在kthread_run中其实真正实现创建进程的是kernel_thread。