操作系统学习笔记 线程
进程和线程
进程概念的两个特点:
资源所有权:一个进程包括一个存放进程映像(包括程序,数据,栈和进程控制块)的虚地址空间。进程拥有对资源的控制和所有权,包括主存,I/O通道,I/O设备和文件等;
调度/执行:一个进程沿着通过一个或者多个程序的一条执行路径执行,其执行过程可能与其他进程的执行过程交替进行。因此,一个具备执行状态和优先级的进程是操作系统调度并分配的实体;
现代操作系统为了区分这两个特点,分派的单位被称为线程或轻量级进程(LightWeight Process,LWP),而拥有资源所有权的单位被成为进程。
多线程
多线程是指在一个进程中可以执行多个线程的能力。传统的操作系统使用的是单线程方式。UNIX支持多用户进程,但是每个线程只有一个线程。Windows支持的是多进程,每个进程可以有多个线程的方式。
在多进程环境中,进程被定义为资源分配和保护的单位。与进程相关联的有:
存放进程映像的虚拟地址空间;
受保护的访问处理器;
其他进程(进程间通信);
文件;
I/O资源;
在一个进程中,可能有一个或者多个线程,每个线程有:
线程执行状态;
在未运行时保存的线程上下文;
一个执行栈;
用于每个线程局部变量的静态存储空间;
对所属进程的内存和资源的访问,并和该进程中的其他线程共享这些资源;
在单线程模型中,进程包括进程控制块,用户地址空间,以及在执行过程中的管理调用/返回行为的的用户栈和内核栈。在多线程模型中,仍然有与进程相关的进程控制块和用户地址空间,但是每个线程都有一个独立的栈,还有独立的控制块包含寄存器,优先级和其他线程相关的状态信息。
进程中的所有线程共享该进程中的状态和资源,它们处于同一块地址空间中,并可以访问相同的数据。例如,一个线程打开一个文件,那么同一个进程的其他线程都可以使用这个文件句柄。
线程在性能方面的优点:
1. 在一个进程中创建一个新的线程比创建一个新的进程要快得多;
2. 终止一个线程比终止一个进程的所花的时间要少;
3. 同一个进程中线程的切换比进程间的切换要快;
4. 同一个进程中线程间的通信更有效率。在大多数操作系统中,进程间的通信需要内核介入,以提供通信机制和保护机制。但是在同一个进程中,由于线程可以共享内存和
文件,它们间的通信就不需要使用内核;
即使在单处理器中,构造多线程对简化逻辑上完成不同功能的程序,同样很有用。使用线程的例子:前台和后台操作;异步处理;加速执行;模块化程序结构。
在支持线程的操作系统中,调度和分派是以线程为单位,因此,大多数与执行相关的信息可以保存在线程级的数据结构中。但是,有些活动影响着进程中的所有线程,操作系统必须在进程一级对它们进行管理。例如,挂起会把一个进程的地址空间换出主存,因为一个进程中的所有线程都会共享同一个地址空间,所以他们都会被挂起。同样,进程的终止会导致进程中所有线程的终止。
线程状态
和进程一样,线程的主要状态有:运行态,就绪态和阻塞态。挂起态对线程没有意义,因为它属于进程一级的概念。与线程状态相关的四种基本操作:
派生:创建新进程的时候,会创建一个线程;进程中的线程也可以创建一个新的线程。创建新线程的时候,会为它提供指令指针和参数。新建的线程有自己的寄存器上下文
和栈空间,被放置在就绪队列中;
阻塞:当线程需要等待一个事件时,它会被阻塞(保存它的用户寄存器,程序计数器,栈指针),然后处理器转到同一个进程中的其他线程或者不同进程的其他就绪线程继
续执行;
解除阻塞:当阻塞一个线程的事件发生时,该线程被转移到就绪队列中;
结束:当一个线程完成时,其寄存器上下文和栈都被释放;
在单处理器中,多个进程中的多个线程可以交替执行。在当前正在运行的线程阻塞或者时间片用完的时候,执行另一个线程。
线程同步
一个进程中的所有线程共享同一个地址空间和资源。一个线程对资源的修改会影响其他线程对资源的使用,因此,需要同步各个线程的活动,以确保它们互不干扰和破坏数据。
用户级线程(User-Level Thread)和内核级线程(Kernel-Level Thread)
用户级线程:有关线程的管理工作都由用户程序控制,内核并不知道线程的存在。应用程序可以通过线程库设计多线程程序。线程库是一个用于管理用户级线程的例程包,它包括创建和销毁线程代码,调度线程执行的代码,在线程间传递消息和数据的代码,以及恢复线程上下文的代码。
默认情况下,应用程序从单线程开始,并从该线程开始执行。应用程序和该进程分配给一个由内核管理的进程,这个进程可以在运行的任何时刻,创建一个新的线程,需要通过调用线程库中的派生(spawn)例程完成。通过过程调用,控制权转移给spawn例程,它为新线程创建一个数据结构,然后再使用调度算法,将控制权转移给该进程中处于就绪态的一个线程。当控制权转移给线程库的时候,需要保存当前线程的上下文,然后当控制权再次转移到一个线程的时候,需要恢复那个线程的上下文。此时的线程上下文包括用户寄存器的内容,程序计数器和栈指针。以上活动都发生在一个用户进程内,而内核并不知道这些活动。在这种情况下,内核是以进程为单位进行调度,并给该进程指定一个执行状态(运行态,就绪态,阻塞态等)。
例子,线程调度和进程调度:
进程B有线程1和线程2两个执行线程,假设进程B现在在它的线程2中执行,则可能发生以下任何一种情况:
1. 线程2执行了系统调用,阻塞了进程B。例如,进行一次I/O调用,这会导致把控制权转移给内核,内核启动了I/O操作,把进程B置为阻塞态,并切换到另一个进程。此时,在线程库中维护的数据结构中,进程B的线程2仍处于运行状态,但是线程2并不会真正的被处理器执行;
2. 内核确认进程B用完了它的时间片,时钟中断把控制权转移给内核,内核把进程B置为就绪态并切换到另一个进程。此时,在线程库中维护的数据结构中,进程B的线程2仍处于运行状态,但是线程2并不会真正的被处理器执行;
3. 线程2执行到某处,需要进程B的线程1执行某些动作。线程2进入阻塞状态,线程1从就绪态转到运行态,进程本身仍然是运行态;
在1和2两种情况中,当内核把控制权切换回进程B时,会恢复线程2的执行。需要注意,进程在执行线程库中的代码时,也可能被中断,或者因为时间片用完,或者因为被一个高优先级的进程抢占。因此在中断的时候,进程可能处于从一个线程切换到另一个线程的中间时刻,即正从一个线程切换到另一个线程。当该进程被恢复时,将继续在线程库中执行,完成线程的切换,并把控制权转移给该进程中的另一个线程。
用户级线程相比内核级线程的优点:
1. 一个进程中的所有线程的数据结构都由进程管理,线程的切换不需要切换到内核模式,较少了从用户模式切换到内核模式,又从内核模式切换到用户模式的开销;
2. 调度方式由应用程序控制。采用简单的轮询调度算法,还是基于优先级的调度算法,都有应用程序控制,而且也不会干扰操作系统的调度器;
3. 用户级线程更容易跨平台,不需要修改底层内核,就可以运行在任何操作系统中;
用户级线程相比内核级线程的缺点:
1. 由于许多系统调用都会引起阻塞,因此,当一个进程中的一个用户级线程被阻塞时,也同时会阻塞整个进程;
2. 在纯粹的用户级线程策略中,一个多线程的应用程序不能享受多处理器技术。内核一次给该进程分配一个处理器,即同一时刻,进程中只会有一个线程被执行;
解决以上缺点的方式:
1. 将应用程序改写成多进程而不是多线程的应用程序,但是由线程切换变为进程切换,导致开销过大;
2. 克服线程阻塞的方法是使用一种称为jacketing的技术。该技术的目的是将一个会产生阻塞的系统调用转换为一个无阻塞的系统调用。例如,线程不直接调用一个系统 I/0例程,而是调用一个应用级的I/0jacket例程,该例程会检测I/O设备是否可用。如果不可用,就阻塞该线程并就切换到该进程中的另一个线程执行,当阻塞的线程重新获 得控制权的时候,再次检测I/O设备的可用性。
内核级线程:在一个纯粹的内核级线程系统中,有关线程管理的工作都由内核完成,应用程序不涉及线程的管理。内核仅为应用程序提供有关内核级线程的编程接口。内核为一个进程的所有线程维护上下文信息,线程调度也是在内核中完成的。该架构解决了用户级线程的两个缺点:内核可以把一个进程中的所有线程同时调度到多个处理器中;如果一个进程中的一个线程被阻塞,内核可以调度该进程中的另一个线程,不会导致阻塞整个进程。
内核级线程另一个优点是:内核例程自身也可以使用多线程。
内核级线程相比于用户级线程的缺点:同一个进程中线程的切换需要切换到内核模式。
可以看出,使用内核级线程比使用单线程的进程有明显的速度提高,使用用户级线程比内核级线程又有一定的提高。如果用户级线程的大多数切换都需要切换到内核模式,用户级线程的方案也不会比内核级线程的方案好多少。
组合方案
某些操作系统提供了一种组合用户级线程和内核级线程的方式。线程的创建依然在用户空间中完成,线程的调度和同步也由应用程序控制。但是一个应用程序的一些线程可以映射到内核级线程上,应用程序可以根据实际情况,调整需要映射到内核级线程的数量以达到最佳效果。如果设计合理,应用程序的多个线程可以并行运行,并且引起阻塞的线程不会阻塞整个进程。这样就可以兼顾用户级线程和内核级线程的优点,并避免它们各自的缺点。
线程和进程的关系
1:1关系:传统的单线程的进程中,每个进程只有一个线程;
M:1关系:现代操作系统中大多使用这种方式,一个进程可以具备一个或多个线程,进程具备地址空间和资源的所有权;
M:N关系:在实验性操作系统TRIX中,研究线程和进程的多对多的关系。该操作系统中有域的概念,域是一个静态的实体,包括一个地址空间和发送,接受消息的端口。线程是一个执行路径,含有执行栈,处理器状态和调度信息。多个线程可以在一个域中执行。也可以在多个域中执行,在这种情况下,线程可以从一个域移动到另一个域;
1:M关系:从用户的角度看,线程是一个活动单位,进程是一个虚拟地址空间和相关的进程控制块。线程被创建后,它通过调用进程中的一个程序入口点,开始在进程中执行。线程可以从一个地址空间转移到另一个地址空间;