今天偶然谈起了进程的相关概念,发现其中有许多不清晰的地方,现就以上的概念作一些研究,所参考的资料所有来自于网络,因此对于其中不正确的地方,欢迎你们给我指正,让我可以对以上概念更加清晰。html
好,首先理清以上几个概念,先来看“内核线程”。此处要申明的一点是,从我所看到的资料来看(《Linux内核设计与实现》、《深刻理解Linux内核》以及网上的诸多文章),只有“内核线程”的概念,不存在所谓的“内核进程”。此处对于内核线程的具体定义我并无找到,不过能够从它的功能出发,窥探一二。linux
关于内核线程的做用主要参考自如下文章:http://blog.csdn.net/kickxxx/article/details/9211381web
内核线程的做用主要有:算法
周期性的将dirty内存页同步到磁盘设备上。 好比 bpflush线程周期性的把dirty数据写回磁盘
内存页不多的状况下,把内存page 交换到磁盘空间。 好比kswapd,系统会为每个NUMA建立一个kswapd进程,可是在非NUMA系统上,则仅有一个kswapd
管理延时动做
实现文件系统的事物日志
主要包括两种类型的内核线程:shell
线程按周期性间隔运行,检测特定资源的使用,在用量超出或者低于预置的限制时采起行动
在线程启动后则一直等待,直到内核线程请求执行某一特定的操做。
同时内核线程是由内核本身建立的线程,也叫作守护线程(deamon)。在终端上用命令”ps -Al”列出的全部进程中,名字以k开关以d结尾的每每都是内核线程,好比kthreadd、kswapd。网络
既然是内核线程是一个单独的概念,那么与用户线程之间必定存在某些区别,主要区别参考自如下文章:http://www.2ndmoon.net/weblog/?p=603数据结构
内核线程与用户线程的相同点是:多线程
都由do_fork()建立,每一个线程都有独立的task_struct和内核栈;
都参与调度,内核线程也有优先级,会被调度器平等地换入换出
不一样之处在于:并发
内核线程只工做在内核态中;而用户线程则既能够运行在内核态(执行系统调用时),也能够运行在用户态;
内核线程没有用户空间,因此对于一个内核线程来讲,它的0~3G的内存空间是空白的,它的current->mm是空的,与内核使用同一张页表;而用户线程则能够看到完整的0~4G内存空间。
在Linux内核启动的最后阶段,系统会建立两个内核线程,一个是init,一个是kthreadd。其中init线程的做用是运行文件系统上的一系列”init”脚本,并启动shell进程,因此init线程称得上是系统中全部用户进程的祖先,它的pid是1。kthreadd线程是内核的守护线程,在内核正常工做时,它永远不退出,是一个死循环,它的pid是2。函数
经过上述不一样之处的对比能够发现,内核线程没有用户内存空间,与之相对的是用户进程(注意,此处不是用户线程,用户线程内存空间与用户进程空间之间存在的必定差别,具体差别能够参考我以前的博文),用户进程同时具有内核空间与用户空间,在进行系统调用时用户进程会由用户内存空间陷入内核内存空间。之因此此处采用内核线程与用户进程(而非用户线程)进行对比,是因为内核线程(kernel thread)是“独立运行在内核空间的标准进程”(以上这句话摘自《linux内核设计与实现》),所以从功能上看内核线程一方面具备进程的概念特色——具备独立功能的程序关于某个数据集合上的一次执行活动,是系统进行资源分配的单位,同时内核线程又具备线程的概念特色——进程内的一个可调度实体。
好了经过以上分析基本能够分清“内核线程(kernel thread)”、“用户进程”、“用户线程”这几个基本概念。这里要补充的一点是以上几个概念所有是在逻辑层面上的,从实现的角度来看linux内核自己并不支持线程这一律念,linux 将全部的线程都看成进程来实现。内核并无准备特别的调度算法或是定义特别的数据结构来表征线程。相反,线程仅仅被视为一个与其余进程(概念上应该是线程)共享某些资源的进程(概念上应该是线程)。每一个线程都拥有惟一隶属于本身的task_struct,因此在内核中,它看起来就像是一个普通的进程(只是线程和其余一些进程共享某些资源,如地址空间)。关于这一点能够经过系统调用clone的实现来验证,不管是fork、vfork、kthread_create最后都是要调用do_fork,而do_fork就是根据不一样的函数参数,对一个进程所需的资源进行分配。在linux2.6以前,内核并不支持线程的概念,仅经过轻量级进程(lightweight process)模拟线程,一个用户线程对应一个内核线程(内核轻量级进程),这种模型最大的特色是线程调度由内核完成了,而其余线程操做(同步、取消)等都是核外的线程库(LinuxThread)函数完成的。但这个问题还存在不少的问题,具体问题请见http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/index.html 这篇文章中的第6小节。在linux2.6以后,为了彻底兼容posix标准,linux2.6首先对内核进行了改进,引入了线程组的概念(仍然用轻量级进程表示线程),有了这个概念就能够将一组线程组织称为一个进程,如此经过这个改变,linux内核正式支持多线程特性。以上是逻辑上的改变,在实现上主要的改变就是在task_struct中加入tgid字段,这个字段就是用于表示线程组id的字段。在用户线程库方面,也使用NPTL代替LinuxThread。不一样调度模型上仍然采用“1对1”模型,关于线程的调度模型,接下来会详细分析。
对于linux内核中对于多线程支持的变化历史请见如下这篇blog:http://blog.sina.com.cn/s/blog_631d3a630102uwn2.html
关于LinuxThread与NPTL的对比请见这篇文章:http://blog.csdn.net/ixidof/article/details/24579879
对于2.4内核中线程的实现,请见这篇文章http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/index.html
接下来来看看内核线程与用户线程之间的对应关系。首先从原理上出发,对内核线程与用户线程之间的对应关系进行分析。
如今在支持多线程的操做系统中通常采用三种调度模型,分别是“一对一模型”、“多对一模型”、“多对多模型”。
以上内容参考自下述两篇blog
http://www.cnblogs.com/zhaoyl/p/3620204.html
1)“一对一模型”
一对一模型中,每一个用户线程都对应各自的内核调度实体。内核会对每一个线程进行调度,能够调度到其余处理器上面。固然由内核来调度的结果就是:线程的每次操做会在用户态和内核态切换。另外,内核为每一个线程都映射调度实体,若是系统出现大量线程,会对系统性能有影响。但该模型的实用性仍是高于多对一的线程模型。LinuxThread与NPTL都是采用这种模型。
在linux中经过LWP(lightweight process)做为线程概念的支持,轻量级线程(LWP)是一种由内核支持的用户线程。它是基于内核线程的高级抽象,所以只有先支持内核线程,才能有LWP。每个进程有一个或多个LWPs,每一个LWP由一个内核线程支持。这种模型实际上就是恐龙书上所提到的一对一线程模型。在这种实现的操做系统中,LWP就是用户线程。
因为每一个LWP都与一个特定的内核线程关联,所以每一个LWP都是一个独立的线程调度单元。即便有一个LWP在系统调用中阻塞,也不会影响整个进程的执行。
轻量级进程具备局限性。首先,大多数LWP的操做,如创建、析构以及同步,都须要进行系统调用。系统调用的代价相对较高:须要在user mode和kernel mode中切换。其次,每一个LWP都须要有一个内核线程支持,所以LWP要消耗内核资源(内核线程的栈空间)。所以一个系统不能支持大量的LWP。图也是盗的。
2)“多对一模型”
多对一线程模型中,线程的建立、调度、同步的全部细节所有由进程的用户空间线程库来处理。用户态线程的不少操做对内核来讲都是透明的,由于不须要内核来接管,这意味不须要内核态和用户态频繁切换。线程的建立、调度、同步处理速度很是快。固然线程的一些其余操做仍是要通过内核,如IO读写。这样致使了一个问题:当多线程并发执行时,若是其中一个线程执行IO操做时,内核接管这个操做,若是IO阻塞,用户态的其余线程都会被阻塞,由于这些线程都对应同一个内核调度实体。在多处理器机器上,内核不知道用户态有这些线程,没法把它们调度到其余处理器,也没法经过优先级来调度。这对线程的使用是没有意义的!
3)“多对多模型”
用户线程库仍是彻底创建在用户空间中,所以用户线程的操做仍是很廉价,所以能够创建任意多须要的用户线程。操做系统提供了 LWP 做为用户线程和内核线程之间的桥梁。 LWP 仍是和前面提到的同样,具备内核线程支持,是内核的调度单元,而且用户线程的系统调用要经过 LWP ,所以进程中某个用户线程的阻塞不会影响整个进程的执行。用户线程库将创建的用户线程关联到 LWP 上, LWP 与用户线程的数量不必定一致。当内核调度到某个 LWP 上时,此时与该 LWP 关联的用户线程就被执行。