操作系统原理之进程与线程
标签:操作系统原理
目录
进程管理
程序与进程
进程是一个正在执行的程序。例如,当我们用 C 或 C++ 编写一个程序并进行编译时,编译器会创建二进制代码。原始代码和二进制代码都是程序。当我们实际运行二进制代码时,它就成为一个进程。
进程是一个 "主动" 的实体,而不是一个被认为是 "被动" 实体的程序。一个程序在多次运行时可以创建许多进程;例如,当我们多次打开一个.exe 或二进制文件时,多个实例开始(创建多个进程)。
进程在内存中是什么样子的?
文本段(Text Section):一个进程,有时也被称为文本段,也包括由以下值代表的当前活动:
- 程序计数器(Program Counter)。
- 堆栈(Stack)。堆栈包含临时数据,如函数参数、返回地址和局部变量。
- 数据部分(Data Section)。包含全局变量。
- 堆段(Heap Section)。在其运行时间内动态分配给进程的内存。
参见 C 程序的内存布局。
C 程序的内存布局
一个典型的 C 语言程序的内存表示由以下部分组成:
- 文本段(即指令,Text segment,instructions)
- 初始化数据段(Initialized data segment)
- 未初始化的数据段 (Uninitialized data segment,bss)
- 堆(Heap)
- 堆栈(Stack)
一个运行中的进程的典型内存布局:
文本段。文本段,也被称为代码段或简称为文本,是对象文件中或内存中的程序部分之一,它包含可执行指令。作为一个内存区域,文本段可以被放在堆或栈的下面,以防止堆和栈溢出覆盖它。通常,文本段是可共享的,因此对于经常执行的程序,如文本编辑器、C 语言编译器、外壳等,只需要在内存中保留一份副本。另外,文本段通常是只读的,以防止程序意外地修改其指令。
初始化数据段。初始化数据段,通常简单地称为数据段。数据段是程序的虚拟地址空间的一部分,它包含全局变量和静态变量,由程序员初始化。请注意,数据段不是只读的,因为变量的值可以在运行时被改变。这个段可以进一步分为初始化的只读区和初始化的读写区。例如,在 C 语言中由 char s [] = "hello world" 定义的全局字符串和 main(即全局)之外的 int debug=1 这样的 C 语句将被存储在初始化读写区。而像 const char* string = "hello world" 这样的全局 C 语句会使字符串字面量 "hello world" 存储在初始化的只读区域,而字符指针变量 string 存储在初始化的读写区域。
未初始化的数据段。未初始化的数据段通常被称为 "bss" 段,以一个古老的汇编器操作符命名,代表着 "由符号开始的块"。在程序开始执行之前,该段中的数据被内核初始化为 0,未初始化的数据从数据段的末端开始,包含所有全局变量和静态变量,这些变量被初始化为 0 或者在源代码中没有明确的初始化。例如,一个声明为静态
int i;
的变量将包含在 BSS 段中。堆栈(Stack)。传统上,stack 与堆积区 heap 相邻,并以相反的方向增长;当栈指针(stack pointer)与堆指针(heap pointer)相遇时,自由内存被耗尽。(有了现代的大地址空间和虚拟内存技术,它们几乎可以放在任何地方,但它们通常还是以相反的方向增长)。堆栈区包含了程序堆,一个后进先出的结构,通常位于内存的较高部分。在标准的 PC x86 计算机架构上,它向零地址增长;在其他一些架构上,它向相反方向增长。一个 " 堆栈指针 "寄存器跟踪堆栈的顶部;每当一个值被" 推 "到堆栈时,它就会被调整。一个函数调用所推送的一组值被称为" 堆栈帧 "(stack frame);一个堆栈帧至少由一个返回地址组成。栈,是储存自动变量的地方,同时还有每次调用函数时保存的信息。每次函数被调用时,要返回地址和关于调用者环境的某些信息,如一些机器寄存器(machine registers),都被保存在堆栈中。然后,新调用的函数在堆栈上为其自动变量分配空间。这就是 C 语言中递归函数的工作方式。每次递归函数调用自己时,都会使用一个新的堆栈帧,所以一组变量不会干扰函数的另一个实例的变量。
堆(Heap):堆是通常发生动态内存分配的部分。堆区从 BSS 段的末端开始,并从那里增长到更大的地址。堆区由
malloc
、realloc
和free
管理,可以使用brk
和sbrk
系统调用来调整其大小(注意,使用 brk/sbrk 和单一的 "堆区" 并不需要履行 malloc/realloc/free 的合同;它们也可以使用mmap
来实现,以保留潜在的非连续的虚拟内存区域到进程的虚拟地址空间)。堆区是由一个进程中的所有共享库和动态加载的模块共享的。
进程的属性或特征
进程具有以下属性:
- 进程标识(Process Id)。一个由操作系统分配的唯一标识符。
- 进程状态(Process State)。可以是就绪、运行等。
- CPU 寄存器(CPU registers)。像程序计数器一样(CPU 寄存器必须被保存和恢复,当一个进程被调入和调出 CPU 时)。当一个进程被换入和换出 CPU 时,必须保存和恢复)。
- 账户信息(Accounts information)。用于进程执行的 CPU 数量,时间限制,执行 ID 等。
- I/O 状态信息。例如,分配给该进程的设备,打开的文件,等等。
- CPU 调度信息。例如,优先级(不同的进程可能有不同的优先级,例如在最短作业的调度中一个较短的进程被分配了高优先级)。
进程的上述所有属性也被称为进程的上下文(context of the process)。每个进程都有自己的过程控制块(PCB,process control block),即每个进程都有一个独特的 PCB。上述所有的属性都是 PCB 的一部分。
进程的状态。一个进程处于以下状态之一:
- 全新(New)。新创建的进程(或)正在创建的进程。
- 准备就绪(Ready)。创建后的进程进入就绪状态,即该进程已准备好执行。
- 运行(Run)。目前在 CPU 中运行的进程(在一个处理器中一次只能有一个进程在执行)。
- 等待(或阻塞)(Wait (or Block))。当一个进程请求 I/O 访问时。
- 完成(或终止)(Complete (or Terminated))。进程完成了它的执行。
- 暂停就绪(Suspended Ready)。当就绪队列变得满了,一些进程被转移到暂停就绪状态。
- 暂停等待(Suspended Block)。当等待队列变满时。
上下文切换
在进程上下文切换中,必须以某种方式保存第一个进程的状态,这样当调度回到第一个进程的执行时,就可以恢复这个状态并继续执行。进程的状态包括该进程可能使用的所有寄存器,特别是程序计数器,加上任何其他可能需要的操作系统特定数据。
保存一个进程的上下文并加载另一个进程的上下文的过程被称为上下文切换(Context Switching)。简单地说,它就像把进程从运行状态加载和卸载到准备状态。
上下文切换何时发生?
- 当一个高优先级的进程来到就绪状态(即比运行中的进程有更高的优先级)时。
- 一个中断发生。
- 用户模式和内核模式的切换(不过这不是必须的)。
- 使用抢占式 CPU 调度。
上下文切换与模式切换(Mode Switch)
当 CPU 的权限级别发生变化时,例如,当系统调用或发生故障时,就会发生模式切换。内核的工作模式比标准的用户任务更有特权。如果一个用户进程想访问只有内核才能访问的东西,就必须进行模式切换。在模式切换过程中,当前执行的进程不需要被改变。
模式切换通常发生在进程上下文切换的时候,只有内核可以导致上下文切换。进程切换涉及模式切换。上下文切换只能在内核模式下发生。
CPU 密集的进程与 I/O 密集的进程
CPU 密集(CPU-Bound)的进程需要更多的 CPU 时间或在运行状态下花费更多的时间。I/O 密集(I/O-Bound)的进程需要更多的 I/O 时间和更少的 CPU 时间。I/O 密集的进程在等待状态下花费更多时间。
进程状态
进程的状态
一个过程的状态如下:
- New (Create):在这一步中,进程即将被创建,但还没有被创建,它是存在于二级存储器(secondary memory)中的程序,将被操作系统拾取以创建进程。
- Ready:New-->Ready,准备运行。在创建一个进程后,该进程进入准备状态,即该进程被加载到主存储器(main memory)中。这里的进程已经准备好运行,并等待获得执行的 CPU 时间。准备被 CPU 执行的进程被保存在一个准备好的进程队列中。
- Run:进程被 CPU 选择执行,进程内的指令由任意一个可用的 CPU 核(core)执行。
- Blocked or wait:每当进程要求访问 I/O 或需要用户的输入或需要访问一个关键区域(该区域的锁已经获得),它就进入阻塞或等待状态。该进程继续在主内存中等待,不需要 CPU。一旦 I/O 操作完成,进程就进入准备状态。
- Terminated or completed:进程被杀死,同时 PCB 也被删除。
- Suspend ready:进程最初处于就绪状态,但被调度器从主内存中换出(参考虚拟内存主题)并放置在外部存储器上,被称为处于暂停就绪状态。每当进程再次被带入主存时,该进程将过渡到就绪状态。
- Suspend wait/suspend blocked:与 suspend ready 类似,但使用的是正在进行 I/O 操作的进程,主内存的缺乏导致它们转移到二级内存。当工作完成后,它可能会转到 Suspend ready 状态。
CPU 和 I/O 密集的进程
如果进程在 CPU 操作方面很密集,那么它就被称为 CPU 密集(CPU Bound)进程。同样,如果进程在 I/O 操作方面很密集,那么它就被称为 I/O 密集(I/O Bound)进程。
调度器的类型
- 长期(Long term)-- 性能 -- 对应该使多少进程保持在准备状态做出决定,这决定了多程序的程度。一旦做出决定,就会持续很长时间,因此称为长期调度器。
- 短期(Short term)-- 上下文切换时间 -- 短期调度器(scheduler)将决定下一个执行的进程,然后它将调用调度器(dispatcher)。调度器(dispatcher)是一个将进程从准备状态转移到运行状态的软件,反之亦然。换句话说,这就是上下文切换。
- 中期(Medium term)-- 交换时间 -- 暂停(Suspension)决定是由中期调度器作出的。中期调度器用于交换,即把进程从主内存移到辅助内存,反之亦然。
多程序(Multiprogramming)
如果我们有许多进程准备运行,有两种类型的多程序:
- 预先抢占(Pre-emption) - 进程被强行从 CPU 中移除。预抢占也被称为时间共享(time sharing)或多任务(multitasking)。
- 非抢占 - 进程在完成执行之前不会被移除。
多程序的程度:在最大限度内可以驻留在准备状态的进程数量决定了多程序的程度,例如,如果程序程度 = 100,这意味着在最大限度内可以有 100 个进程驻留在准备状态。
进程调度器
进程调度是进程管理器(process manager)的活动,它处理从 CPU 中移除正在运行的进程并根据特定的策略选择另一个进程。
进程调度是多程序(Multiprogramming)操作系统的一个重要组成部分。这种操作系统允许在同一时间将一个以上的进程加载到可执行内存中,并且加载的进程使用时间复用(time multiplexing)来共享 CPU。
有三种类型的进程调度器:
- 长期调度器或任务调度器(Long Term or job scheduler): 它把新的进程带到 "就绪状态"。它控制多程序的程度(Degree of Multi-programming),即在任何时间点存在于准备状态的进程数量。重要的是,长期调度器要对 I/O 和 CPU 密集的进程进行仔细选择。I/O 密集的任务是在输入和输出操作中使用大部分时间,而 CPU 密集的进程是在 CPU 上花费时间。工作调度器通过保持两者之间的平衡来提高效率。
- 短期调度器或 CPU 调度器(Short term or CPU scheduler):它负责从就绪状态中选择一个进程,将其调度到运行状态。注意:短期调度器只选择进程进行调度,并不加载运行中的进程。这里是所有调度算法被使用的时候。CPU 调度器负责确保没有由于高爆发时间的进程而产生的饥饿。调度器(Dispatcher)负责将短期调度器选择的进程加载到 CPU 上(就绪到运行状态),上下文切换只由调度器(dispatcher)完成。一个调度器(dispatcher)做以下工作:切换上下文、切换到用户模式、跳转到新加载的程序中的适当位置。
- 中期调度器(Medium-term scheduler): 它负责暂停(suspending)和恢复(resuming)进程。它主要进行交换(swapping)(将进程从主内存转移到磁盘,反之亦然)。交换可能是必要的,以改善进程的组合,或者因为内存需求的变化使可用的内存被过度占用(overcommitted),需要释放内存。它有助于保持 I/O 密集和 CPU 密集进程之间的完美平衡。它减少了多程序化的程度。
进程表和进程控制块(PCB)
在创建一个进程时,操作系统会执行若干操作。为了识别进程,它为每个进程分配了一个进程识别号(PID)。由于操作系统支持多程序编程(multi-programming,多进程),它需要跟踪所有的进程。对于这项任务,进程控制块(PCB,process control block)被用来跟踪进程的执行状态。每个内存块都包含有关进程状态、程序计数器、堆栈指针、已打开文件的状态、调度算法等信息。所有这些信息都是需要的,并且在进程从一个状态切换到另一个状态时必须保存。当进程从一个状态过渡到另一个状态时,操作系统必须更新进程的 PCB 中的信息。
进程控制块(PCB)包含关于进程的信息,即寄存器、量子(quantum)、优先级等。进程表(process table)是一个 PCB 的数组,这意味着在逻辑上包含了系统中所有当前进程的一个 PCB。
- 指针 - 它是一个堆栈指针,当进程从一个状态切换到另一个状态时,需要保存这个指针以保留进程的当前位置。
- 进程状态 - 它存储进程的各自状态。
- 进程编号 - 每个进程都有一个唯一的 ID,称为进程 ID 或 PID,它存储进程的标识符。
- 程序计数器 - 它存储计数器,其中包含进程要执行的下一条指令的地址。
- 寄存器 - 这些是 CPU 的寄存器,包括:累加器(accumulator)、基数(base)、寄存器和一般用途寄存器。
- 内存限制 - 这个字段包含操作系统所使用的内存管理系统的信息。这可能包括页表(page tables)、段表(segment tables)等。
- 打开的文件列表 - 这个信息包括为一个进程打开的文件列表。
- 杂项核算和状态数据(Miscellaneous accounting and status data):这个字段包括关于 CPU 使用量、时间限制、作业或进程编号等信息。
进程控制块存储了处理器被阻塞运行(blocked)时的寄存器内容,也被称为执行内容。这种执行内容架构使操作系统能够在进程返回到运行状态时恢复进程的执行环境。当进程从一个状态过渡到另一个状态时,操作系统会更新其在进程的 PCB 中的信息。操作系统在一个进程表中维护着每个进程的 PCB 的指针,以便它能快速访问 PCB。
中断(Interrupts)
中断是一个由硬件或软件在一个进程或一个事件需要立即关注(attention)时发出的信号,它提醒处理器有一个高优先级的进程需要中断当前的工作进程。在 I/O 设备中,有一条总线控制线专门用于此目的,被称为中断服务程序(ISR,Interrupt Service Routine)。
当一个设备在假设的进程 i 处引发中断时,处理器首先完成指令 i 的执行,然后用 ISR 的第一条指令的地址加载程序计数器(PC,Program Counter)。在向程序计数器加载地址之前,被中断指令的地址被移到一个临时位置。因此,在处理完中断后,处理器可以继续执行进程 i+1。
当处理器处理中断时,它必须通知设备它的请求已经被识别,以便它停止发送中断请求信号(interrupt request signal)。另外,保存寄存器以便将来可以恢复被中断的进程,这就增加了从收到中断到开始执行 ISR 的延迟。这就是所谓的中断延迟(Interrupt Latency)。
硬件中断
在硬件中断中,所有的设备都连接到中断请求线(Interrupt Request Line)。一条请求线用于所有 N 个设备。为了请求一个中断,一个设备关闭其相关的开关。当一个设备请求一个中断时,INTR 的值是来自各个设备的请求的逻辑 OR(logical OR)。
处理一个 IRQ(Interrupt Request)所涉及的事件的顺序:
- 设备提出一个 IRQ。
- 处理器中断当前正在执行的程序。
- 设备被告知其请求已被识别,设备停用请求信号。
- 请求的动作被执行。
- 中断被激活,被中断的程序被恢复。
处理多个设备:
当一个以上的设备发出一个中断请求信号时,那么需要额外的信息来决定首先考虑哪个设备。以下方法用于决定选择哪个设备:轮询(Polling)、矢量中断(Vectored Interrupts)和中断嵌套(Interrupt Nesting)。以下是对这些方法的解释:
- 轮询(Polling)。在轮询中,遇到的第一个设置了 IRQ 位的设备就是首先被服务的设备。适当的 ISR 被调用来服务。这很容易实现,但由于询问所有设备的 IRQ 位而浪费了大量的时间。
- 矢量中断(Vectored Interrupts):在矢量中断中,请求中断的设备通过在总线上向处理器发送一个特殊的代码来直接识别自己。这使处理器能够识别产生中断的设备。该特殊代码可以是 ISR 的起始地址,也可以是 ISR 在内存中的位置,被称为中断向量。
- 中断嵌套(Interrupt Nesting)。在这种方法中,I/O 设备被组织在一个优先级结构中。因此,来自高优先级设备的中断请求被识别,而来自低优先级设备的请求则不被识别。处理器只接受来自具有优先权的设备 / 进程的中断。
处理器的优先级被编码在 PS(进程状态寄存器,Process Status register)的几个位上。它可以通过写进 PS 的程序指令来改变。处理器只在执行操作系统程序时处于监督模式(supervised mode)。在执行应用程序之前,它会切换到用户模式(user mode)。
线程
什么是线程?
一个线程是一个进程中的执行路径(path)。一个进程可以包含多个线程。
为什么是多线程?
一个线程也被称为轻量级进程。其目的是通过将一个进程划分为多个线程来实现并行化。例如,在一个浏览器中,多个标签可以是不同的线程。MS Word 使用多个线程:一个线程用于格式化文本,另一个线程用于处理输入,等等。下面将讨论多线程的更多优点。
进程与线程
主要区别在于,同一进程中的线程在共享内存空间中运行,而进程在独立的内存空间中运行。
线程不像进程那样相互独立,因此,线程与其他线程共享其代码部分、数据部分和操作系统资源(如打开的文件和信号)。但是,像进程一样,线程有自己的程序计数器(PC)、寄存器集和堆栈空间。
线程比进程的优势
- 响应性(Responsiveness)。如果进程被分成多个线程,如果一个线程完成了它的执行,那么它的输出可以立即被返回。
- 更快的上下文切换。与进程上下文切换相比,线程之间的上下文切换时间更短。进程上下文切换需要 CPU 的更多开销。
- 有效利用多处理器系统。如果我们在一个进程中有多个线程,那么我们可以在多个处理器上安排多个线程。这将使进程执行得更快。
- 资源共享。代码、数据和文件等资源可以在一个进程中的所有线程之间共享。注意:堆栈和寄存器不能在线程之间共享。每个线程都有自己的堆栈和寄存器。
- 通信(Communication)。多个线程之间的通信比较容易,因为这些线程共享共同的地址空间。而在进程中,我们必须遵循一些特定的通信技术来实现两个进程之间的通信。
- 增强系统的吞吐量(throughput)。如果一个进程被分为多个线程,每个线程的功能被视为一个任务,那么每单位时间内完成的工作数量就会增加,从而提高系统的吞吐量。
线程的类型
有两种类型的线程:
- 用户级线程
- 内核级线程
用户级线程和内核级线程之间的区别:
参数 | 用户级线程 | 内核级线程 |
---|---|---|
实现(Implemented by) | 用户线程由用户实现 | 内核线程由操作系统(OS)实现。 |
识别(Recognize) | 操作系统不识别用户级线程。 | 内核线程被操作系统识别。 |
实现的复杂性 | 用户线程的实现很容易。 | 内核线程的实现很复杂。 |
上下文切换时间 | 上下文切换时间较短。 | 上下文切换时间较长。 |
硬件支持 | 上下文切换不需要硬件支持。 | 需要硬件支持。 |
阻塞操作(Blocking operation) | 如果一个用户级线程执行阻塞操作,那么整个进程将被阻塞。 | 如果一个内核线程执行阻塞操作,那么另一个线程可以继续执行。 |
多线程(Multithreading) | 多线程应用程序不能利用多处理(multiprocessing)的优势。 | 内核可以是多线程的。 |
创建和管理 | 用户级线程可以更快创建和管理。 | 内核级线程需要更多时间来创建和管理。 |
操作系统 | 任何操作系统都可以支持用户级线程。 | 内核级线程是针对操作系统的。 |
线程管理 | 线程库包含线程创建、消息传递、线程调度、数据传输和线程销毁的代码 | 应用程序代码不包含线程管理代码。它只是内核模式的一个 API。Windows 操作系统就利用了这个功能。 |
例子 | Java 线程,POSIX 线程。 | Window Solaris。 |
优点 | 用户级线程的创建简单而快速;可以在任何操作系统上运行;它们比内核线程性能更好,因为它们不需要进行系统调用来创建线程;在用户级线程中,线程之间的切换不需要内核模式的权限; | 在内核级线程中,在不同的处理器上调度属于同一进程的多个线程是可能的;多线程可以存在于内核例程中;当内核级的一个线程停止时,内核可以为同一进程安排另一个线程。 |
劣势 | 用户级线程上的多线程应用不能从多处理中获益;如果一个用户级线程执行了一个阻塞操作,整个进程就会停止。 | 在一个进程中把控制权从一个线程转移到另一个线程,就必须把模式切换到内核模式;内核级线程比用户级线程需要更多时间来创建和管理。 |
线程及其类型
线程是一个进程中的单一序列流(single sequence stream)。线程具有与进程相同的属性,因此它们被称为轻量级进程(light weight processes)。线程是一个接一个地执行的,但给人的错觉是它们在并行(parallel)地执行。每个线程都有不同的状态。每个线程都有:
- 一个程序计数器
- 一个寄存器组
- 一个堆栈空间
线程之间不是相互独立的,因为它们共享代码、数据和操作系统资源等。
线程和进程之间的相似之处:
- 每次只有一个线程或进程是活动的(active)。
- 在进程中,两者都按顺序(sequential)执行。
- 两者都可以创建子进程或者子线程(children)
线程和进程之间的区别:
- 线程不是独立的,进程才是。
- 线程的设计是为了相互协助,进程可能会也可能不会这样做
线程的类型分类
用户级线程(ULT,User Level thread)
在用户级库中实现,它们不使用系统调用来创建。线程切换不需要调用操作系统,也不需要给内核造成中断。内核不知道用户级线程,并把它们当作单线程进程来管理。
ULT 的优点:
- 可以在不支持多线程的操作系统上实现。
- 表示简单,因为线程只有程序计数器、寄存器组和堆栈空间。
- 创建简单,因为没有内核的干预。
- 由于不需要调用操作系统,所以线程切换非常快。
ULT 的局限性:
- 线程和内核之间没有或很少有协调性。
- 如果一个线程引起了页面故障,整个进程就会阻塞。
内核级线程(KLT,Kernel Level Thread)
内核知道并管理 KLT 线程。内核本身拥有线程表(主表,thread table),而不是每个进程中的线程表,它跟踪系统中的所有线程。此外,内核还维护着传统的进程表(process table),以记录进程的情况。操作系统内核提供系统调用(system call)来创建和管理线程。
KLT 的优点:
- 由于内核对系统中的线程有充分的了解,调度器(scheduler)可以决定给有大量线程的进程更多时间。
- 适合于经常阻塞的应用程序。
KLT 的局限性:
- 速度慢,效率低。
- 它需要线程控制块(thread control block),所以它是一个开销。
总结:
- 每个 ULT 都有一个进程,使用线程表来跟踪线程。
- 每个 KLT 都有线程表(TCB)以及进程表(PCB)。
进程和线程的区别
进程:进程基本上是指从准备好的状态下派发的程序,并被安排在 CPU 中执行。PCB(进程控制块)拥有进程的概念。一个进程可以创建其他进程,这些进程被称为子进程。进程需要更多的时间来终止,它是孤立的(isolated),意味着它不与任何其他进程共享内存。进程可以有以下状态:新建、准备、运行、等待、终止和暂停(new, ready, running, waiting, terminated, and suspended)。
线程:线程是一个进程的部分(segment),这意味着一个进程可以有多个线程,这些多个线程包含在一个进程中。一个线程有三种状态:运行、准备和阻塞(Running, Ready, and Blocked)。
与进程相比,线程终止所需的时间更短,但与进程不同的是,线程不具有隔离性。
进程 | 线程 |
---|---|
进程是指任何正在执行的程序 | 线程是指一个进程的一个部分。 |
进程需要更多的时间来终止 | 线程需要更少的时间来终止。 |
它需要更多的时间来创建 | 它需要更少的时间来创建。 |
它还需要更多的时间进行上下文切换。 | 它需要更少的时间进行上下文切换。 |
进程在通信方面的效率较低。 | 线程在通信方面的效率更高。 |
多重编程(Multiprogramming)包含了多进程的概念 | 一个进程已经由多个线程组成。 |
进程是孤立的 | 线程共享内存。 |
进程被称为重量级进程。 | 线程是轻量级的,因为进程中的每个线程共享代码、数据和资源。 |
进程切换使用操作系统中的一个接口 | 线程切换不需要调用操作系统,且不会引起内核的中断。 |
如果一个进程被阻断,那么它不会影响其他进程的执行 | 如果一个用户级线程被阻断,那么所有其他用户级线程都被阻断。 |
进程有自己的进程控制块、堆栈和地址空间。 | 线程有父母的 PCB、自己的线程控制块、堆栈和公共地址空间。 |
对父进程的改变不会影响子进程。 | 由于同一进程的所有线程共享地址空间和其他资源,所以对主线程的任何改变都可能影响进程中其他线程的行为。 |
其中涉及到系统调用。 | 不涉及到系统调用,它是使用 API 创建的。 |
进程之间不共享数据。 | 线程之间共享数据。 |