线程

来源:互联网 发布:之知为知之不知为不知 编辑:程序博客网 时间:2024/06/11 18:28

多线程

进程的概念包括两个特点:

  • 资源所有权:一个进程包括一个存放进程映像的虚拟地址空间;进程映像是程序、数据、栈和进程控制块中定义的属性的集合。一个进程总是拥有对资源的控制或所有权,这些资源包括内存、I/O通道、I/O设备和文件等。操作系统提供保护功能,以防止进程之间发生不必要的与资源相关的冲突。
  • 调度?执行:一个进程沿着可以通过一个或多个程序的执行路径执行。其执行过程可能与其他进程的执行过程交替执行。因此,一个进程具有一个执行状态(运行、就绪和阻塞等)和一个被分配的优先级,它是一个可被操作系统调度和分派的实体。

多线程是指操作系统在单个进程内支持多个并发执行路径的能力。

在多线程环境中,进程被定义成资源分配的单位和一个被保护的单位。与进程相关联的有:

  • 存放进程映像的虚拟地址空间
  • 受保护地对处理器、其他进程(用于进程间通信)、文件和I/O资源(设备和通道)的访问

在一个进程中,可能有一个或多个线程,每个线程有:

  • 线程执行状态(运行、就绪等)
  • 在未运行时保存的线程上下文:从某种意义上看,线程可被视为进程内的一个独立操作的程序计数器
  • 一个执行栈
  • 用于每个线程局部变量的静态存储空间
  • 与进程内的其他线程共享的对进程的内存和资源的访问

从进程管理的角度说明了线程和进程的区别:这里写图片描述

在单线程进程模型中,进程的表示包括它的进程控制块和用户地址空间,以及在进程执行中管理调用/返回行为的用户栈和内核栈。当进程正在运行时,处理器寄存器被该进程所控制;当进程不运行时,这些处理器寄存器中的内容将被保存。在多线程环境中,进程仍然只有一个与之关联的进程控制块和用户地址空间。但是每个线程都有一个独立的栈,还有独立的控制块用于包含寄存器值、优先级和其他与线程相关的状态信息。

因此,进程中的所有线程共享该进程的状态和资源,它们驻留在同一块地址空间中,并且可以访问到相同的数据。当一个线程改变了内存中的一个数据项时,其他线程在访问这一数据项时能够看到变化后的结果。如果一个线程以读权限打开一个文件,那么同一个进程中的其他线程也能够从这个文件中读取数据。

线程提高了不同的执行程序间通信的效率。在大多数操作系统中,独立进程间的通信需要内核的介入,以提供保护和通信所需要的机制。但是,由于在同一个进程中的线程共享内存和文件,它们无需调用内核就可以互相通信。

有些活动影响着进程中的所有线程,操作系统必须在进程一级对它们进行管理。例如,挂起操作涉及把一个进程的地址空间换出内存来为其他进程的地址空间腾出位置。因为一个进程中的所有线程共享同一个地址空间,所以它们都会同时被挂起。类似的,进程的终止会导致进程中所有线程的终止。

线程状态

四种与线程状态改变相关的基本操作:

  • 当派生一个新进程时,同时也为该进程派生了一个线程。随后,进程中的线程可以在同一个进程中派生另一个线程,并为新线程提供指令指针和参数;新线程拥有自己的寄存器上下文和栈空间,且被放置在就绪队列中。
  • 阻塞:当线程需要等待一个事件时,它将被阻塞(保存它的用户寄存器、程序计数器和栈指针),此时处理器转而执行另一个就绪线程
  • 解除阻塞:当阻塞一个线程的事件发生时,该线程被转移到就绪队列中
  • 结束:当一个线程完成时,其寄存器上下文和栈都被释放

线程同步:一个进程中的所有线程共享同一个地址空间和诸如打开的文件之类的其他资源。一个线程对资源的任何修改都会影响同一个进程中其他线程的环境。因此,需要同步各种线程的活动,以便它们互不干涉且不破坏数据结构。例如,如果两个线程都试图同时往一个双向链表中增加一个元素,则可能会丢失一个元素或者破坏链表结构。

线程分类

用户级和内核级线程

用户级线程:在一个纯碎的用户级软件中,有关线程管理的所有工作都由应用程序完成,内核意识不到线程的存在。任何应用程序都可以通过使用线程库被设计成多线程程序。线程库是用于用户级线程管理的一个例程包,它包含用于创建和销毁线程的代码、在线程间传递消息和数据的代码、调度线程执行的代码,以及保存和恢复线程上下文的代码。

下面阐述线程调度和进程调度的关系

进程在执行线程库中的代码时可以被中断,或者是由于它的时间片用完了,或者是由于被一个更高优先级的进程所抢占。因此在中断时,进程有可能处于线程切换的中间时刻,即正在从一个线程切换到另一个线程。当该进程被恢复时,线程库得以继续运行,并完成线程切换和把控制转移到该进程中的另一个线程。

内核级线程:在一个纯粹的内核级线程软件中,有关线程管理的所有工作都是由内核完成的,应用程序部分没有进行线程管理的代码,只有一个到内核线程设施的应用程序编程接口(API)。

Linux的进程和线程管理

Linux任务

Linux中的进程或任务由一个task_struct数据结构表示。这个task_struct数据结构包含以下各类信息:

  • 状态:进程的执行状态(执行态、就绪态、挂起态、停止态,僵死态)
  • 调度信息:Linux调度进程所需要的信息。
  • 标识符:每个进程有一个唯一的进程标识符,还有用户标识符和组标识符
  • 进程间通信:
  • 链接:每一个进程都有一个到其父进程的链接及到其兄弟进程的链接和到所有子进程的链接。
  • 时间和计时器:包括进程创建的时刻和进程所消耗的处理器时间总量
  • 文件系统:包括指向被该进程打开的任何文件的指针和指向该进程当前目录与根目录的指针
  • 地址空间:定义分配给该进程的虚拟地址空间
  • 处理器上下文:构成该进程上下文的寄存器和栈信息

进程的执行状态:

  • 运行
  • 可中断:这是一个阻塞状态,此时进程正在等待一个事件(如一个I/O操作)的结束、一个可用资源或另一个进程的信号
  • 不可中断:这是另一种阻塞状态。它与可中断状态的区别是,在不可中断状态下,进程正在直接等待一个硬件条件,因此不会接受任何信号。
  • 停止:进程被终止,并且只能由来自另一个进程的主动动作恢复。 例如,一个正在被调试的进程可以被置于停止态
  • 僵死:进程已经被终止,但是由于某些原因,在进程表中仍然有它的任务结构。

Linux线程

在Linux中通过复制当前进程的属性可创建一个新进程。新进程被克隆出来,以使它可以共享资源,如文件、信号处理程序和虚存。当两个进程共享相同虚存时,它们可以被当做是一个进程中的线程。但没有为线程单独定义数据结构,因此,Linux中进程和线程没有区别。

当Linux内核执行从一个进程到另一个进程的切换时,它将检查当前进程的页目录地址是否和将调度的进程的相同。如果相同,那么它们共享同一个地址空间,所有此时上下文切换仅仅是从代码的一处跳转到代码的另一处。

虽然属于同一进程组的被克隆的进程共享同一内存空间,但它们不能共享同一个用户栈。所以clone()调用为每个进程创建独立的栈空间。