标签(空格分隔): 操作系统之哲学原理
进程的分身术——线程
线程就是我们为了让一个进程能够同时干多件事情而发明的分身术
在线程模式下,一个进程至少有一个线程,但也可以有多个线程。将进程分解为线程还可以有效利用多处理器和多核计算机。在没有线程的情况下,增加一个处理器并不能提高一个进程的执行速度,但是分解为多个线程,可以让不同的线程运行在不同的处理器上,从而提高进程的执行速度。
线程管理
线程管理与进程管理类似:要维持线程的各种信息。存放这些信息的数据结构称为线程控制表或者线程控制块
线程共享一个进程空间,许多资源是共享的,共享的资源是放在进程控制块。但是线程是不同的执行序列,总有不能共享的资源。而这些不能共享的资源就是放在线程控制块。
如何才能确定到底哪些资源是同一进程的不同线程共享?哪些是不共享的呢?
规律就是:应当让共享的资源越多越好。因为我们发明线程的目的就是为了协作,共享是我们不懈的追求。
资源是否应该设置为共享的评判标准
如果某资源不独享会导致运行错误,则该资源就应该独享,否则就应该共享
线程共享的资源:
- 地址空间
- 全局变量
- 文件
- 子进程
- 闹铃
- 信号及信号服务程序
- 记帐信息
线程独享资源:
- 程序计数器
- 寄存器
- 栈
- 状态字
线程模型的实现(实际上也是线程调度的实现)
线程是在进程基础上的二次并发
与进程一样,线程本身对应某种物理实现,也需要存储和调度。器存储是直接附于进程存储方案上。
线程调度的两种方式:
- 可以由进程负责:用户态实现
- 可以由操作系统负责:内核态实现
用户态和内核态的判断:以线程表所处的位置为依据,位于内核的叫内核态实现,位于用户层的叫用户态实现
注意到,我们在研究进程的时候没有讨论进程的实现方式(用户态实现还是内核态实现),毕竟进程在CPU实现并发,而CPU是由操作系统直接管理的。因此只存在内核态
内核态线程实现
操作系统如何管理线程?
与进程类似,要管理线程就要持有线程的所有信息,将线程控制块存放在操作系统的内核空间,这样操作系统就同时持有进程控制块和线程控制块。
操作系统管理线程(内核态线程)实现有什么好处?
- 最重要的好处是用户编程简单
- 如果一个线程执行了阻塞操作,操作系统可以从容的调度另一个线程执行。操作系统可以监控所有的线程
内核态线程实现的缺点?
- 效率较低,线程在内核态实现,每次线程切换都要进入内核,花销比较大。
- 内核态实现占用内核稀缺资源,因为操作系统需要维护线程表,本来之前操作系统的内存空间一旦家在结束后就是固定的了,随着线程数量的增加,内核空间将被迅速耗尽。如果需要建立进程但是内核空间已经满了怎么办?“死掉”,投降才是真本事。
- 最致命的是:内核态的实现需要修改操作系统。要向人家证明线程管理是有用的,于是先实现用户态实现。
用户态线程实现
用户态实现意味着什么?用户态实现是什么意思?
就是用户自己做线程的切换,自己管理线程的信息,而操作系统无需知道线程的存在
用户态是如何实现线程调度的尼?
就是用户自己写一个执行系统作为调度器,也就是说除了正常执行任务的线程外,还有一个专门负责线程调度的线程。大家都在用户态下执行,都是小弟,谁也不比谁更占优势,要想取得CPU的控制权只能靠大家的合作。
用户态实现线程的优点
- 灵活性很强,操作系统无需知道线程的存在,在任何操作系统上都能应用
- 线程切换很快
- 不用修改操作系统,实现容易
用户态实现的缺点
- 编程变得很诡异,我们必须仔细斟酌什么时候让出CPU给别的线程使用,而让出的时机对线程的效率和可靠性有很大的影响
- 用户态线程实现无法完全达到终极目的:进程级多道编程
- 因为在线程的执行过程中,如果一个线程受阻,将无法把控制权交出来(因为受阻后无法执行交出CPU的命令了),这样的话,整个进程都无法进行。操作系统马上过来收回控制权并交给其他进程使用。于是一个线程受阻导致整个进程受阻,线程对进程的分身计划失败。
想办法挽救用户态实现
不让线程阻塞
- 将所有的操作改成非阻塞操作,比如读写磁盘,收发数据包,,。,但是不可行呀,因为修改系统调用就要修改操作系统,这是其一。第二是没有操作系统的人员帮忙。最后一点是,很多系统调用的语义里面就包括阻塞,说明部分的阻塞式其正确运行的前提。综上,改为非阻塞操作的做法不可取
- 不让线程调用阻塞操作。只需要在线程进行任何系统调用之前确认一下该调用是否会发生阻塞。写一个包裹将系统调用包裹起来,这个包裹专门用于检查。
进程阻塞后想办法激活受阻进程的其他线程
这种方式非常依赖于操作系统,因为进程阻塞后CPU控制权就交到了操作系统的手中,但是想办法让操作系统在进程切换的时候先不去切换进程,而是通知受阻进程的执行系统,询问是不是还有其他可以执行的线程,要是还有的话,就把控制权交过去,从而调度另一个线程执行。这就是传说中的调度器激活
但是这种做法也有两个缺点:
- 要修改操作系统,但是修改的范围比较小,还是可以忍受
- 这种操作系统调用用户态执行系统的做法违反了我们的层次架构原则,
操作系统的线程实现模型
因为用户态与内核态都有缺陷,于是就中庸之道。
用户态的执行系统负责内部线程在非阻塞时的切换,内核态的操作系统负责阻塞线程的切换,内核态线程数量较少,用户态线程数量较多。用户态线程可以多路复用到内核态线程上
多线程的关系
退出多线程的目的就是为了实现进程级并发。线程在共享地址空间的时候也会产生矛盾:
- 线程之间如何通信?
- 线程之间如何同步?
讨论:从用户态进入内核态
什么情况会造成一个线程从用户态进入到内核态?
- 如果程序在·运行过程中发生中断或者异常,系统将自动切换到内核态来运行或异常处理机制
- 程序进行系统调用也将造成从用户态进入到内核态的转换
讨论:线程的困惑—–确定性与非确定性
系统运行可以说是确定性和非确定性的,确定是说的每个程序的运行结果基本确定,不确定是指每个程序的运行顺序不确定,而每个单线程执行效率和执行正确性均存在不确定性。
从某种程度上来说,线程与流水线分别是软件层和硬件层不确定性的根源。但其带来的操作系统,编译系统和指令集系统结构的高度复杂是否值得,也许不容易被回答!!
国际惯例:总结
线程是进程级并发。围绕线程有很多的问题值得我们关心。