• 内核线程创建分析


    文件:kthread.c

     1 /**
     2  * kthread_create - create a kthread.
     3  * @threadfn: the function to run until signal_pending(current).
     4  * @data: data ptr for @threadfn.
     5  * @namefmt: printf-style name for the thread.
     6  *
     7  * Description: This helper function creates and names a kernel
     8  * thread.  The thread will be stopped: use wake_up_process() to start
     9  * it.  See also kthread_run(), kthread_create_on_cpu().
    10  *这里面给出了如何运行一个内核线程的流程。
    参数说明:threadfn是指线程回调函数,data为回调函数的参数,namefmt为线程名字
    11 * When woken, the thread will run @threadfn() with @data as its argument.
    12 @threadfn() can either call do_exit() directly if it is a
    standalone thread for which noone will call kthread_stop(), or 14 * 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(). 17 * 18 * Returns a task_struct or ERR_PTR(-ENOMEM). 19 */ 20 struct task_struct *kthread_create(int (*threadfn)(void *data), 21 void *data, 22 const char namefmt[], 23 ...) 24 { 25 struct kthread_create_info create; 26 //初始化线程创建描述符 27 create.threadfn = threadfn; 28 create.data = data;
    /*kthread_create采用了完成量机制,可以查阅等待量机制的原理*/
    29 init_completion(&create.done); 30 31 spin_lock(&kthread_create_lock); 32 list_add_tail(&create.list, &kthread_create_list); //将线程队列加入到全局线程创建队列中
     /*注意这个全局链表kthread_create_list, 所用通过kthread_create创建的内核线程都会挂在这*/
    33 spin_unlock(&kthread_create_lock);
    34   /*这是最重要的地方,从代码看是唤醒了kthreadd_task这个进程,如果对代码比较熟悉的话,就会想到这是内核中  的1号进程kthreadd*/ 35 wake_up_process(kthreadd_task);
       /*当前进程在完成量上睡眠等待*/
    36 wait_for_completion(&create.done); 37 38 if (!IS_ERR(create.result)) { 39 struct sched_param param = { .sched_priority = 0 }; 40 va_list args; 41 42 va_start(args, namefmt); 43 vsnprintf(create.result->comm, sizeof(create.result->comm), 44 namefmt, args); 45 va_end(args); 46 /*设置线程属性,包括调度策略, 47 * root may have changed our (kthreadd's) priority or CPU mask. 48 * The kernel thread should not inherit these properties. 49 */ 50 sched_setscheduler_nocheck(create.result, SCHED_NORMAL, &param); 51 set_user_nice(create.result, KTHREAD_NICE_LEVEL);
    //设置调度属性,具体原理参考http://www.cnblogs.com/papam/archive/2009/08/27/1555353.html
    52 set_cpus_allowed_ptr(create.result, cpu_all_mask);
    在多核架构的内核中,对于每个cpu,都有一个struct rq* ptr静态结构体指针变量对应,但是通过传统方法无法得到该静态指针变量的值,通过阅读/kernel/sched.c函数,在导出函数set_cpus_allowed_ptr中使用过该指针变量,具体参考:http://wanderer-zjhit.blogbus.com/logs/186876356.html
    53 } 54 return create.result; 55 } 56 EXPORT_SYMBOL(kthread_create);

    首先分析一下重要的一个函数:

    1 int wake_up_process(struct task_struct *p)
    2 {
    3     return try_to_wake_up(p, TASK_ALL, 0);
    4 }
      1 /***
      2  * try_to_wake_up - wake up a thread
      3  * @p: the to-be-woken-up thread
      4  * @state: the mask of task states that can be woken
      5  * @sync: do a synchronous wakeup?
      6  *看这里的解释,让唤醒的进程进入运行队列,
      7  * Put it on the run-queue if it's not already there. The "current"
      8  * thread is always on the run-queue (except when the actual
      9  * re-schedule is in progress), and as such you're allowed to do
     10  * the simpler "current->state = TASK_RUNNING" to mark yourself
     11  * runnable without the overhead of this.
     12  *
     13  * returns failure only if the task is already active.
     14  */
     15 static int try_to_wake_up(struct task_struct *p, unsigned int state, int sync)
     16 {
     17     int cpu, orig_cpu, this_cpu, success = 0;
     18     unsigned long flags;
     19     long old_state;
     20     struct rq *rq;
     21 
     22     if (!sched_feat(SYNC_WAKEUPS))
     23         sync = 0;
     24 
     25 #ifdef CONFIG_SMP
     26     if (sched_feat(LB_WAKEUP_UPDATE) && !root_task_group_empty()) {
     27         struct sched_domain *sd;
     28 
     29         this_cpu = raw_smp_processor_id();
     30         cpu = task_cpu(p);
     31 
     32         for_each_domain(this_cpu, sd) {
     33             if (cpumask_test_cpu(cpu, sched_domain_span(sd))) {
     34                 update_shares(sd);
     35                 break;
     36             }
     37         }
     38     }
     39 #endif
     40 
     41     smp_wmb();
    1. 关闭本地中断并给本地可执行队列rq加锁
    42 rq = task_rq_lock(p, &flags); 43 update_rq_clock(rq);
    2. 如果当前进程状态p->state不在要唤醒的进程状态集中,则不能唤醒该进程
    44 old_state = p->state; 45 if (!(old_state & state)) 46 goto out; 47 3. 如果当前进程本身就在可执行队列中,则无需唤醒本进程 48 if (p->se.on_rq) 49 goto out_running; 50 4. task_cpu(p)返回当前进程p所使用的CPU编号(p所归属的runqueue所在的CPU编号) 51 cpu = task_cpu(p); 52 orig_cpu = cpu; 53 this_cpu = smp_processor_id(); 54 55 #ifdef CONFIG_SMP 56 if (unlikely(task_running(rq, p))) 57 goto out_activate; 58 59 cpu = p->sched_class->select_task_rq(p, sync); 60 if (cpu != orig_cpu) { 61 set_task_cpu(p, cpu); 62 task_rq_unlock(rq, &flags); 63 /* might preempt at this point */ 64 rq = task_rq_lock(p, &flags); 65 old_state = p->state; 66 if (!(old_state & state)) 67 goto out; 68 if (p->se.on_rq) 69 goto out_running; 70 71 this_cpu = smp_processor_id(); 72 cpu = task_cpu(p); 73 } 74 75 #ifdef CONFIG_SCHEDSTATS 76 schedstat_inc(rq, ttwu_count); 77 if (cpu == this_cpu) 78 schedstat_inc(rq, ttwu_local); 79 else { 80 struct sched_domain *sd; 81 for_each_domain(this_cpu, sd) { 82 if (cpumask_test_cpu(cpu, sched_domain_span(sd))) { 83 schedstat_inc(sd, ttwu_wake_remote); 84 break; 85 } 86 } 87 } 88 #endif /* CONFIG_SCHEDSTATS */ 89 90 out_activate: 91 #endif /* CONFIG_SMP */ 92 schedstat_inc(p, se.nr_wakeups); 93 if (sync) 94 schedstat_inc(p, se.nr_wakeups_sync); 95 if (orig_cpu != cpu) 96 schedstat_inc(p, se.nr_wakeups_migrate); 97 if (cpu == this_cpu) 98 schedstat_inc(p, se.nr_wakeups_local); 99 else 100 schedstat_inc(p, se.nr_wakeups_remote);
    更新唤醒进程p的平均睡眠时间sleep_avg和动态优先级prio;记录该进程唤醒前的睡眠状态;将该进程插入活跃优先级数组
    101 activate_task(rq, p, 1); 102 success = 1; 103 104 /* 105 * Only attribute actual wakeups done by this task. 106 */ 107 if (!in_interrupt()) { 108 struct sched_entity *se = &current->se; 109 u64 sample = se->sum_exec_runtime; 110 111 if (se->last_wakeup) 112 sample -= se->last_wakeup; 113 else 114 sample -= se->start_runtime; 115 update_avg(&se->avg_wakeup, sample); 116 117 se->last_wakeup = se->sum_exec_runtime; 118 } 119 如果唤醒进程p的动态优先级prio比当前进程current的动态优先级高则当前进程的TIF_NEED_RESCHED就需要设置 120 out_running: 121 trace_sched_wakeup(rq, p, success); 122 check_preempt_curr(rq, p, sync); 123 124 p->state = TASK_RUNNING; 125 #ifdef CONFIG_SMP 126 if (p->sched_class->task_wake_up) 127 p->sched_class->task_wake_up(rq, p); 128 #endif 129 out: 130 task_rq_unlock(rq, &flags); 131 132 return success; 133 }

    由于电池问题,暂时分析到这里,有几个问题,目前需要弄清楚:

    1)线程如何进行管理的?

    2)kthreadd_task,kthread_create_list具体的作用是什么?

    kthread_create_list这个队列唯一被调用的地方,是在int kthreadd(void *unused)函数中。
     1 int kthreadd(void *unused)
     2 {
     3     struct task_struct *tsk = current;
     4 
     5     /* Setup a clean context for our children to inherit. */
     6     set_task_comm(tsk, "kthreadd");
     7     ignore_signals(tsk);
     8     set_user_nice(tsk, KTHREAD_NICE_LEVEL);
     9     set_cpus_allowed_ptr(tsk, cpu_all_mask);
    10     set_mems_allowed(node_possible_map);
    11 
    12     current->flags |= PF_NOFREEZE | PF_FREEZER_NOSIG;
    13 
    14     for (;;) {
    15         set_current_state(TASK_INTERRUPTIBLE);
    16         if (list_empty(&kthread_create_list))  //如果队列是空,进行调度
    17             schedule();
    18         __set_current_state(TASK_RUNNING);  //设置当前状态为运行状态
    19 
    20         spin_lock(&kthread_create_lock);
    21         while (!list_empty(&kthread_create_list)) {
    22             struct kthread_create_info *create;
    23 
    24             create = list_entry(kthread_create_list.next,
    25                         struct kthread_create_info, list);  //从list中取出需要创建的线程描述符
    26             list_del_init(&create->list);
    27             spin_unlock(&kthread_create_lock);
    28 
    29             create_kthread(create);
    30 
    31             spin_lock(&kthread_create_lock);
    32         }
    33         spin_unlock(&kthread_create_lock);
    34     }
    35 
    36     return 0;
    37 }
     1 static void create_kthread(struct kthread_create_info *create)
     2 {
     3     int pid;
     4 
     5     /* We want our own signal handler (we take no signals by default). */
     6     pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD); //创建一个新的进程。
     7     if (pid < 0) {
     8         create->result = ERR_PTR(pid);
     9         complete(&create->done);
    10     }
    11 }
    而对ktheadd调用的地方为:
     1 static noinline void __init_refok rest_init(void)
     2 __releases(kernel_lock)
     3 {
     4 int pid;
     5 
     6 kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
     7 numa_default_policy();
     8 pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); //线程添加函数,在系统初始化的时候开启
     9 kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
    10 unlock_kernel();
    从以上的分析可以看到,在系统启动的时候,创建一个进程,运行kthreadd函数,而ktheadd函数循环判断线程队列是否为空,如果为空则调度出去,否则取出新添加的线程描述符,创建一个新的线程(进程);这个也就是kthread_create_list的作用。

    再就是kthreadd_task这个变量。初始化在
    __init_refok rest_init(void)函数中,通过函数名可以看出来,task作用是通过pid找到任务。内核中调用kthread_task总共三处,一处是在初始化的时候,另外两处是:static void reparent_to_kthreadd(void),kthread_create();
    线程创建时通过wake_up_process(kthreadd_task);唤醒ktheadd_task,然后将线程描述符添加到进程管理中,而在reparent_to_kthreadd中则是对当前进程进行设置,
    current->real_parent = current->parent = kthreadd_task;

       线程创建的过程就是以上的过程,剩下的就是对进程管理进行分析的。

  • 相关阅读:
    意图识别及槽填充联合模型bert
    意图识别及槽填充联合模型的改进 cnn-self-attention
    意图识别及槽填充联合模型cnn-seq2seq
    cnn-residual用于意图识别
    centos 8 上配置asp.net core 5
    WPF自定义控件
    jdk 动态代理源码分析
    在windows上构建angular项目 (下)
    在windows上构建angular项目 (上)
    "名字好难想队“团队项目
  • 原文地址:https://www.cnblogs.com/gogly/p/2668101.html
Copyright © 2020-2023  润新知