• 工作队列


    参考:1、http://blog.csdn.net/droidphone/article/details/7518428

              2、http://blog.csdn.net/lizhiguo0532/article/details/6533443

              3、《内核设计与实现》

              4、2.6.34

    首先,看下默认工作队列keventd_wq和管理调度其它内核线程(当然也包含工作线程)时需要的kthreadd线程的创建过程;

    start_kernel()---->rest_init()
                      |---->kernel_thread(kernel_init, NULL, CLONE_FS | 
                      |                   CLONE_SIGHAND);
    | |---->pid = kernel_thread(kthreadd, NULL, CLONE_FS |
                      |                   CLONE_FILES);
    |----kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns); | kthreadd_task保存线程kthreadd的task_struct结构体地址 | kthreadd线程根据kthread_create_list上链入的kthread_create_info结构体来创建特定线程.


    kthreadd的工作流程,从kthread_create_list上取下信息,来创建新的内核线程。
    kthreadd()---->struct kthread_create_info *create;
             |     create = list_entry(kthread_create_list.next,
             |                         struct kthread_create_info, list);
             |----create_kthread(create);
                  |----pid = kernel_thread(kthread, create, CLONE_FS | 
                  |                        CLONE_FILES | SIGCHLD);


    static int kthread(void *_create)
        |----int (*threadfn)(void *data) = create->threadfn;
        |----void *data = create->data;
        |----create->result = current; //kthread_create_info的result保存新创建内核线程的task_struct结构体指针
        |----complete(&create->done);
        |----ret = threadfn(data); //根据kthread_create_info的信息执行相应的函数
        |----do_exit(ret);

    kernel_init()---->do_basic_setup()
                      |---->init_workqueues()

    init_workqueues()---->keventd_wq = create_workqueue("events"); //创建kevent_wq工作队列

    如何创建kevent_wq工作队列,如下:

    create_workqueue("events")
        |---- __create_workqueue_key("event", 0, 0, 0, NULL, NUL); //非signle,每个核上都创建


    __create_workqueue_key("event", 0, 0, 0, NULL, NULL)
       |---->struct workqueue_struct *wq;
       |     struct cpu_workqueue_struct *cwq;
       |     wq = kzalloc(sizeof(*wq), GFP_KERNEL);
       |     wq->cpu_wq = alloc_percpu(struct cpu_workqueue_struct);
       |
       |----list_add(&wq->list, &workqueues);所有的workqueue都链入workqueues
       |
       |对于该类型的工作队列
       |每个核上都建立一个对应的工作线程
       |for_each_possible_cpu(cpu)
       |---->cwq = init_cpu_workqueue(wq, cpu);
       |---->err = create_workqueue_thread(cwq, cpu);


    在每个核上如何创建工作线程,如下:
    int create_workqueue_thread(struct cpu_workqueue_struct *cwq, int cpu)
       |---->struct task_struct *p;
       |     p = kthread_create(worker_thread, cwq, fmt, wq->name, cpu);
             |---->struct kthread_create_info create;
             |     create.threadfn = threadfn;
             |     create.data = data;
             |---->init_completion(&create.done); //初始化完成量
             |
             |---->list_add_tail(&create.list, &kthread_create_list);
             |         将新建的create链接到全局的线程链表kthread_create_list|
             |---->wake_up_process(kthreadd_task); //唤起threadd线程来创建新的内核线程
             |    kthread_task保存内核线程kthreadd的task_struct地址
             |    直接效果就是worker_thread(cwq);
             |
             |---->wait_for_completion(&create.done);
             |.......
      |cwq->thread = p;

    对于每一种类型的工作队列,除了signlethread情形外,在各个核上都创建了相应类型的工作线程。无论时哪种类型的工作队列,各个工作线程最终都会调用worke_thread线程函数。

    static int worker_thread(void* __cwq)
        |----DEFINE_WAIT(wait);
        |
        |循环forever
        |---->prepare_to_wait(&cwq->more_work, &wait, TASK_INTERRUPTIBLE);
        |
        |         if (!freezing(current) &&
        |             !kthread_should_stop() &&
        |             list_empty(&cwq->worklist))
        |                   schedule();//队列上没有工作,睡眠
        |
        |       finish_wait(&cwq->more_work, &wait);
        |        
        |       try_to_freeze();
        |       
        |       if (kthread_should_stop())
        |              break;
        |       
        |       run_workqueue(cwq);//执行对列上的工作
        |.....never_exit

    run_workqueue(cwq)
        |----struct work_struct *work = 
        |    list_entry(cwq->worklist.next, struct work_struct, entry)
        |----work_func_t f = work->func;
        |----cwq->current_work = work;
        |    list_del_init(cwq->worklist.next);
        |    work_clear_pending(work);
        |    f(work); //执行相应的工作函数
        |----cwq->current_work = NULL;                                                                                

    我们看到,在worker_thread中,工作线程将睡眠,那么何时将其唤醒,构建完工作后,将schedule_work,此函数中将唤醒默认的工作线程。

    将工作提交给默认队列时所进行的业务:
    int schedule_work(struct work_struct *work)
        |---->queue_work(keventd_wq, work); 
        |     keventd_wq工作队列是在函数init_workqueues()中创建的


    queue_work()
    int queue_work(struct workqueue_struct *wq, struct work_struct *work)
        |---->queue_work_on(get_cpu(), wq, work);
             |---->__queue_work(wq_per_cpu(wq, cpu), work);


    __queue_work()
    static void __queue_work(struct cpu_workqueue_struct *cwq,
                             struct work_struct *work)
        |----insert_work(cwq, work, &cwq->worklist);
             |---->set_wq_data(work, cwq); //注意work_struct结构体中data域的用法
                   |----new = (unsigned long) cwq | 
                   |           (1UL << WORK_STRUCT_PENDING);
                   |----new |= WORK_STRUCT_FLAG_MASK & 
                   |           *work_data_bits(work);
                   |----atomic_long_set(&work->data, new);
             |---->list_add_tail(&work->entry, head);
             |     将工作项加入等待队列
             |---->wake_up(&cwq->more_work);
             |     唤醒等待在该等待队列头上的所有等待队列项



    至于创建新的工作队列(相应的创建工作线程),由于以上流程从kevetd_wq工作队列的创建开始分析,因此很容易。

  • 相关阅读:
    SpringCloud采用Dubbo远程调用(SpringCloud Alibaba)
    服务注册与发现Nacos
    Nacos配置管理
    Spring Cloud Stream
    MySQL查询优化explain
    索引
    MySQL架构
    mysql事务、视图
    多表查询
    分布式为什么使用python
  • 原文地址:https://www.cnblogs.com/openix/p/3295628.html
Copyright © 2020-2023  润新知