layout | title | category | description | tags |
---|---|---|---|---|
post |
内核线程 |
进程 |
内核线程... |
内核线程 idel swapper init 守护进程 |
内核线程(kernel thread)是直接由内核本身启动的进程,内核线程实际上是将内核函数委托给独立的进程,与系统中其他进程『并行』执行。内核线程经常被称之为守护进程。它们用于执行下列任务:
- 周期性地将修改的内存页与页面来源块设备同步。
- 如果内存页很少使用,则写入交换区。
- 管理延时动作。
- 实现文件系统的事物日志。
基本上,有两种类型的内核线程:
- 线程启动后一直等待,直至内核请求线程执行某一特定的操作。
- 线程启动后按周期性间隔运行,监测特定的资源使用,在用量超出或者低于预置的限制时采取行动。
这些任务包括刷新磁盘高速缓存,交换出不用的页框,维护网络连接等等。实际上,如果以严格的线性方式执行这些任务效率不高,如果把它们放在后台调度,则会有较好的效率。这些任务委托给内核线程,内核线程不受不必要的用户态上下文的拖累,内核线程和普通进程的区别有:
- 内核线程只运行在内核态,而普通进程既可以运行在内核态,也可以运行在用户态。
- 因为内核线程只运行在内核态,它们只使用大于PAGE_OFFSET的线性地址空间。普通进程可以使用4GB的线性地址空间。
调用kernel_thread函数可以启动一个内核线程。
{% highlight c++ %} int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) {% endhighlight %}
产生的线程将执行fn指针传递的函数,以便内核线程可以根据函数的不同而执行不同的函数,而用arg指定的参数将自动化传递给该函数。
大多数计算机上的系统的全部虚拟地址空间分成两部分:地步可以由用户层程序访问,上部则专供内核使用。在内核代表用户层运行时,虚拟地址空间的用户空间部分有mm指向mm_struct实例描述。每当内核执行上下文切换时,虚拟地址空间的用户层部分都会切换,以便与当前运行的进程匹配。
这为优化提供了一些方法,可遵循所谓的惰性TLB处理。由于内核线程不与任何特定的用户层进程相关,内核不需要倒换虚拟地址空间的用户层部分。由于内核线程之前可能有其他用户层进程在执行,因此用户空间部分的内存本质上时随机的。内核线程绝不能修改其内容。
为强调用户空间部分不能访问,mm设置为空指针。但由于内核必须知道用户空间当前包含了什么,所以在active_mm中保存了指向mm_struct的一个指针来描述它。
内核线程可以用两种方法实现。第一种是将一个函数直接传递给kernel_thread,这个函数接下来负责帮助内核调用daemonize以转换为守护进程。将触发下列操作。
- 该函数从内核线程释放其父进程的所有资源,因为守护进程只操作内核地址区域,不需要这些资源。
- daemonize阻塞信号的接受。
- 将init用作守护进程的父进程。
kthread_create帮助创建内核线程。
{% highlight c++ %} struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, const char namefmt[], ) {% endhighlight %}
另一个被选方案是使用宏指令kthread_run,参数与thread_create相同。它会调用kthread_create创建新线程,但是立即唤醒它。
所有进程的祖先叫做进程0,idle进程,因为历史原因也可叫做swapper进程,它是在Linux的初始化阶段从无到有创建的一个内核线程。
由进程0创建的内核线程执行init()函数,init()依次完成内核初始化。init()调用execve()系统调用转入可执行程序init,init内核线程变为一个普通进程,且拥有自己的每进程内核数据结构。在系统关闭之前,init进程一直存活,因为它创建和监控在操作系统外层执行的所有进程的活动。