操作系统 进程线程协程知识

注意:本文章整理于GPT。
27,29

1、操作系统知识相关。进程 线程 协程的区别?进程和线程、上下文切换了什么?共享了什么?线程独占什么?切换时候内核做了什么?

进程、线程、协程的区别

1. 进程(Process)
  • 定义:进程是一个程序在其自身地址空间中的执行实例,是资源分配的最小单位。
  • 特性
    • 独立性:每个进程都有自己独立的内存空间(代码段、数据段、堆、栈)。
    • 资源拥有:进程拥有自己的资源(文件句柄、信号等)。
    • 调度:操作系统负责调度和管理进程。
2. 线程(Thread)
  • 定义:线程是进程内的一个执行单元,是调度的最小单位。一个进程可以有多个线程,共享该进程的资源。
  • 特性
    • 共享资源:同一进程内的线程共享进程的资源(内存、文件句柄等)。
    • 独立栈:每个线程都有自己的栈空间和寄存器上下文。
    • 轻量级:创建和销毁线程的开销比进程小,线程切换速度快。
3. 协程(Coroutine)
  • 定义:协程是一种用户态的轻量级线程,由用户程序来调度,而不是操作系统内核。
  • 特性
    • 用户态调度:协程的切换由程序代码控制,不需要操作系统的上下文切换。
    • 高效性:由于不涉及内核切换,协程切换非常快,适用于I/O密集型任务。
    • 协作式多任务:协程通过显式的让出操作(如yield)来实现多任务。

上下文切换和共享内容

进程上下文切换
  • 切换内容

    • 处理器状态:保存和恢复CPU寄存器(通用寄存器、程序计数器、堆栈指针等)。
    • 内存管理信息:切换页表或段表,重新设置内存管理单元(MMU)。
    • 内核态数据:切换内核栈、任务控制块(TCB)、进程描述符等。
  • 共享内容

    • 几乎没有共享:进程间通常不共享内存空间,但可以通过IPC(进程间通信)共享数据,如管道、消息队列、共享内存等。
线程上下文切换
  • 切换内容

    • 处理器状态:保存和恢复CPU寄存器。
    • 线程相关数据:切换线程栈、线程局部存储(TLS)、线程控制块(TCB)等。
  • 共享内容

    • 进程资源:同一进程内的线程共享进程的虚拟内存空间、全局变量、文件句柄等。
线程独占内容
  • 独占栈:每个线程有自己独立的栈空间。
  • 独占寄存器上下文:每个线程在运行时都有自己独立的CPU寄存器状态。
  • 线程本地存储(TLS):线程拥有自己的局部存储区域,用于存放线程特定的数据。

上下文切换时内核做了什么

  • 保存当前线程/进程状态:保存当前线程或进程的CPU寄存器状态到其内核栈中。
  • 选择下一个要运行的线程/进程:根据调度算法选择下一个要运行的线程或进程。
  • 恢复下一个线程/进程状态:从内核栈中恢复下一个线程或进程的CPU寄存器状态。
  • 更新内存管理单元(MMU):如果是进程切换,需要更新页表或段表,以映射新进程的地址空间。
  • 内核数据结构更新:更新内核中的调度队列、统计信息等。

这种切换涉及的系统调用通常有context_switchschedule等,对于用户态来说,上下文切换是透明的,由操作系统内核完全管理。

2、进程上下文切换开销?进程的内存空间?一个可执行文件如何变成进程的?CPU调度基本单位?进程和线程的优缺点,各适用于什么场合?

进程上下文切换开销

1. 上下文切换的组成部分
  • 保存当前状态:将当前进程的CPU寄存器、程序计数器等状态保存到该进程的内核栈中。
  • 切换页表:在不同进程之间切换时,更新MMU中的页表或段表以切换进程的地址空间。
  • 内核数据结构:更新内核调度队列和其他相关数据结构。
  • 恢复新状态:从下一个要运行的进程的内核栈中恢复其CPU寄存器和程序计数器状态。
2. 开销因素
  • CPU寄存器状态保存和恢复:涉及多个寄存器的保存和恢复操作。
  • 页表切换:需要刷新TLB(Translation Lookaside Buffer),增加内存访问延迟。
  • 缓存失效:新进程可能带来缓存失效,导致缓存命中率降低,增加内存访问时间。
3. 开销大小
  • 上下文切换开销取决于CPU架构和操作系统实现,但通常来说,进程切换的开销较高,可能需要数百到数千个CPU周期。

进程的内存空间

1. 内存布局
  • 代码段(Text Segment):存放程序的可执行代码。
  • 数据段(Data Segment):存放已初始化的全局变量和静态变量。
  • BSS段(Block Started by Symbol):存放未初始化的全局变量和静态变量。
  • 堆(Heap):动态内存分配,由malloc等函数分配,向高地址方向增长。
  • 栈(Stack):用于函数调用时存储局部变量、返回地址等,向低地址方向增长。

一个可执行文件如何变成进程的?

1. 加载可执行文件
  • 文件读取:操作系统读取可执行文件(如ELF格式)到内存中。
  • 内存映射:将文件的各个段(代码段、数据段等)映射到进程的虚拟地址空间中。
  • 堆和栈初始化:为堆和栈分配初始内存。
2. 创建进程控制块(PCB)
  • 进程表项:在操作系统的进程表中创建新的进程表项(PCB),包含进程ID、状态、寄存器状态等信息。
3. 初始化CPU状态
  • 寄存器设置:设置程序计数器(PC)指向程序的入口点,初始化其他CPU寄存器。
4. 开始执行
  • 调度:将新进程加入调度队列,等待CPU调度运行。

CPU调度基本单位

  • 线程:在现代操作系统中,CPU调度的基本单位通常是线程(Thread)。线程比进程更轻量,具有更高的调度效率。

进程和线程的优缺点及适用场合

进程(Process)
优点
  • 独立性强:进程之间互不干扰,提高了系统稳定性和安全性。
  • 资源隔离:各进程拥有独立的地址空间,减少了数据竞争和资源冲突。
缺点
  • 开销大:进程创建、销毁和上下文切换开销较大。
  • 通信复杂:进程间通信(IPC)复杂且效率低。
适用场合
  • 高可靠性需求:如操作系统服务、数据库管理系统。
  • 独立任务:需要独立运行的任务,如服务器进程、后台任务。
线程(Thread)
优点
  • 轻量级:线程创建、销毁和上下文切换开销较小。
  • 共享内存:线程共享进程的内存空间,数据传递和同步较快。
缺点
  • 数据同步复杂:共享内存容易引发数据竞争和死锁问题,需要精细的同步机制。
  • 资源争用:线程共享进程资源,错误的资源操作可能导致整个进程崩溃。
适用场合
  • 高并发需求:如网络服务器、高性能计算。
  • 实时性要求:如图形界面更新、多媒体应用。

总结

  • 进程适用于需要高可靠性和独立性的场合,如操作系统服务和后台任务。
  • 线程适用于需要高并发和实时性的场合,如网络服务器和图形界面更新。

3、fork的作用是什么?为什么不用system?内核创建进程时会自动打开哪几个文件?进程控制块?什么情况下需要多线程频繁切换?僵尸进程是什么,怎么处理?

fork 的作用和与 system 的比较

fork 的作用
  • 创建新进程fork 是系统调用,用于创建一个新的进程。新进程称为子进程,它是从父进程复制而来的。
  • 独立运行:子进程从fork调用返回时开始执行,独立于父进程运行。子进程与父进程共享文件描述符,但具有独立的地址空间。
  • 并行执行fork 允许父进程和子进程并行执行,从而实现并发。
为什么不用 system
  • system 的作用system 函数用于执行一个 shell 命令,在调用时会创建一个新的进程来执行该命令,然后等待命令执行完成。
  • 灵活性fork 允许在子进程中执行任意代码,而 system 只能执行特定的 shell 命令。
  • 安全性:使用 system 时需要传递一个字符串,该字符串会被 shell 解释执行,可能带来安全隐患(如命令注入)。
  • 控制权fork 后可以通过 exec 系列函数执行新程序,且在执行前可以设置子进程的环境和状态;system 则不提供这种控制。

内核创建进程时会自动打开哪几个文件?

当内核创建一个新进程时,会自动为该进程打开以下几个标准文件描述符:

  1. 标准输入(stdin):文件描述符0,通常连接到终端的输入。
  2. 标准输出(stdout):文件描述符1,通常连接到终端的输出。
  3. 标准错误(stderr):文件描述符2,通常连接到终端的错误输出。

这些文件描述符在父进程和子进程之间是共享的。

进程控制块(PCB)

  • 定义:进程控制块(Process Control Block, PCB)是操作系统内核中用来管理进程的一个数据结构。
  • 包含信息
    • 进程标识符(PID):进程的唯一标识符。
    • 进程状态:进程的当前状态(如运行、就绪、等待)。
    • 寄存器状态:CPU寄存器的内容,包括程序计数器和栈指针。
    • 内存管理信息:指向进程页表或段表的指针。
    • 文件描述符表:进程打开的文件列表。
    • 调度信息:优先级、调度队列指针等。
    • 会计信息:CPU使用时间、进程创建时间等。

什么情况下需要多线程频繁切换?

  • I/O密集型任务:在等待I/O操作(如磁盘读写、网络通信)时,可以切换到其他线程执行,以提高资源利用率。
  • 实时性要求:在实时系统中,需要频繁切换线程以保证及时响应各种外部事件。
  • 多任务并发:在多任务系统中,需要频繁切换线程以实现并发执行多个任务。

僵尸进程是什么,怎么处理?

僵尸进程的定义
  • 僵尸进程:僵尸进程是指已经终止但其父进程尚未调用 waitwaitpid 系统调用获取其退出状态的进程。僵尸进程仍然占据进程表项(PID),但不占用系统资源。
处理僵尸进程
  • 父进程调用 waitwaitpid:父进程应调用 waitwaitpid 以回收子进程的退出状态,从而释放其PID和进程表项。
  • 信号处理:父进程可以通过捕捉 SIGCHLD 信号,在信号处理程序中调用 waitwaitpid 来处理子进程退出。
  • 孤儿进程处理:如果父进程先于子进程退出,子进程会成为孤儿进程,由init进程(PID为1)接管。init进程会自动调用 wait 回收孤儿进程的退出状态,从而防止僵尸进程的产生。

总结

  • fork 用于创建新进程,提供灵活的并行执行机制;而 system 仅用于执行简单的 shell 命令,灵活性和安全性较低。
  • 内核在创建进程时自动打开标准输入、输出和错误文件描述符。
  • 进程控制块包含进程标识、状态、内存管理信息等,是操作系统管理进程的核心数据结构。
  • 需要多线程频繁切换的场合包括I/O密集型任务、实时系统和多任务并发。
  • 僵尸进程是已终止但未被父进程回收的进程,处理方法包括父进程调用 wait 系列函数或信号处理。

4、threadlocal的实现原理,业务用来做什么?程序什么时候应该使用线程,什么时候单线程效率高?cpu怎么调度进程?调度算法?多线程好处?

ThreadLocal 的实现原理及业务用途

ThreadLocal 的实现原理

ThreadLocal 是 Java 提供的一种机制,允许每个线程都拥有其私有的变量副本。主要实现原理包括:

  1. 内部类 ThreadLocalMap:每个 Thread 对象都有一个 ThreadLocalMap 实例,用于存储 ThreadLocal 变量。
  2. ThreadLocalMap 的键值对ThreadLocalMapThreadLocal 对象作为键,线程特定的值作为值,存储每个线程独有的变量。
  3. getset 方法
    • get 方法:从当前线程的 ThreadLocalMap 中获取与当前 ThreadLocal 对象关联的值。如果没有关联的值,则调用 initialValue 方法返回初始值,并将其存储在 ThreadLocalMap 中。
    • set 方法:将当前线程的 ThreadLocalMap 中与当前 ThreadLocal 对象关联的值设置为新值。
业务用途
  • 线程安全:在并发编程中,使用 ThreadLocal 可以避免线程间共享变量的竞争问题,从而简化线程安全的实现。
  • 会话管理:在Web应用中,使用 ThreadLocal 存储与用户会话相关的数据(如用户ID),方便在不同层之间传递而无需显式传递参数。
  • 事务管理:在数据库操作中,使用 ThreadLocal 存储事务对象,确保同一线程内的数据库操作在同一事务中执行。

程序何时应该使用线程,何时单线程效率高?

使用多线程的场景
  • I/O 密集型任务:如网络通信、文件读写等。多线程可以在等待I/O操作完成时切换执行其他任务,提高资源利用率。
  • 并行处理:需要同时处理多个独立任务时,如并行计算、Web服务器处理多个请求。
  • 实时性要求:需要快速响应外部事件,如图形界面应用、实时数据处理。
使用单线程的场景
  • CPU 密集型任务:如复杂的计算任务。如果任务本身是串行的,并且CPU是主要瓶颈,单线程避免了线程切换开销。
  • 任务简单且短暂:如简单的数据处理或一次性任务,使用多线程可能引入不必要的复杂性和上下文切换开销。
  • 共享资源访问:当任务需要频繁访问和修改共享资源,单线程可以避免复杂的同步机制和数据竞争问题。

CPU如何调度进程?调度算法?

CPU 调度过程
  1. 选择进程:操作系统根据调度算法选择下一个要运行的进程或线程。
  2. 上下文切换:保存当前进程的状态(寄存器、程序计数器等)到进程控制块(PCB),然后从选定进程的PCB中恢复其状态。
  3. 更新调度队列:将当前进程移出运行队列,并将选定进程移入运行队列。
调度算法
  • 先来先服务(FCFS):按进程到达的顺序调度,简单但可能导致长时间等待。
  • 短作业优先(SJF):优先调度执行时间最短的进程,最小化平均等待时间,但需要知道作业长度。
  • 轮转法(Round Robin):每个进程按时间片轮流执行,适用于时间共享系统。
  • 优先级调度:根据进程优先级调度,优先级高的进程优先执行。可能导致低优先级进程饥饿。
  • 多级队列调度:将进程分成多个队列,每个队列有不同的调度算法,如交互式进程和批处理进程分开调度。

多线程的好处

  • 并行处理:多线程可以利用多核CPU的并行处理能力,显著提高计算任务的执行速度。
  • 资源利用率高:在I/O密集型任务中,多线程可以在等待I/O操作时执行其他任务,提高资源利用率。
  • 响应性好:在图形界面应用中,使用多线程可以保持界面响应,同时在后台执行耗时任务。
  • 结构清晰:多线程将不同任务分离到不同的线程中,提高程序结构的清晰度和可维护性。

总结

  • ThreadLocal 提供线程局部变量,确保每个线程都有独立的变量副本,常用于线程安全、会话管理和事务管理。
  • 使用多线程适用于I/O密集型任务、并行处理和实时性要求的场景;单线程适用于CPU密集型任务、简单短暂任务和共享资源访问频繁的场景。
  • CPU调度进程通过选择进程、上下文切换和更新调度队列来实现,常见调度算法包括FCFS、SJF、Round Robin、优先级调度和多级队列调度。
  • 多线程的好处包括并行处理、资源利用率高、响应性好和程序结构清晰。

5、进程怎么实现访问隔离?线程有什么数据是自己私有,哪些是共享的?io密集型和计算密集型分别适合多线程还是多进程?如何理解协程不被操作系统内核管理,而完全是由程序控制?用户级线程和内核级线程的区别?

进程如何实现访问隔离?

进程的访问隔离通过以下几种机制实现:

  1. 独立地址空间

    • 每个进程拥有独立的虚拟地址空间,进程的内存分布在独立的代码段、数据段、堆和栈中。
    • 使用硬件支持的内存管理单元(MMU),通过页表或段表将虚拟地址映射到物理内存,确保进程之间的内存访问隔离。
  2. 进程控制块(PCB)

    • 操作系统维护每个进程的控制块(PCB),其中包含进程的状态、寄存器信息、打开的文件描述符等。
    • PCB之间相互独立,进程间无法直接访问彼此的控制块。
  3. 权限和安全机制

    • 操作系统通过用户态和内核态的特权级别控制,防止进程非法访问内核数据或其他进程数据。
    • 使用系统调用和保护模式来限制进程的操作范围。

线程的数据私有与共享

私有数据:
  • :每个线程都有自己的栈空间,用于存储函数调用、局部变量、返回地址等。
  • 寄存器:包括程序计数器、栈指针和通用寄存器等,在线程切换时保存和恢复。
共享数据:
  • 全局变量和静态变量:位于进程的全局数据段和静态数据段,所有线程共享。
  • :通过动态内存分配(如 malloc)获取的内存区域,所有线程共享。
  • 打开的文件描述符:所有线程共享进程打开的文件描述符表,可以共同操作文件。

IO密集型和计算密集型任务适合的多线程与多进程模型

IO密集型任务:
  • 多线程:适合IO密集型任务,因为线程的上下文切换开销较低,能够高效处理大量的IO操作并保持高并发性。
  • 多进程:也可以使用,但进程上下文切换开销较大,一般不如多线程高效。
计算密集型任务:
  • 多进程:适合计算密集型任务,因为各进程独立运行,不会相互干扰,可以充分利用多核CPU资源。
  • 多线程:也可以使用,但需要注意线程间的同步和资源竞争问题。

理解协程不被操作系统内核管理,而完全由程序控制

  • 用户态调度:协程是完全由用户程序在用户态进行调度和切换的,不依赖操作系统内核进行上下文切换。
  • 轻量级:协程切换仅需保存和恢复少量上下文信息(如程序计数器和局部变量),开销极低。
  • 协作式多任务:协程主动让出执行权(通过 yield 或类似操作),由程序显式控制切换时机和顺序。

用户级线程和内核级线程的区别

用户级线程(User-Level Threads):
  • 实现:在用户态库中实现,操作系统内核不感知。
  • 调度:由用户级线程库管理,用户态调度,开销较小。
  • 上下文切换:仅在用户态完成,不涉及内核,速度快。
  • 缺点:无法利用多处理器并行能力,一个线程阻塞会导致整个进程阻塞。
内核级线程(Kernel-Level Threads):
  • 实现:由操作系统内核直接支持和管理。
  • 调度:由内核调度程序管理,能够利用多处理器并行执行。
  • 上下文切换:涉及内核态和用户态的切换,开销较大。
  • 优点:可以实现真正的并行处理,一个线程阻塞不会影响其他线程。

总结

  • 进程访问隔离:通过独立地址空间、PCB和安全机制实现。
  • 线程数据:栈和寄存器是私有的,全局变量、静态变量、堆和文件描述符是共享的。
  • 任务适配:IO密集型任务适合多线程,计算密集型任务适合多进程。
  • 协程调度:由用户程序控制,不涉及操作系统内核。
  • 线程级别:用户级线程在用户态实现和调度,内核级线程由内核管理和调度。

6、进程有哪些状态,怎么转换的?线程状态及转化?进程的创建需要系统分配什么资源?僵尸进程和孤儿进程是什么,具体怎么查看?守护进程是什么?进程可以忽视信号吗?

进程状态及其转换

进程状态
  • 创建(New):进程正在被创建。
  • 就绪(Ready):进程已准备好运行,等待被分配CPU。
  • 运行(Running):进程正在CPU上执行。
  • 等待/阻塞(Waiting/Blocked):进程等待某个事件(如I/O操作完成)。
  • 终止(Terminated):进程已完成执行或被终止。
  • 挂起(Suspended):进程暂时不活动,可能被从内存中移到磁盘上。
状态转换
  • 创建 -> 就绪:进程创建完成,进入就绪队列。
  • 就绪 -> 运行:调度器选择进程,分配CPU执行。
  • 运行 -> 等待/阻塞:进程等待I/O或其他事件完成。
  • 等待/阻塞 -> 就绪:等待事件完成,重新进入就绪队列。
  • 运行 -> 就绪:时间片结束,进程回到就绪队列。
  • 运行 -> 终止:进程执行完成或被强制终止。
  • 就绪 -> 挂起:进程被挂起,移出内存。
  • 挂起 -> 就绪:挂起结束,进程回到内存中。

线程状态及其转换

线程状态
  • 新建(New):线程对象被创建。
  • 就绪(Runnable):线程准备运行,等待CPU调度。
  • 运行(Running):线程正在执行。
  • 等待(Waiting):线程等待另一个线程通知或事件完成。
  • 超时等待(Timed Waiting):线程等待指定时间后重新就绪。
  • 阻塞(Blocked):线程等待某个锁。
  • 终止(Terminated):线程完成执行或被终止。
状态转换
  • 新建 -> 就绪:线程调用start()方法。
  • 就绪 -> 运行:CPU调度线程执行。
  • 运行 -> 等待:线程调用wait()或类似方法。
  • 等待 -> 就绪:等待结束,重新调度。
  • 运行 -> 超时等待:线程调用sleep()等方法。
  • 超时等待 -> 就绪:等待时间结束,重新调度。
  • 运行 -> 阻塞:线程等待锁释放。
  • 阻塞 -> 就绪:锁释放,重新调度。
  • 运行 -> 终止:线程执行完成或被强制终止。

进程创建需要系统分配的资源

  • 进程控制块(PCB):存储进程状态、寄存器信息、调度信息等。
  • 地址空间:为代码段、数据段、堆和栈分配内存。
  • 文件描述符表:为进程打开的文件分配文件描述符。
  • 内核资源:包括进程表项、调度队列等。

僵尸进程和孤儿进程

僵尸进程
  • 定义:已终止但其父进程尚未调用waitwaitpid获取其退出状态的进程。
  • 查看:用ps aux | grep Z命令查看,状态为Z表示僵尸进程。
  • 处理:父进程调用waitwaitpid回收子进程。
孤儿进程
  • 定义:父进程先于子进程终止,子进程成为孤儿进程。
  • 查看:孤儿进程由init(PID 1)进程接管,可以用ps -ef | grep <PPID>查看父进程ID为1的进程。
  • 处理init进程会自动回收孤儿进程的资源。

守护进程

  • 定义:在后台运行的进程,不与任何终端关联,通常在系统启动时启动,提供系统级服务。
  • 特性:通常在系统启动时启动,并在后台运行,为其他程序或用户提供服务,如cronsshd等。

进程可以忽视信号吗?

  • 信号处理:进程可以通过信号处理程序处理信号,也可以选择忽略某些信号。
  • 忽略信号:通过调用signalsigaction函数,将信号处理程序设置为SIG_IGN
  • 不可忽略的信号:有些信号不能被忽略,如SIGKILLSIGSTOP,这些信号用于强制终止或暂停进程。

总结

  • 进程状态:创建、就绪、运行、等待/阻塞、终止、挂起,状态间根据事件进行转换。
  • 线程状态:新建、就绪、运行、等待、超时等待、阻塞、终止,状态间根据事件进行转换。
  • 进程资源:PCB、地址空间、文件描述符表、内核资源。
  • 僵尸进程和孤儿进程:僵尸进程已终止但未回收,孤儿进程父进程已终止。
  • 守护进程:后台运行的进程,提供系统服务。
  • 信号处理:进程可忽略某些信号,但SIGKILLSIGSTOP等信号不可忽略。

7、协程是在生命周期的哪一步?什么情况适合用协程池,什么情况适合用线程池?fork会拷贝页表吗?线程的调度怎么完成?

协程是在生命周期的哪一步?

协程的生命周期

协程的生命周期和线程有些相似,但更加轻量化,通常包括以下几个状态:

  1. 创建(Created):协程对象被创建,但尚未开始执行。
  2. 就绪(Ready):协程已经准备好执行,等待调度器分配执行机会。
  3. 运行(Running):协程正在执行中。
  4. 挂起(Suspended):协程在执行过程中被主动挂起,等待被唤醒重新执行。
  5. 结束(Terminated):协程执行完毕或被终止。

协程的状态转换通常由用户代码控制,而非操作系统内核。具体的控制步骤如下:

  • 从创建到就绪:协程对象初始化完成。
  • 从就绪到运行:调度器分配执行机会,开始执行协程代码。
  • 从运行到挂起:协程主动调用yield或类似操作,暂时让出执行权。
  • 从挂起到就绪:外部事件或其他协程唤醒,协程重新进入就绪状态。
  • 从运行到结束:协程执行完毕或遇到终止条件,进入终止状态。

适合用协程池和线程池的情况

协程池
  • I/O密集型任务:协程适合处理大量I/O操作,如网络请求、文件读写等。在这些场景下,协程的轻量级上下文切换和非阻塞特性可以显著提高并发处理能力。
  • 高并发轻量任务:适合大量小任务需要高并发处理,如Web服务器的请求处理、事件驱动的系统。
线程池
  • CPU密集型任务:线程适合执行计算密集型任务,如复杂计算、数据处理等。线程可以充分利用多核CPU资源,实现真正的并行计算。
  • 需要操作系统级别支持的任务:如多线程应用中需要利用操作系统提供的多核并行能力、并需要线程间同步机制(如锁、信号量等)的场景。

fork 会拷贝页表吗?

是的,fork 会拷贝页表,但具体的拷贝机制依赖于操作系统的实现,通常是采用写时复制(Copy-On-Write, COW)机制:

  • 页表拷贝fork 系统调用创建子进程时,父进程的页表会被拷贝到子进程,但实际的物理内存页不会立即拷贝。
  • 写时复制:父子进程共享相同的物理内存页,直到其中一个进程尝试写入某个页时,内核才会复制该页,以确保父子进程拥有独立的内存副本。

线程的调度怎么完成?

线程调度的实现

线程调度是操作系统的一个关键功能,它决定了哪些线程可以在何时运行。线程调度可以在用户态和内核态完成:

  1. 内核态调度

    • 内核级线程(Kernel-Level Threads, KLTs)由操作系统内核调度。
    • 调度器根据调度算法(如时间片轮转、优先级调度等)选择下一个要运行的线程。
    • 调度过程涉及保存和恢复线程上下文(如寄存器、程序计数器等)。
  2. 用户态调度

    • 用户级线程(User-Level Threads, ULTs)由用户级线程库调度。
    • 用户态调度器在用户空间选择和切换线程,不涉及内核,因此切换开销较小。
    • 需要注意用户态调度的限制:一个用户级线程阻塞可能导致整个进程阻塞。
调度算法
  • 时间片轮转(Round Robin):每个线程按时间片轮流执行,时间片结束后切换到下一个线程。
  • 优先级调度(Priority Scheduling):根据线程的优先级选择下一个执行的线程,高优先级线程优先执行。
  • 多级反馈队列(Multilevel Feedback Queue):多级队列结合时间片轮转和优先级调度,线程可以在不同级别队列间移动,适应不同类型的负载。
  • 短作业优先(Shortest Job Next, SJN):优先调度预计执行时间最短的线程,适用于某些特定场景。

总结

  • 协程生命周期:协程的状态包括创建、就绪、运行、挂起和结束,状态转换由用户代码控制。
  • 协程池和线程池的适用情况:协程池适合I/O密集型和高并发轻量任务,线程池适合CPU密集型任务和需要操作系统级别支持的任务。
  • fork 会拷贝页表:但采用写时复制机制,实际物理内存页只有在写操作时才被复制。
  • 线程调度的实现:包括内核态调度和用户态调度,采用不同的调度算法如时间片轮转、优先级调度等。
  • 22
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值