操作系统-进程管理(线程)
- 线程
- 线程的基本概念
线程是比进程更小的、能够独立运行的基本单位,线程比进程能更好地提高程序的并行执行速度,充分利用多处理机的优越性。引用线程主要是为了提高系统的处理效率,减少处理机的空转时间和进行调度时因保护CPU现场浪费的时间。
线程是进程中执行运算的最小单位,即执行处理机调度的基本单位。在引入线程的操作系统中,可以在一个进程内部进行线程的切换。
进程是资源分配的基本单位,同一进程的所有线程共享该进程的所有资源。线程是分配处理机的基本单位,真正在处理机上运行的是线程。
Linux系统中没有区分进程和线程,它们都使用相同的描述方法,使用相同的调度和管理策略。
- 线程的状态与转换操作
线程只有三种基本状态:执行、阻塞和就绪。针对线程的三种状态,存在五种操作来转换线程的状态。
- 派生(Spawn)
线程在进程中派生出来,也可再派生线程。用户可以通过相关的系统调用来派生自己的线程。Linux系统中,库函数clonc()和create_thread()分别用来派生不同执行模式的线程。
一个新派生出的线程具有相应的数据结构指针和变量,这些指针和变量作为寄存器上下文放在本线程的寄存器和堆栈中。新派生出来的线程被放入就绪队列。
- 调度(Schedule)
选择一条就绪线程进入执行状态。
- 阻塞(Block)
如果一个线程在执行过程中需要等待某个事件发生,则被阻塞。阻塞时,寄存器上下文,程序计数器,及堆栈指针都会得到保证。
- 激活(Unblock)
如果阻塞线程所等待的事件发生,则该线程被激活进入就绪队列。
- 结束(Finish)
一个线程执行结束,它的寄存器上下文以及堆栈内容等将被释放。
- 派生(Spawn)
- 引入线程的好处
- 易于调度。由于线程只作为独立调度的基本单位,同一进程的多个线程共享进程的资源,所以进程易于切换。
- 提高了系统的效率。通过线程可以有效地实现并发性,进程可创建多个线程执行同一个程序的不同部分。
- 创建一个线程比创建一个进程的开销少,创建速度快。
- 多线程的实现
线程分为用户级线程和内核级线程(也称核心级线程)。
- 用户级线程ULT(User Level Thread)。是由用户应用程序建立的,并由用户应用程序进行调度和管理。操作系统内核并不知道这些线程的存在,只对进程进行管理。因此这种线程与内核无关。
ULT的优点如下:
(1)应用程序中线程开关的时空开销远小于内核级线程的开销。
(2)线程的调度算法与操作系统的调度算法无关。
(3)因为与操作系统内核无关,所以适用于任何操作系统。
缺点为:
(1)在一个典型的操作系统中,有许多的系统请求正在被阻塞着,因此当一个线程执行一个系统请求时,不禁本线程被阻塞,该进程所有线程都被阻塞。
(2)在该方法的系统中,因为每个进程每次只能由一个线程在CPU运行,因此无法利用多处理器的优点。
- 内核级线程KLT(Kernel Level Thread)。内核级线程中所有线程的创建、管理与调度都由操作系统内核完成。一个应用程序可以按多线程的方式编写程序,当它提交给一个多线程的操作系统执行时,内核会为它创建一个进程和一个线程,线程可以创建新的线程。操作系统内核会为应用程序提供相应的系统调用和应用程序接口,以便用户程序可以创建、执行和撤销线程。Windows NT便是此类。
内核级线程优点:
(1) 内核可调度一个进程中的多个线程,使其在多个处理机上并行运行,从而提高系统的效率。
(2) 一个线程被阻塞时,其它线程仍可运行。
(3) 内核本身可以以线程方式运行。
缺点:
由于线程调度程序运行在内核态,而用户程序运行在内核态,因此同一进程中的线程切换要经过从用户态到内核态,再从内核态到用户态的两次模式的切换。于是催生了用户级线程与内核级线程相结合的模式。
- 用户级线程ULT(User Level Thread)。是由用户应用程序建立的,并由用户应用程序进行调度和管理。操作系统内核并不知道这些线程的存在,只对进程进行管理。因此这种线程与内核无关。
- Linux系统的线程
Linux的内核级线程也称为系统级线程。Linux可以同时支持内核级线程和用户级线程。大多数操作系统对于线程单独定义数据结构,采用单独的线程管理方式。而Linux将线程定义为“执行上下文”,它实际上只是进程的另外一个执行上下文而已,和进程享有相同的表示、管理和调度方式。执行上下文的定义可参考下面这个
用户空间的应用程序,通过系统调用,进入内核空间。这个时候用户空间的进程要传递 很多变量、参数的值给内核,内核态运行的时候也要保存用户进程的一些寄存器值、变量等。所谓的“进程上下文”,可以看作是用户进程传递给内核的这些参数以及内核要保存的那一整套的变量和寄存器值和当时的环境等。
那么什么是内核空间和用户空间呢?它们的一个主要的区别就是两者所能访问到的资源不同。
内核空间具有最高的权限,可以访问到所有资源。而用户空间只能访问受限权限,不能直接访问内存等硬件设备,必须通过系统调入到内核中才能访问这些特权资源。进程既可以在用户空间运行,又可以在内核空间中运行。进程在用户空间运行时,被称为进程的用户态,而陷入内核空间的时候,被称为进程的内核态。
要想从用户态转到内核态,需要通过系统调用来完成。
比如,当我们查看文件内容时,就需要多次系统调用来完成:首先调用 open() 打开文件,然后调用 read() 读取文件内容,并调用 write() 将内容写到标准输出,最后再调用 close() 关闭文件。
在这个过程中就发生了 CPU 上下文切换,整个过程是这样的:
1、保存 CPU 寄存器里原来用户态的指令位
2、为了执行内核态代码,CPU 寄存器需要更新为内核态指令的新位置。
3、跳转到内核态运行内核任务。
4、当系统调用结束后,CPU 寄存器需要恢复原来保存的用户态,然后再切换到用户空间,继续运行进程。所以,一次系统调用的过程,其实是发生了两次 CPU 上下文切换。(用户态-内核态-用户态)
本文引用部分的文字来自知乎文章:一文让你明白CPU上下文切换
原文地址:https://www.cnblogs.com/lunar-ubuntu/p/12233519.html