Android 8.0 系统启动流程之Linux内核启动--kthreadd进程(二)

    在上一篇文章中详细的分析了idle进程的启动,idle进程启动过程中会启动init进程与kthreadd进程,本文将详细分析kthreadd进程的启动过程。

1、kthreadd进程启动

由上篇文章分析可知,在rest_init函数中启动了kthreadd进程

static noinline void __init_refok rest_init(void)
{
    ......
    pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
    ......
}

kernel_thread会调用do_fork函数用于创建进程,进程创建成功后会通过函数指针回调执行kthreadd函数;进程创建过程在此就不做分析,着重来分析一下kthreadd函数

2、kthreadd函数

定义在kernel/msm-4.4/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);//允许kthreadd在任意CPU上运行
    set_mems_allowed(node_states[N_MEMORY]);

    current->flags |= PF_NOFREEZE;
    cgroup_init_kthreadd();

    for (;;) {
        //首先将线程状态设置为 TASK_INTERRUPTIBLE, 如果当前没有要创建的线程则主动放弃 CPU 完成调度.此进程变为阻塞态
        set_current_state(TASK_INTERRUPTIBLE);
        if (list_empty(&kthread_create_list))//没有需要创建的内核线程
            schedule();//执行一次调度, 让出CPU
        //运行到此表示 kthreadd 线程被唤醒,设置进程运行状态为 TASK_RUNNING  
        __set_current_state(TASK_RUNNING);

        spin_lock(&kthread_create_lock);//spin_lock是自旋锁,不可休眠
        while (!list_empty(&kthread_create_list)) {
            struct kthread_create_info *create;
            //kthread_create_list是一个链表,从链表中取出下一个要创建的kthread_create_info,即线程创建信息
            create = list_entry(kthread_create_list.next,
                        struct kthread_create_info, list);
            list_del_init(&create->list);//删除create中的list
            spin_unlock(&kthread_create_lock);

            create_kthread(create);//创建线程

            spin_lock(&kthread_create_lock);
        }
        spin_unlock(&kthread_create_lock);
    }

    return 0;
}

kthreadd函数的作用就是循环地从kthread_create_list链表中取出要创建的线程信息(添加过程详见第5小节),然后执行create_kthread函数,直到kthread_create_list为空,让出CPU,进入睡眠,我们来看下create_kthread函数

3、 create_kthread

定义在kernel/msm-4.4/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);
    }
}

其实这里面就是调用kernel_thread函数创建进程,然后执行kthread函数,注意不要搞混了,之前那个函数叫kthreadd,接下来看看kthread函数

4、kthread

定义在kernel/msm-4.4/kernel/kthread.c中

static int kthread(void *_create)
{
    /* Copy data: it's on kthread's stack */
     // create 就是之前kthreadd函数循环取出的需要创建线程信息kthread_create_info
    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);//表示线程创建完毕
    //让出CPU,注意这里并没有执行新线程的threadfn函数就直接进入睡眠了,
    //然后等待线程被手动唤醒,然后才执行threadfn
    schedule();

    ret = -EINTR;

    if (!test_bit(KTHREAD_SHOULD_STOP, &self.flags)) {
        cgroup_kthread_ready();
        __kthread_parkme(&self);
        ret = threadfn(data);
    }
    /* we can't just return, we must preserve "self" on stack */
    do_exit(ret);
}

到此处,其实kthreadd进程已经启动完成,进入死循环查看kthread_create_list链表中是否有新的线程需要创建。接下来分析一下在内核中启动线程时,是如何将线程信息加入kthread_create_list链表中,从而启动线程。

5、内核启动线程方式

kthreadd创建线程是遍历kthread_create_list链表,那kthread_create_list链表中的值是哪儿来的呢?
一般情况下,Linux创建内核线程有两种方式,kthread_create和kthread_run
定义在kernel/msm-4.4/include/linux/kthread.h

#define kthread_create(threadfn, data, namefmt, arg...) \
    kthread_create_on_node(threadfn, data, NUMA_NO_NODE, namefmt, ##arg)

#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;                                   \
})

    kthread_create和kthread_run并不是函数,而是宏定义,在编译时会替换对应代码,宏的参数没有类型定义,多行宏的定义会在行末尾加上\
    这两个宏最终都是调用kthread_create_on_node函数,只是kthread_run在线程创建完成后会手动唤醒,我们来看看kthread_create_on_node函数

6、 kthread_create_on_node

定义在kernel/msm-4.4/kernel/kthread.c中

struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
                       void *data, int node,
                       const char namefmt[],
                       ...)
{
    DECLARE_COMPLETION_ONSTACK(done);//初始化done,之前讲过completion和wait_for_completion同步
    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);
    //将要创建的线程加到kthread_create_list链表尾部
    list_add_tail(&create->list, &kthread_create_list);
    spin_unlock(&kthread_create_lock);

    //唤醒kthreadd进程,开启列表循环创建线程
    wake_up_process(kthreadd_task);
    /*
     * 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.
     */
     //当&done complete时,会继续往下执行
    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.
         */
        //task类型为task_struct,该函数作用是设置新线程调度策略,SCHED_NORMAL 普通调度策略,
        //非实时,优先级低于实时调度策略SCHED_FIFO和SCHED_RR,param的优先级上面定义为0
        sched_setscheduler_nocheck(task, SCHED_NORMAL, &param);
         //允许新线程在任意CPU上运行
        set_cpus_allowed_ptr(task, cpu_all_mask);
    }
    kfree(create);
    return task;
}

kthread_create_on_node主要作用就是在kthread_create_list链表尾部加上要创建的线程,然后唤醒kthreadd进程进行具体创建工作

7、小结

    kthreadd进程由idle通过kernel_thread创建,并始终运行在内核空间, 负责所有内核线程的调度和管理,所有的内核线程都是直接或者间接的以kthreadd为父进程。

    kthreadd进程会执行一个kthreadd的函数,该函数的作用就是遍历kthread_create_list链表,从链表中取出需要创建的内核线程进行创建, 创建成功后会执行kthread函数。

    kthread函数完成一些初始赋值后就让出CPU,并没有执行新线程的工作函数,因此需要手工 wake up被唤醒后,新线程才执行自己的真正工作函数。

    当我们调用kthread_create和kthread_run创建的内核线程会被加入到kthread_create_list链表,kthread_create不会手动wake up新线程,kthread_run会手动wake up新线程。

    到此kthreadd进程就分析完成了,下一篇将分析kernel_init进程。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值