1.线程
通过操作系统原理课,我们知道进程是系统资源分配的基本单位,线程是程序独立运行的基本单位。线程有时候也被称作小型进程,首先,这是因为多个线程之间是可以共享资源的;其次,多个线程之间的切换所花费的代价远远比进程低。
在用户态下,使用最广泛的线程操作接口即为POSIX线程接口,即pthread。通过这组接口可以进行线程的创建以及多线程之间的并发控制等。
2.轻量级进程
如果内核要对线程进行调度,那么线程必须像进程那样在内核中对应一个数据结构。进程在内核中有相应的进程描述符,即task_struct结构。事实上,从Linux内核的角度而言,并不存在线程这个概念。内核对线程并没有设立特别的数据结构,而是与进程一样使用task_struct结构进行描述。也就是说线程在内核中也是以一个进程而存在的,只不过它比较特殊,它和同类的进程共享某些资源,比如进程地址空间,进程的信号,打开的文件等。我们将这类特殊的进程称之为轻量级进程(Light Weight Process)。
按照这种线程机制的理解,每个用户态的线程都和内核中的一个轻量级进程相对应。多个轻量级进程之间共享资源,从而体现了多线程之间资源共享的特性。同时这些轻量级进程跟普通进程一样由内核进行独立调度,从而实现了多个进程之间的并发执行。
3.POSIX线程库的实现
用户线程和内核中轻量级进程的关联通常实在符合POSIX线程标准的线程库中完成的。支持轻量级进程的线程库有三个:LinuxThreads、NGPT(Next-Generation POSIX Threads)和NPTL(Native POSIX Thread Library)。由于LinuxThreads并不能完全兼容POSIX标准以及NGPT的放弃,目前Linux中所采用的线程库即为NPTL。
4.线程组
POSIX标准规定在一个多线程的应用程序中,所有线程都必须具有相同的PID。从线程在内核中的实现可得知,每个线程其实都有自己的pid。为此,Linux引入了线程组的概念。在一个多线程的程序中,所有线程形成一个线程组。每一个线程通常是由主线程创建的,主线程即为调用pthread_create()的线程。因此该线程组中所有线程的pid即为主线程的pid。
对于线程组中的线程来说,其task_struct结构中的tpid字段保存该线程组中主线程的pid,而pid字段则保存每个轻量级进程的本身的pid。对于普通的进程而言,tgid和pid是相同的。事实上,getpid()系统调用中返回的是进程的tgid而不是pid。
5.内核线程
上面所描述的都是用户态下的线程,而在内核中还有一种特殊的线程,称之为内核线程(Kernel Thread)。由于在内核中进程和线程不做区分,因此也可以将其称为内核进程。毫无疑问,内核线程在内核中也是通过task_struct结构来表示的。
内核线程和普通进程一样也是内核调度的实体,只不过他们有以下不同:
1).内核线程永远都运行在内核态,而不同进程既可以运行在用户态也可以运行在内核态。从另一个角度讲,内核线程只能之用大于PAGE_OFFSET(即3GB)的地址空间,而普通进程则可以使用整个4GB的地址空间。
2).内核线程只能调用内核函数,而普通进程必须通过系统调用才能使用内核函数。
参考:
1. 深入理解Linux内核 第三版