【操作系统原理】第二章-进程和线程

进程和线程

进程

什么是进程

  在操作系统中,操作系统需要对各种资源进行管理,大概可以分为以下几类:内存,文件,磁盘,进程。所谓进程就是操作系统有序管理应用程序的执行的方式,来保证以下几点:
  1、所有资源对多个应用程序是可用的。
  2、物理处理器在多个应用程序中切换,保证所有程序都在执行中。
  3、处理器和I/O设备都能得到充分的利用。
  因此所有现代操作系统都依赖于一个模型,在该模型中,一个应用程序对应一个或多个进程。进程的定义有以下几条:
  1、一个正在执行的程序。
  2、一个正在计算机上执行的程序实例。
  3、能分配给处理器并由处理器执行的实体。
  4、由一组执行的指令、一个当前状态和一组相关的系统资源表征的活动单元。
  简单来说什么是进程,进程就是正在执行中的程序。而在操作系统中,操作系统为了更好的描述一个进程,于是将进程视为一些元素组成的实体,而其中最重要的两个元素是程序代码数据集。一般来说一个程序有了程序代码和数据集就可以顺利执行了,但是操作系统说还不够,为了满足操作系统对进程的控制,例如调度,中断,执行等操作,操作系统将每个进程描述为一个叫做进程控制块(PCB) 的数据结构,在PCB中存储着操作系统对控制一个进程所需要的全部信息,可以根据PCB找到程序代码,找到程序的数据,程序获得的资源等等。所以一个进程对于操作系统来说就是一个PCB。PCB中所存储的信息我们在下文中有详细介绍。
  知道了操作系统是通过PCB管理进程的后接下来讨论进程的状态。

进程状态

两状态模型

  在多道操作系统中,我们假设现在的处理器都是单核的即同时只能有一个进程正在处理器中执行,但是操作系统为了让用户看上去所有进程都在“同时”运行于是他在操作系统中设置了时间片,即一个进程可以连续执行的最大时间,并且按照调度算法快速在不同进程间进行切换执行,执行中的进程状态为运行态,而未执行的则成为非运行态,其中关系如下图。

两状态模型

  同时我们可以把非运行态的进程组织到队列中,每次切换进程从队列中调出一个进程开始运行,而切换下来的进程要么重新加入队列要么执行完毕退出,如下图。
两状态模型

  这里提一下可能导致创建新进程的事件和可能导致进程退出的事件。
  进程创建由以下4种事件触发:
  1、新的批处理作业。新的批处理作业进入操作系统肯定会创建新的进程来执行批处理作业。
  2、用户登录。用户登录往往也会创建新进程来执行用户指令,之所以使用进程是为了将用户与操作系统隔离,一个用户指令的崩溃不会影响到其他用户乃至操作系统。
  3、为提供服务由操作系统创建。有时操作系统为了提供一个服务也会创建新的进程,例如用户进程请求打印一个文件,系统可以创建一个管理打印的进程,进而使请求进程可以继续执行。
  4、由现有进程派生。当现有进程引发另一个进程的创建时,操作系统也会创建新的进程,这就是进程派生,这往往很有用,派生出的进程可以帮助主进程处理数据,组织数据等等。
  进程的终止由以下14种事件触发:
  1、正常完成。正常结束运行。
  2、超过时限。进程运行超过规定的时限。
  3、无内存可用。系统无法满足进程需要的内存。
  4、超出范围。进程试图访问非法的内存单元。
  5、保护错误。进程试图使用不允许使用的资源或文件。
  6、算术错误。进程试图进行被禁止的运算。
  7、时间超出。进程等待某一事件发生的时间超过了规定的时间。
  8、I/O失败。在输入输出期间发生错误。
  9、无效指令。进程试图执行一个不存在的指令。
  10、特权指令。进程试图使用为操作系统保留的指令。
  11、数据误用。错误类型或未初始化的一块数据。
  12、操作员或操作系统干涉。操作员或操作系统终止进程。
  13、父进程终止。在某些操作系统中,父进程终止时操作系统会自动终止该进程的所有子进程。
  14、父进程请求。父进程要求终止其子进程。

五状态模型

  如果所有进程都做好了准备,操作系统会从未运行队列中以轮转的方式调度每个进程。但是这里有个问题,如果并非所有进程都做好了准备呢?也许未运行的进程中有些进程正在等待某一事件的发生,也就是处于阻塞,因此单纯的对所有未运行的进程进行轮转是不科学的,应该对所有已经就绪的进程进行调度。解决这种问题的最好方法就是将未执行进程队列拆分为两个队列分别是就绪队列和阻塞队列,由此进程的状态由2状态变为了3状态,此外还要增加新建退出态,这十分有必要。改进后的状态模型如下图所示。

五状态模型

  运行态:进程正在执行。
  就绪态:进程做好了准备,随时接收调度。
  阻塞态:进程在等待某些事件的发生,在事件发生前不能执行,如I/O操作。
  新建态:刚刚新建的进程,操作系统还未将其加载至内存,通常是PCB已经创建但是还并未加载到内存中的新程序。
  退出态:操作系统从可执行进程组中释放的进程。
  新建态与推出态十分有必要。在一个进程被新建时它并非绝对会被调入内存,通常是分两步,首先创建该进程的PCB,并与之关联,但是此时可能面临内存不足或者操作系统限制了最大进程数导致这个进程还无法被调入进程,因此该进程被暂时留在新建态,在这个状态的进程PCB已经创建并且加载进内存,但是进程的代码和数据往往还留在外村中等待加载。
  退出态也和新建态同理。当进程因为某些人原因要被终止时,此时并不直接将其调出内存,首先操作系统会停止执行该进程的代码,但是暂时让其留在内存中,因为某些辅助程序或是支持程序会来记录该进程相关数据和信息,此时进程停留在退出态。等相关程序收集完所需信息后,再将其所有数据从内存中移除。
  关于阻塞,就绪和运行三种状态的转换更为普遍和便于理解。操作系统从就绪队列中调度某个进程进入运行态运行,当时间片结束后操作系统将其放回就绪态执行其他进程,如果在执行期间进程必须等待某些事件,便将其放入阻塞态,然后调度其他进程执行。当该进程等待的事件完成后操作系统则将其放回就绪态等待调度。
  但是此时又有一个问题,如果所有阻塞进程放在同一个阻塞队列中,当一个事件完成后操作系统不得不扫描整个队列找到那些等待该事件的进程然后将其放进就绪队列中,这样的效率十分低下,因此通常是为每一个事件创建一个阻塞队列。同理当按照优先级进行调度时,也会将优先级相同的进程放进一个就绪队列,避免扫描等低效的做法,这是典型的用空间换时间的做法。
五状态模型

七状态模型

  在介绍七状态模型前,我们思考一个问题,三个基本状态(就绪,运行和阻塞)的所有进程都必须存储在内存中,此时就可能出现一种情况,即所有进程都处于阻塞态,没有就绪状态的进程,此时又开始了处理器的空转,处理器没办法执行进程只能开始等待进程从阻塞态恢复就绪态,并且加入此时又有新的进程处于新建态,由于内存不足,处于新建态的进程也没办法进入内存无法执行,这是一个十分致命的处理器空转问题,解决这个问题有两个方法:扩大内存,很显然成本太高了;将阻塞态的进程暂时调出内存放回磁盘,来让新建态的进程有足够内存进入就绪态开始处理器的调度和运行。
  但是在将一个阻塞态进程挂起后,操作系统可以选择接纳一个新建态进程进入就绪队列,也可以选择将一个之前挂起的进程恢复就绪态,并且为了减少操作系统的负载操作系统更倾向于后者。但是处于挂起的进程也可能还并未接触阻塞,将一个阻塞进程放回内存没有任何意义,于是更好的方法是将挂起区分为两个状态即就绪/挂起态阻塞/挂起态,这样每次操作系统就只需要考虑是否应该把进程从就绪/挂起态换回就绪态即可。完整的七状态模型如下:

七状态模型

  阻塞/挂起态:进程在外存中并等待一个事件。
  就绪/挂起态:进程在外存中,但只要载入内存即可开始运行。
  并且操作系统允许进程从就绪变为就绪/挂起态,或从阻塞/挂起态变更为阻塞态,只是这样做的意义不大,因此并不会这样做。
  导致进程被挂起的事件有以下几种:
  1、交换。为了释放内存空间。
  2、其他OS原因。操作系统可能会挂起后台进程或者工具进程,或挂起可能会导致问题的进程。
  3、交互式用户请求。用户希望挂起一个进程来进行调试。
  4、定时。进程可被周期性的执行,并在等待下一个时间间隔时挂起。
  5、父进程请求。父进程可能希望挂起后代进程的执行,以检查或修改挂起的进程。

进程描述

进程在操作系统中的描述方式

  操作系统可以管理计算机内的任何资源,包括内存、设备、文件和进程但是操作系统是如何管理的呢?对于操作系统来说,所有的资源都被组织成对应的数据结构,内存对应内存表,设备对应设备表,文件对应文件表,进程自然也有进程表,如下图。接下来我们将详细介绍操作系统如何描述操作系统中的所有进程,也就是进程表的结构。

进程描述

  如上图所示,进程表中存放着一个一个进程,而每个进程项都指向一个进程映像,什么是进程映像呢?我们说一个进程最基本的元素是用户代码以及元素集,初次之外还有若干操作系统控制进程所需的信息,这些信息都存放在进程映像中,并且还有一个进程用于存储临时数据的栈,因此进程映像中的典型元素可以概括如下:
  1、用户数据。用户空间中的可修改部分,包括程序数据、用户栈区域和可修改的程序。
  2、用户程序。待执行的程序。
  3、栈。每个进程有一个或多个后进先出栈,栈用于保存参数、过程调用地址和系统调用地址。
  4、进程描述块。操作系统控制进程所需的数据。
  有了以上信息就有了一个进程调度,运行所需的全部数据,这些数据在内存中有可能是连续的也有可能是不连续的,这根据操作系统内存管理的方式来决定,但是但从操作系统描述管理进程方式来看,操作系统通过在内存中的主进程表,每一表项都至少包含一个指向进程映像的指针,通过进程表操作系统可以找到控制进程所需的全部数据。

进程属性

  我们知道了操作系统通过进程表和进程映像描述进程,进程映像中的用户数据和用户程序都是根据用户所写的程序而定的,栈也只是用来保存参数调用地址所用的临时储存空间,但是其中我们要尤为重要介绍PCB(进程描述块)。正如之前所说进程描述块中储存了操作系统控制进程所需的一切信息,对于操作系统来说拿到进程控制块就可以控制进程进行调度等操作,那么进程控制块中到底存放了进程哪些信息呢?
  不同操作系统的PCB中组织的信息是不同的,但是PCB中所有操作系统都需要的共用基础信息一共8种:
  1、标识符:PID,与进程相关的唯一标识符。
  2、状态:进程状态,状态的划分是接下来介绍的重点。
  3、优先级:与进程调度有关的优先级。
  4、程序计数器:程序中即将执行的下一条指令的地址。
  5、上下文数据:进程执行时处理器的寄存器中的数据。
  6、内存指针:包括程序代码及相关数据的指针,以及与其他进程共享内存的指针。
  7、I/O状态信息:进程的I/O请求,分配给进程的I/O设备和进程使用文件
  8、记账信息:包括处理器时间综合、使用的时钟数综合、时间限制、记帐号等。
  这些信息一共可以分为三类进程标识信息处理器状态信息进程控制信息。进程标识信息典型的就是标识符,他是一个操作系统中唯一标识一个进程的基本索引。处理器状态信息由处理器寄存器的内容组成,中断进程时,必须保存寄存器中的所有信息,以便进程恢复时使用,这些信息就保存在PCB中,典型的有上下文数据。进程控制信息是操作系统控制和协调各种活动进程所需的额外信息,例如进程优先级。
  根据以上的介绍,进程映像在虚存中的结构基本如下图所示,但是具体情况还得视操作系统的具体管理方案而定。

进程描述

进程控制

执行模式

  操作系统必须保证自己的安全性,因此再让用户进程运行时并不能将所有的权限交给用户,这样操作系统很可能会被进程搞到崩溃,最好的方式是操作系统将一些特权指令不进行公开,用户进程不能直接执行这些指令,但是操作系统允许进程发起使用特权指令的请求,然后再有操作系统自己代替用户执行指令,这样可以大大增强操作系统的健壮性,同时内存也并不会让用户进程都可以访问到,如果修改了操作系统即内核可能会发生致命错误,于是这中间操作系统加入了种种限制,先从一个进程的执行上来说,操作系统将其分为了两种模式用户模式(用户态/目态)内核模式(内核态/管态)
  用户进程默认是在用户模式下运行,在用户模式下进程的权限受到控制,而如果发生了一些特殊事件,例如请求系统调用模式会从用户模式转换为内核模式。说白了用户模式即处理器在执行用户代码,内核模式即处理器目前在执行内核代码。那么这样有出现两个问题,处理器如何知道它正在什么模式下执行?一般情况下,程序转太子中通常存在一个只是执行模式的位,该位会因模式的变化而变化,也就是说在处理器的一个寄存器中存储了当前处理器处于什么模式下的信息。例如Intel Itanium处理器中就有一个包含2位CPL(当前特权级别)字段的处理器状态寄存器用于存储模式信息。

进程创建

  操作系统在创建一个进程的时候会进行哪些工作呢?当操作系统决定创建一个进程时会执行以下操作:
  1、为新进程分配一个唯一的进程描述符。
  2、为进程分配空间。
  3、初始化PCB。
  4、设置正确的链接。例如将进程放到就绪队列中,而就绪队列是一个链表,此时就需要在数据结构上进行连接。
  5、创建或扩充其他数据结构。例如创建账单和评估性能。

进程切换和模式切换

进程切换

  进程切换在什么时候发生呢?理论上在任何时刻只要操作系统拿到控制权就可以进行进程切换,那么什么时候操作系统会重新拿到控制权呢?
  这里首先考虑中断的情况,而中断又可分为两种:中断陷阱。中断一般是与当前正运行进程无关的某种外部事件相关,例如完成了一次I/O操作,中断处理器完成一些基本的辅助操作后将控制权转给与已发生的终端相关的操作系统历程,简单来说中断的发生属于正常的事件,不过是操作系统暂时停止执行当前进程转为处理另外一件更加紧急的事情。例如以下三种中断:
  1、时钟中断。当前进程时间片到期,转为从就绪队列中调度新的进程开始运行。
  2、I/O中断。某一I/O完成,操作系统判断是否有正在等待该I/O的进程,如果有将其放回就绪态,随后操作系统根据调度算法调度合适的进程继续运行。
  3、缺页中断。处理器遇到一个引用不存在内存中的虚存地址时,此时会发生缺页中断,然后操作系统要根据算法将访问的页调入内存,这块的处理与操作系统对内存管理有很大关系。
  除了中断,陷阱也有可能会导致进程状态的切换。所谓陷阱就是异常或者错误。即发生在程序内部的不可预期的非法错误。如果错误致命则将当前进程改为退出态,不致命时操作系统的行为决定于操作系统的设计,有可能是简单的通知用户,也有可能是尝试恢复。
  还有一种可能会导致进程切换的事件,就是系统调用。当用户进程发起一个特权指令(系统调用)时,操作系统会将当前用户进程设置为阻塞态,然后会调用系统例程执行系统调用指令,当执行完毕会在此调度用户进程开始执行。
  综上所述,可能造成进程状态切换的事件有三种中断,陷阱(异常),系统调用

模式切换

  操作系统为了安全设置了不同的执行模式,那么操作系统何时进行模式切换呢?我们知道内核模式就是处理器在执行内核中的系统代码,那么不难得出,只要发生状态转换的事件一定会造成模式转换。例如中断,不管时哪一种中断,都少不了操作系统要根据调度算法重新调度进程开始运行,更不用说缺页中断中操作系统还需要进入内核状态执行内存置换算法换页等等;异常也是需要操作系统判断如何进行下一步处理也需要进行模式切换;系统调用就是在执行系统历程,更需要模式的切换,因此我们可以得出进程模式切换的基本事件就是中断,陷阱(异常),系统调用
  但是要注意的时,并非模式切换一定会导致运行态进程切换,例如在中断后操作系统根据调度算法决定继续执行当前用户进程,那么当前用户进程就完全不需要改变状态,相比切换运行态进程单单切换模式,操作系统所要做的操作可要少多了。所以进程切换一定会导致模式切换,但进程模式切换并不一定会发生进程状态切换。

操作系统的组织形式

  我们之前的介绍都基于操作系统是在所有进程独立外的一个大型程序,是一组进程,那么操作系统到底是进程么?如果是进程的话又要怎么控制它?
  以下是几种操作系统内核的设计方式。

无进程内核

  这种设计方式在许多老操作系统中都十分常用,是一种相当传统的设计方式。这种设计方式的原则是将操作系统视为独立于每个用户进程外执行的一个大的系统内核。我们每次要执行操作系统代码例如发生中断,陷阱,系统调用时,都需要进行代码及及数据的切换,将用户代码及数据暂时保存然后执行操作系统内核代码,执行完毕后恢复调度用户进程或是调度其他进程。在这种设计方法下,进程这一概念仅适用于用户程序,操作系统代码则是在内核模式下单独运行的实体。下图为这种设计方法的示意图:
进程描述

在用户进程内运行

  较小的计算机操作系统通常采用这种设计方式,这种方式是将系统内核代码放到每个进程虚存中的共享区,这样做的好处是如果要执行系统代码不需要像无进程内核那样切换代码及数据以切换系统历程,这种方式仍然是在每个用户及进程内部执行操作系统代码,不需要切换进程,只用在同一进程中切换模式即可,所带来的系统开销更小,更加快捷。并且在一个进程内用户程序和操作系统程序都可执行,而在不同用户进程中执行的操作系统程序是相同的,这也是为什么要将系统内核放到共享地址空间的原因,在这种方式下一个进程在虚存中的映像如下:
进程描述

  这种设计方式的示意图如下:
进程描述

基于进程的操作系统

  这种设计方式是把操作系统作为一组系统进程来实现。和其他方法一样同样是在内核模式下运行系统代码,但是在这种情况下是吧内核功能都组织为独立的进程,但同时往往也将一些进行进程切换工作的代码独立出来。这种方式的好处是使用模块化系统设计的原理,可以将一些操作系统功能作为独立进程来实现,同时这种方式在多处理或多继环境中很有用。这种设计方式的示意图如下:

进程描述

线程

线程和进程

  现代的大多数操作系统都支持线程的使用,因为进程所具有的两个特点资源所有权调度,但是操作系统更希望将这两个特点分开进行处理,于是便诞生了线程,我们将进程视为资源分配的基本单位,将线程视为处理器调度的基本单位,线程也可以视为一个轻量级进程

多线程

  多线程是指操作系统允许在单个进程内有多个并发执行路径,一个并发执行路径又被成为一个线程。早期各个版本的操作系统他们支持多用户进程,即允许一个任务内拥有多个进程进行并发处理,但是每个进程内部只允许有一条执行路径,也就是只允许拥有一个线程,而如今的现代操作系统中绝大多数操作系统支持多线程方法,其中的差别可用下图表示:

多线程

  在多线程的基础上程序并发将会更容易实现,因为线程是一个轻量级进程因此切换和调度的消耗会更少,并且同一进程内的线程之间共享虚拟地址空间,因此通讯会更加方便。在多线程环境中,进程定义为资源分配基本单位和一个保护单位,一个进程内部有:
  1、容纳进程映像的地址空间。
  2、对处理器、其他进程(进程间通讯)、文件和I/O资源(设备和通道)的受保护访问。
  一个进程中可能有一个或多个进程,每个线程都有:
  1、一个线程执行状态。
  2、未运行时保存的线程上下文。
  3、一个执行栈。
  4、每个线程用于局部变量的一些静态存储空间。
  5、与线程内其他线程共享的内存和资源的访问。
  在多线程环境下,每个进程依然有自己的进程控制块以及进程映像,但是进程中的每个线程也拥有属于自己的独立的栈以及线程控制块,控制块中存储着线程状态,调度优先级,上下文数据等,这些是每个线程独立的信息,除此之外进程内的代码段用户数据段包括进程控制块中的信息在进程内各个线程间共享,因此才可以做到进程内各个线程之间都驻留在同一地址空间中可以做到除独立信息外的数据及代码共享。但是每个操作系统对多线程环境的实现方法都不尽相同,具体实现方法视具体环境而定,但是都应该满足进程和线程各自的基本特点。模式如下:

多线程

进程和线程之间的区别

  这是十分常见的问题,在此做同一归纳和梳理:
  1、进程是资源分配的基本单位,线程是处理器调度的基本单位。
  2、同一进程内线程共享进程状态和资源,例如数据段,代码段,I/O信息等。但是每个线程内也有独立的数据,每个线程都拥有属于自己的栈,线程属性信息存在线程控制块中,例如上下文数据,线程状态,调度信息等。
  3、线程是轻量级进程,因此创建和销毁所消耗的系统资源更少,更快。
  4、同一进程内线程切换所消耗的资源相比进程切换更少且更快。
  5、同一进程内线程共享大部分数据因此通信起来更加方便,无需借助内核。

线程分类

  在讨论线程分类前我们先思考这么几个问题:如果进程中的一个线程因为请求资源而被阻塞那么这整个进程是否应该被阻塞?在多核操作系统中统一进程中的线程是否应该允许并行执行?
  根据以上两个特点,我们将线程可以分为内核级线程(KLT)用户级线程(ULT)。它们二者之间各有优点各有特色,当然这都却决于操作系统的具体实现方式的基础上。示意图如下:

多线程

用户级线程

  在纯ULT操作系统上,对于操作系统来说线程是不可见的,操作系统依然只负责维护进程的相关控制和管理工作,而进程内线程之间的调度以及管理包括通信全部由应用程序自行完成,内核并意识不到现成的存在。用户可以使用线程库来将任何一个程序设计成多线程的,并用线程库完成多线程的管理和控制。
  在这样的实现方式下由于操作系统并无法意识到线程的存在所以除非用户自己主动调度线程,操作系统并无法完成进程内线程间的切换,并且如果此时进程内一个线程发生阻塞,例如进行了一次系统调用,系统会将整个进程置为阻塞态,包括进程内其他线程也会一起阻塞,这是十分不灵活的设计。
  例如假设进程B中有着线程1线程2两个线程,它们的状态如(a)所示,现在有可能发生如下情况:
  1、线程2进行了一次系统调用,由于操作系统无视线程的存在,因此它认为是进程B进行了系统调用,因此将整个进程B进行了阻塞。在此之后线程2依然处于运行态,但是对于操作系统来说线程2实际上并不处于运行态。直到进程B取消阻塞,线程2继续恢复运行。此时状态如(b)所示。
  2、时钟中断使整个进程B停止运行态转为就绪态,此时调度其他进程执行,此时将进程B置为就绪态,线程2依然处于运行态,直到下次再此调度进程B恢复线程2的运行。如图(c)所示。
  3、线程2运行到需要线程1执行某些动作的一个点,应用程序内部将线程2置于阻塞态,开始运行线程1。如图(d)所示。

多线程

  使用ULT有以下优点:
  1、所有线程管理数据结构都在进程内的用户地址空间中,线程之间调度不需要内核的参与,因此就不需要模式的转换,效率更高。
  2、不同的调度程序可以设置不同的线程调度算法,灵活性高。
  3、ULT可在任何操作系统上运行,不需要对系统内核代码修改以支持ULT。
  但ULT也有着以下缺点:
  1、一个线程的进行了系统调用导致阻塞会阻塞整个进程,影响其他所有线程。
  2、多线程应用程序无法利用多处理技术,即内核一次只能把一个进程交给处理器,因此一个进程中只有一个线程可以执行,这相当于再一个进程内实现了多道程序设计,并无法使一个进程内的线程并行执行。
  当然以上两种缺点是有办法弥补的。如果希望程序并行执行就将程序设计为多进程而非多线程;系统调用使进程阻塞可以使用套管技术解决,即将一个可能会产生阻塞的系统调用转换为一个非阻塞系统调用,当然这样的处理更加繁琐一些。

内核级线程

  内核级进程就是将进程的管理全权交给内核来处理,用户使用内核提供的API来控制线程,Windows就是使用这样的方式来实现线程的。
  KLT的优点就是ULT的缺点,KLT的缺点就是ULT的优点。其中最大的缺点就是每次线程之间的切换都需要内核模式的切换,消耗更大,但是可以肯定的是哪怕KLT线程的消耗再大也是远远小于进程之间切换的,因此为了方便管理目前常用的操作系统都是基于内核级线程来实现的。

混合方法

  在混合方法中,内核级线程会被映射到一些由内核管理的内核级线程上,内核级线程是小于或等于用户级线程的。当用户级线程和内核级线程相等时此时等价于使用了纯KLT方式。
  这种混合方法在设计合理时可以完美结合KLT和ULT的优点,并克服它们的缺点。目前Solaris就是使用了这种混合线程的方法。

Linux的进程和线程管理

进程管理

  Linux属于类UNIX操作系统,实现的原理与UNIX进程的实现方法类似,其实大部分的操作系统都要遵循系统设计的基本原理,但是实现细节上会有所不同。在Linux上进程状态转换如下图:

多线程

  在Linux系统实现中最大的变化就是将阻塞态变为了可中断不可中断两个状态,并且加入了停止态
  1、可中断:这是一个阻塞态,进程正在等待一个事件的结束。
  2、不可中断:这是一个阻塞态,与可中断的区别是,此时进程正在等待一个硬件条件,因此屏蔽任何信号。
  3、停止:进程收到信号要求被其他进程暂停执行,并且只能由另一个进程的主动动作恢复运行。

线程管理

  Linux使用一种十分特殊的线程处理方式,它内核中并没有独立的线程控制块,Linux选择使用PCB模拟实现线程,因此在Linux中PCB其实就相当于是一个线程。
  那么Linux又是如何实现进程间数据独立线程间数据共享的呢?Linux将用户级线程映射到内核级进程上,组成一个用户级进程的用户级线程则映射到共享一个组ID的多个Linux内核级进程上,使得同一个组内部的进程共享文件和内存等资源,就像一个进程内部的线程共享资源一样。也就是说用进程模拟实现线程,通过给进程分组的方式来实现数据的共享和独立。
  同时Linux在内部又通过命名空间来管理进程的数据。命名空间可使一个进程拥有与其他不同命名空间的进程不同的系统视图,因此可以获得不同资源。典型的命名空间有:
  1、Mount命名空间。为进程提供文件系统层次的特定试图。
  2、UTS命名空间。与系统配置有关。
  3、IPC命名空间。隔离进程间IPC资源,如信号量。
  4、PID命名空间。隔离进程ID空间。
  5、网络命名空间。隔离与网络相关的系统资源。
  6、用户命名空间。提供容器使其与父进程隔离。

-------------本文结束感谢您的阅读!-------------
记录学习每一分,感谢您的赞助