完成量是基于等待队列设计的,所以显然不能在中断上下文使用完成量。

struct completion {unsigned int done;wait_queue_head_t wait;
};

我们来看一个使用完成量的经典例子:

struct kthread_create_info
{/* Information passed to kthread() from kthreadd. */int (*threadfn)(void *data);void *data;int node;/* Result passed back to kthread_create() from kthreadd. */struct task_struct *result;struct completion done;struct list_head list;
};

在创建内核线程的例子中,我们使用了一个kthread_create_info结构来封装了一个完成量:

struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),void *data, int node,const char namefmt[],...)
{struct kthread_create_info create;create.threadfn = threadfn;---------------要创建的线程的主函数create.data = data;create.node = node;init_completion(&create.done);------------动态初始化完成量spin_lock(&kthread_create_lock);list_add_tail(&create.list, &kthread_create_list);-------------加入链表,相当于把请求挂在一个双向循环链表中spin_unlock(&kthread_create_lock);wake_up_process(kthreadd_task);-----------唤醒处理完成量的内核线程,来处理我们发送的请求wait_for_completion(&create.done);--------等待完成,这个在等待完成量的期间,会导致本进程睡眠。。。。。。。

如上代码是提交请求的一侧,那么,处理请求的一侧是怎么完成该任务,并通知到请求方呢?

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;
}

简单地看,没看到怎么通知请求方,代码其实是在create_kthread中实现的:

static void create_kthread(struct kthread_create_info *create)
{int pid;#ifdef CONFIG_NUMAcurrent->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) {create->result = ERR_PTR(pid);complete(&create->done);-------------------通知请求方,一般就是唤醒了}
}

以上就是使用完成量的经典例子,两个互不干扰的执行流,一个通过wait_for_completion来等待请求完成,一个通过complete,还有complete_all等来通知请求方,完成交互。

除了动态初始化一个完成量,还有一种静态初始化的方式,

static __initdata DECLARE_COMPLETION(kthreadd_done);
比如我们下面要描述的kthreadd线程,在 rest_init 中调用 的时候:(为啥叫rest_init,个人觉得是,因为包括mm,调度器之类的都已经初始化好了,就剩下这个初始化了,所以叫rest_init
,这个函数还有个特点就是,它最终会调用cpu_startup_entry(CPUHP_ONLINE); 死循环,永不退出,一直处于内核态)
static noinline void __init_refok rest_init(void)
{int pid;rcu_scheduler_starting();/** We need to spawn init first so that it obtains pid 1, however* the init task will end up wanting to create kthreads, which, if* we schedule it before we create kthreadd, will OOPS.*/kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);numa_default_policy();pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);------------创建2号进程,也就是kthradd内核线程rcu_read_lock();kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);rcu_read_unlock();complete(&kthreadd_done);------------唤醒被阻塞的进程

在kthreadd 创建之前,

kernel_init-->kernel_init_freeable函数会使用静态初始化的完成量  kthreadd_done 来等待 kthreadd 内核线程创建好。
static noinline void __init kernel_init_freeable(void)
{/** Wait until kthreadd is all set-up.*/wait_for_completion(&kthreadd_done);

也就是说,在2号进程,也就是 kthreadd 被创建好之前,1号进程其实是阻塞的。有一个疑问就是,为什么1号进程要等待2号进程呢?因为假设1号进程不等待,那么用户态进程就可能通过

系统调用来获取资源,而如果这些资源是由2号线程或者2号线程的子线程来维护的话,则必然产生oops。所以这个地方的完成量,起的是一个时序的作用。

我们可以看到,内核线程的创建接口,是由 kthreadd 内核线程来完成fork的,

 ps -ef |grep -i kthreadd
root         2     0  0 9月15 ?       00:00:00 [kthreadd]

这个内核线程的pid是2,其他所有的内核线程都是它fork出来的,因为init进程占据了pid 1,所以它的pid是2。

我们假设一下,如果pid 为1的init进程,最终不去执行

if (!run_init_process("/sbin/init") ||!run_init_process("/etc/init") ||!run_init_process("/bin/init") ||!run_init_process("/bin/sh"))

那么它这个时候纯粹还是内核线程,它全部工作在内核态,没有用户态进程的os有没有用呢?

我觉得是有的,没有交互罢了,全部在内核态。恩,如果你把一些任务放在内核里面完成,完全可以不要用户态进程嘛。

总结一下:
进程1又称为init进程,是所有用户进程的祖先,注意,是用户进程,不是内核进程,内核进程的祖先是kthreadd,这哥们负责fork所有的内核线程。

由进程0在start_kernel调用rest_init创建
init进程PID为1,当调度程序选择到init进程时,init进程开始执行kernel_init ()函数
init是个普通的用户态进程,它是Unix系统内核初始化与用户态初始化的接合点,它是所有用户进程的祖宗。在运行init以前是内核态初始化,该过程(内核初始化)的最后一个动作就是运行/sbin/init可执行文件。
完成量,既可以完成通信的作用,又可以完成时序控制的作用,既能够动态初始化来完成交互,又可能静态init来完成交互。

转载于:https://www.cnblogs.com/10087622blog/p/9666929.html

linux 3.10中完成量的使用相关推荐

  1. Linux驱动 - 多线程之 完成量

    Linux 系统提供了一种比信号量更好的同步机制,即完成量(completion ,它用于一个执行单元等待另一个 执行单元执行完某事. Linux 系统中与 completion 相关的操作主要有以下 ...

  2. Linux 4.10中两个新特性与我的一段故事

    今早5点半起来没有开始写文章,而是去西湾红树林连跑带走折腾了将近20公里,回来后就8点多了...洗了个澡之后坐稳当,开始写一段关于我的故事.         在2014年到2015年期间,我在负责研发 ...

  3. win10内核linux,windows 10中发布完整的Linux内核

    5月8日消息: 近年来,微软对Linux开发者社区的支持令许多人感到惊讶,包括将诸如Bash shell之类的东西引入到Windows,或者在Windows 10中支持原生OpenSSH,甚至包括Wi ...

  4. window10内核Linux,windows 10中发布完整的Linux内核

    5月8日消息: 近年来,微软对Linux开发者社区的支持令许多人感到惊讶,包括将诸如Bash shell之类的东西引入到Windows,或者在Windows 10中支持原生OpenSSH,甚至包括Wi ...

  5. 内核并发控制---完成量 (来自网易)

    定义在头文件linux/completion.h中; 完成量(completion)是Linux系统提供的一种比信号量更好的同步机制,是对信号量的一种补充;它用于一个执行单元等待另一个执行单元完成某事 ...

  6. linux内核7大功能,Linux Kernel5.10十个值得关注的功能

    Linux Kernel 5.10 有望在本周末发布,外媒 Phoronix 盘点了 10 个值得关注的改进和新功能.5.10 不仅是 2020 年最后一个内核版本,而且还是长期支持(LTS)版本. ...

  7. 联想 键盘 fn linux,开发者提交补丁,Linux 5.10 或支持联想 PC 键盘快捷键

    Linux 5.10 中可能将支持新型号联想 Thinkpad 键盘上的快捷键驱动. 联想 ThinkPad 一直以来对支持 Linux 发行版情有独钟,Fedora.Ubuntu 与 Red Hat ...

  8. Linux内核中锁机制之完成量、互斥量

    在上一篇博文中笔者分析了关于信号量.读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题. 八.完成量 下面讨论完成量的内容,首先需明确完成量表示为一个执行单元需要等 ...

  9. linux中同步例子(完成量completion)

    这是一个公交司机和售票员之间的线程调度,用于理解完成量,完成量是对信号量的一种补充,主要用于多处理器系统上发生的一种微妙竞争 在这里两个线程间同步,只有当售票员把门关了后,司机才能开动车,只有当司机停 ...

最新文章

  1. 定义一个不能被拷贝的类
  2. python的span方法_Python Span.set_extension方法代码示例
  3. InceptionNet V3整理总结
  4. unity3d双面材质_[转]unity3d中创建双面材质
  5. 书籍推荐:零基础入门学习Python
  6. Android8.0 学习 (17)Android国际化(多语言)实现,支持8.0
  7. poj 1330(LCA)
  8. 业务gis 怎么让别的开发人员不需要懂gis就可以搞开发? (一)
  9. 斯坦福大学深度学习公开课cs231n学习笔记(1)softmax函数理解与应用
  10. hibernate教程笔记10
  11. Apple Pay的使用
  12. dimens文件生成器使用方法
  13. UVALive 4850 Installations——思维
  14. 伦敦大学国王学院 计算机phd,重磅!伦敦国王学院全奖博士录取一枚!
  15. Blender软件使用Maps Models Importer插件导入Google地图3d模型(非常简单)
  16. 字节跳动校招面试题演练
  17. 简易记账开发笔记之Fragment(前传)
  18. 数据分析——问卷调查从模型到算法
  19. 工作4年感想:美团、360、陌陌、百度、阿里、京东面试
  20. 正面杠腾讯音乐与网易云音乐,抖音与快手谁能“弯道超车“?

热门文章

  1. 第五章:几个重要技术总结
  2. 如果用float实现居中
  3. Iterator(迭代器)
  4. R7-11 h0096. 单词合并 (10 分)
  5. vue请求数据传给子组件_vue.js基础,父组件如何向子组件传递数据「607」
  6. android edittext不可复制_Android中使EditText只读的方法(可恢复编辑状态)
  7. centos dhcp 服务器搭建 多vlan
  8. 调用图灵机器人API实现简单聊天
  9. python 3.6 MJ小工具
  10. day2 程序流程控制