news 2026/8/27 20:46:15

26-CPU进程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
26-CPU进程

进程 - 线程
进程:
1.进程概念:
程序:存放在外存中的一段代码的集合,不动的,只是一个文件
进程:程序动态执行的过程,包括进程的创建、调度、消亡

一个程序可以有多个进程,各种软件的多用户同时登录

2.进程命令:
top命令,根据CPU占用率查看系统运行的进程
PID:进程的ID号,唯一区分进程的ID号(不是名字)
PPID:父进程的ID号
USER:进程的发起使用的用户

ps -ef不会实时刷新,只打印当前的进程信息
ps -ef | grep *.out搜索进程

pstree: 查看进程关系

ps -aux: 查看进行状态

kill: 杀死进程任务(发信号)
kill -l 查看kill的一些配置(不同的信号)
kill -信号编号 进程PID

SIGSEGV = Segmentation Violation(段错误)
信号编号:11
含义:程序试图访问不允许访问的内存区域
结果:程序崩溃,产生 "Segmentation Fault" 错误

SIGKILL = Signal Kill(强制终止信号)
信号编号:9
作用:立即终止进程,强制结束
特点:无法被捕获、阻塞或忽略

./a.out &: 后台执行任务

jobs: 查看后台的任务

ps: 查看终端下的前后台任务

fg 编号: 将后台任务放到前台

进程优先级别(NI):linux中值越小优先级越高,win中相反,默认优先级为10
nice -n 10 ./a.out 以指定优先级开启进程
renice -n 优先级 进程PID 更改运行中的进程优先级

3.进程的创建:
虚拟空间:
32位系统:4GB(2^32)
64位系统:16EB(2^64,但实际通常用 48 位 = 256TB)
可以划分为文本段数据段以及系统数据段
文本段:代码以及指令

特性:进程空间是独立的,每个进程独享进程空间
所有进程共享0-4G虚拟内存
分时复用,同一虚拟空间不同进程数据不重叠
MMU将虚拟地址空间映射为实际的物理空间



┌─────────────────────────────┐ 高地址 (0xFFFFFFFF)
│ 内核空间 │ ← 用户态无法访问
│ (Kernel Space) │ 只读
├─────────────────────────────┤
│ │
│ 栈 (Stack) │ ← 向下增长
│ ↓ │ 局部变量、函数调用
│ │
├─────────────────────────────┤
│ 空洞 │
│ (未使用) │
├─────────────────────────────┤
│ │
│ 堆 (Heap) │ ← 向上增长
│ ↑ │ malloc 分配
│ │
├─────────────────────────────┤
| 字符串常量区(.rodata) | <-字符串常量
|-----------------------------|
│ 未初始化数据段 (.bss) │ ← 未初始化的全局变量,静态变量 加载到进程空间后清零
├─────────────────────────────┤
│ 已初始化数据段 (.data) │ ← 初始化的全局变量,静态变量
├─────────────────────────────┤
│ 代码段 (.text) │ ← 程序代码(只读)
│ │
└─────────────────────────────┘ 低地址 (0x00000000)




进程的调度:
1.多个进程任务需要同时进行
2.常见的进程调度算法:
以前先来先执行,后来后执行
后来短作业优先调度
后来高优先级调度

中断:现在(时间片轮转调度算法)
打断当前的操作 -> 保存现场 -> 切换任务 -> 保存现场 -> 切换任务 -> 恢复现场 -> 继续任务
CPU在进程任务中执行的一小段时间,叫做时间片
宏观并行,微观串行

以后,多级队列反馈
以后,负载均衡

进程的状态:
1.就绪态(R): 没有被CPU轮转到的正在执行的任务,等待被执行
2.运行态(R): 正在被CPU执行的任务,
3.可唤醒等待态(S): 由于缺少资源让程序暂时挂起的状态
4.不可唤醒等待态(D): 等待的过程中不可被唤醒,人为延时,条件触发等...
5.停止态(T): 由于系统需要让进程任务暂停,则将进程设置为停止态,debug调试
6.僵尸态(Z): 进程代码已经执行完了,空间没有被回收(尽量避免)
7.结束态(X): 代码执行结束,空间被回收的那一时刻处于结束态

进程相关的函数接口:
1.fork: man 2 fork
#include <sys/types.h>
#include <unistd.h>

pid_t fork(void);

功能: 创建一个子进程空间,调用的进程被认为子进程的父进程
子进程是父进程的完整副本(代码、数据、堆、栈、文件描述符,缓存等)
数据一样,但是相互独立!
堆区地址一样,也就是虚拟地址一样,但物理地址内存不一样
同一个教室,上午,下午,晚上都有同学,但是同学不一样。
父子进程从 fork() 返回后各自独立运行
参数:缺省
返回:
成功,父进程返回子进程的PID,子进程返回0
失败,返回-1,

虚拟内存与物理地址
每个进程都有一个独立的页表,存储该进程的虚拟地址到物理地址的映射。进程切换时,CPU 加载对应进程的页表基址(CR3 寄存器),实现地址转换的切换。因此,同一虚拟地址在不同进程中指向不同的物理地址,但不会混淆。父地址与子地址相同,当子进程复制父进程虚拟地址后,物理地址权限更新为只读,子进程按照虚拟地址写入时触发COW(Copy-on-Write),复制新的物理地址给子进程的独立页表的虚拟地址,子进程与父进程的地址一样,但指向不同的物理地址。

刚 fork() 完,子进程是直接读取父进程的物理内存地址(共享的),并没有复制数据;
只有当子进程(或父进程)试图修改时,才会触发复制,复制出一份独立的物理页。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
pid_t pid = fork();

if (pid == -1) {
perror("fork failed");
return 1;
}

if (pid == 0) {
// 子进程代码
printf("子进程: PID = %d, 父进程 PID = %d\n",
getpid(), getppid());
} else {
// 父进程代码
printf("父进程: PID = %d, 子进程 PID = %d\n",
getpid(), pid);
}

return 0;
}

父进程: PID = 1234, 子进程 PID = 1235
子进程: PID = 1235, 父进程 PID = 1234

┌─────────────────────────────────────────────────────┐
│ 父进程 │
│ PID = 1234 │
│ │
│ int pid = fork(); ← 调用 fork │
│ │ │
│ ├──────────────────┐ │
│ ↓ ↓ │
│ fork() 返回 fork() 返回 │
│ 子进程 PID 0 │
│ (1235) │ │
│ │ │ │
│ ↓ ↓ │
│ if (pid > 0) if (pid == 0) │
│ 父进程执行 子进程执行 │
│ │
└─────────────────────────────────────────────────────┘


fork()使用场景:
1.创建守护进程,2.并行处理,3.fork()+exec()执行新程序

特殊
1.僵尸进程:// 子进程退出后,父进程没有调用 wait()
// 子进程变成僵尸进程,占用进程表项
解决:#include <sys/wait.h>

// 父进程等待子进程结束
wait(NULL);
waitpid(pid, NULL, 0);

2.孤儿进程:// 父进程先退出,子进程变成孤儿进程
// 孤儿进程被 init (PID=1) 收养

3.调用两次fork
fork();
fork();
会有四个进程,一个本身,两个子进程,一个子子进程


创建子进程 pid_t pid = fork();
子进程判断 if (pid == 0)
父进程判断 if (pid > 0)
错误处理 if (pid == -1)
等待子进程 wait(NULL);
等待特定子进程 waitpid(pid, NULL, 0);
获取当前 PID getpid()
获取父进程 PID getppid()
执行新程序 execlp("ls", "ls", "-l", NULL);

================================================================================================
exit()

void exit(int status);
功能: 进程结束
终止调用进程
刷新所有标准 I/O 缓冲区(stdio 缓冲)
关闭所有打开的文件描述符
执行注册的 atexit() 和 on_exit() 函数
将 status 传递给父进程(父进程可通过 wait() 获取)

status 进程退出状态码(0-255),0 表示成功,非 0 表示失败

exit(status) = 终止当前进程,刷新缓冲区,执行 atexit() 清理函数,返回 status 给父进程。与return 在 main() 中等效,但普通函数中只返回调用者。子进程用 _exit() 避免刷新父进程缓冲区。
status 由子进程在退出时主动设置(如 exit(1) 表示错误),父进程通过 wait() 读取这个值,从而知道子进程的执行结果,并作出相应处理。

常用状态码
0 程序正常结束,成功
1 一般性错误
2 使用错误(如参数不正确)
EXIT_SUCCESS 成功(定义在 <stdlib.h>,值为 0)
EXIT_FAILURE 失败(定义在 <stdlib.h>,值为 1)

===================================================================================================

wait()
#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *wstatus);

作用:等待任意一个子进程(谁先结束就等谁)
阻塞行为 如果没有子进程结束,阻塞等待
返回值 结束的子进程 PID pid_t类型(int),失败返回-1
状态信息 通过 status(指针) 返回子进程退出状态

位 31-24 未使用 / 信号码 / 未使用 始终为 0
位 23-16 用户码 / 未使用 / 未使用 始终为 0
位 15-8 信号码 / 用户码 用户退出码 (WEXITSTATUS)
位 7 core 标志 core 标志 (WCOREDUMP)
位 6 stop 标志 stop 标志 (WIFSTOPPED)
位 5-0 信号编号 信号编号 (WTERMSIG)


exit(int status) 参数是 int(32 位),但内核只保留最低 8 位(0-255),高位被丢弃!

*WIFEXITED(status) 是否正常退出 真/假 任何情况
*WEXITSTATUS(status) 获取退出码 (0-255) 退出码 WIFEXITED 为真时
*WIFSIGNALED(status) 是否被信号终止 真/假 任何情况
*WTERMSIG(status) 获取终止信号编号 信号编号 WIFSIGNALED 为真时
WCOREDUMP(status) 是否产生 core dump 真/假 WIFSIGNALED 为真时
WIFSTOPPED(status) 是否被信号停止 真/假 任何情况
WSTOPSIG(status) 获取停止信号编号 信号编号 WIFSTOPPED 为真时
WIFCONTINUED(status) 是否从停止恢复 真/假 任何情况

*WEXITSTATUS(status) 位 15-8 ((status) >> 8) & 0xff
WCOREDUMP(status) 位 7 ((status) & 0x80)
WIFSTOPPED(status) 位 6 (((status) & 0xff) == 0x7f)
*WTERMSIG(status) 位 5-0 (status) & 0x7f
WSTOPSIG(status) 位 5-0 (status) & 0x7f
*WIFEXITED(status) 位 6-0(低7位) (((status) & 0x7f) == 0)
*WIFSIGNALED(status) 位 6-0(低7位) (((status) & 0x7f) != 0)

传入的参数为,wait(&status)写的这个status(int)4字节32位,不是指针是变量名字。

================================================================================================
waitpid(pid, status, options);

pid_t waitpid(pid_t pid, int *wstatus, int options);

参数:
pid 要等待的进程ID
status 存储退出状态的指针
options 控制行为的选项

pid > 0 等待特定的子进程(PID = pid)
pid == -1 等待任意子进程(等价于 wait())
pid == 0 等待同进程组的任意子进程
pid < -1 等待进程组 ID = pid 的任意子进程

options参数:
0 阻塞等待(默认)一直等着结束
WNOHANG 非阻塞,如果没有子进程结束则立即返回 0
WUNTRACED 捕获已停止的子进程

成功返回回收进程的ID,失败返回-1, 如果 非阻塞下 子进程没有结束返回0

wait() == waitpid(-1, status, 0)

常见用法:
等待特定子进程(waitpid(pid, NULL, 0))
非阻塞等待(waitpid(pid, NULL, WNOHANG))
轮询多个子进程(waitpid(-1, NULL, WNOHANG))

==================================================================================================

进程的消亡:
僵尸态(Z)产生原因:
进程代码执行完毕,但空间没有被回收
如何避免?
1.(用的不多)让父进程先结束,子进程称为孤儿进程,被init进程收养,子进程结束,init进程被回收子进程空间
2.子进程结束,父进程回收子进程空间

===================================================================================================
什么是虚拟空间?虚拟空间有哪些部分?
虚拟空间(虚拟地址空间)是操作系统为每个进程提供的“假想”内存空间,让进程以为自己独占整个内存。
每个进程有独立的虚拟地址空间
虚拟地址通过 MMU + 页表 映射到物理地址
同一虚拟地址在不同进程中映射到不同物理地址
32位 Linux 32 位 4 GB
64位 Linux 48 位(实际使用) 256 TB
64位 Linux 64 位(理论) 16 EB

32位系统:2^32 = 4,294,967,296 字节 ≈ 4 GB
64位系统:2^48 = 281,474,976,710,656 字节 ≈ 256 TB


┌─────────────────────────────┐ 高地址 (0xFFFFFFFF)
│ 内核空间 │ ← 用户态无法访问
│ (Kernel Space) │ 只读
├─────────────────────────────┤
│ │
│ 栈 (Stack) │ ← 向下增长
│ ↓ │ 局部变量、函数调用
│ │
├─────────────────────────────┤
│ 空洞 │
│ (未使用) │
├─────────────────────────────┤
│ │
│ 堆 (Heap) │ ← 向上增长
│ ↑ │ malloc 分配
│ │
├─────────────────────────────┤
| 字符串常量区(.rodata) | <-字符串常量
|-----------------------------|
│ 未初始化数据段 (.bss) │ ← 未初始化的全局变量,静态变量 加载到进程空间后清零
├─────────────────────────────┤
│ 已初始化数据段 (.data) │ ← 初始化的全局变量,静态变量
├─────────────────────────────┤
│ 代码段 (.text) │ ← 程序代码(只读)
│ │
└─────────────────────────────┘ 低地址 (0x00000000)

虚拟空间是操作系统为每个进程提供的独立地址空间,从低地址到高地址依次为:代码段(.text) → 数据段(.data) → BSS段(.bss) → 堆(Heap) → 栈(Stack) → 内核空间。堆向上增长,栈向下增长,各区域有独立的权限控制。

====================================================================================================
虚拟空间与物理空间的关系?

虚拟空间是每个进程独立的“假想”地址空间,物理空间是所有进程共享的真实内存。MMU + 页表将虚拟地址转换为物理地址。不同进程的同一虚拟地址可以映射到不同的物理地址,实现了进程隔离;多个进程的页表也可以指向同一物理地址,实现了共享内存。

=====================================================================================================
MMU是什么?什么是页表?与页表的关系?
MMU 是 CPU 中负责地址转换的硬件模块;页表是内存中存储地址转换规则的数据结构。MMU 依靠当前进程的页表,将虚拟地址实时翻译成物理地址。

流程:
进程 A 运行 → CPU 加载进程 A 的页表基址到 MMU 的寄存器中。
程序访问一个虚拟地址。
MMU 拿着这个虚拟地址,去查询当前进程 A 的页表。
页表 返回物理地址给 MMU。
MMU 将翻译后的物理地址发送给内存控制器,完成读/写操作

====================================================================================================
什么是寄存器?作用?
寄存器是 CPU 内部的一小块超高速存储单元,用于暂存指令、数据和地址。
寄存器是计算机体系中最快的存储设备,比内存(RAM)快几十到几百倍。
暂存数据 临时存储计算结果,避免频繁访问内存
地址计算 存储内存地址,用于寻址
指令执行 保存当前正在执行的指令
程序控制 控制程序执行流程(跳转、循环、函数调用)
状态记录 记录运算结果的状态(进位、溢出、零标志等)

架构 通用寄存器数量
x86(32位) 8 个
x86_64(64位) 16 个
ARM(32位) 16 个
ARM(64位) 32 个
寄存器是 CPU 与内存之间的桥梁。它的大小取决于 CPU 架构(如 64 位系统的寄存器为 64 位,即 8 字节)。
CPU 架构 寄存器大小 字节数
32 位 CPU 32 位 4 字节
64 位 CPU 64 位 8 字节
ARM 32 位 32 位 4 字节
ARM 64 位 64 位 8 字节

CR3 控制寄存器 页表基址(物理地址)
RAX 通用寄存器 数据/地址值(虚拟地址)
RSP 通用寄存器 栈指针(虚拟地址)
RIP 通用寄存器 指令指针(虚拟地址)

====================================================================================================
什么是进程?线程?
进程是“程序的执行实例”,是操作系统进行资源分配的基本单位。每个进程都拥有独立的虚拟地址空间(0 - 4G)、内存、文件描述符等资源(文本段,数据段,系统数据段),进程间互不干扰,相当于一个“独立运行的大箱子”。比如,你同时打开浏览器和音乐播放器,它们就是两个独立的进程。
线程是“进程内的执行流”,是CPU进行任务调度的基本单位。一个进程里可以包含多个线程,它们共享进程的资源(如内存),但各自拥有独立的 栈 (8M)和寄存器(其余堆区,数据区,文本区与其他线程共享),相当于“大箱子里的多个小工人”。比如,浏览器进程里,一个线程负责渲染页面,另一个线程负责下载资源。

====================================================================================================
进程如何处理?有哪些CPU调度方法?
进程调度由内核的调度器完成,核心是选择下一个运行的进程,并进行上下文切换。调度算法包括 FCFS、SJF、优先级、时间片轮转(RR)和多级反馈队列(MLFQ),现代操作系统通常采用 MLFQ 或其变体,平衡交互式响应和后台吞吐量。
上下文 = 进程在 CPU 上运行所需的所有状态信息。
┌─────────────────────────────────────────────────────────────────────────────┐
│ 进程的上下文 │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 1. 寄存器内容 │ │
│ │ ├── 通用寄存器 (RAX, RBX, RCX, RDX...) │ │
│ │ ├── 栈指针 (RSP) │ │
│ │ ├── 基址指针 (RBP) │ │
│ │ └── 指令指针 (RIP) ← 当前执行到哪条指令 │ │
│ │ │ │
│ │ 2. 程序计数器 (PC) │ │
│ │ │ │
│ │ 3. 栈数据 │ │
│ │ │ │
│ │ 4. 内存管理信息 (页表基址 CR3) │ │
│ │ │ │
│ │ 5. 打开的文件描述符 │ │
│ │ │ │
│ │ 6. 信号掩码 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
操作系统用 PCB 保存进程的上下文。
上下文切换是 CPU 从一个进程切换到另一个进程时,保存当前进程状态(寄存器、程序计数器、栈)并加载新进程状态的过程。开销包括寄存器保存/恢复、缓存和TLB失效。线程切换比进程切换更轻量,因为线程共享页表。

页表 (Page Table) 存储虚拟地址→物理地址映射的数据结构 内存 慢
TLB (Translation Lookaside Buffer) 页表映射的硬件缓存 CPU 内部 极快

PCB(Process Control Block,进程控制块)是操作系统内核中用于描述和管理进程的核心数据结构。
它存储了进程的所有关键信息,是操作系统感知和管理进程的唯一依据。操作系统通过 PCB 来记录每个进程的状态、资源、上下文等信息。

内存(内核空间) PCB 存储在内核空间中,由操作系统维护
CPU CPU 只存储当前正在运行的进程的部分上下文(寄存器值),PCB 本身不存储在 CPU 中
PCB 是一块内存数据,CPU 通过寄存器(如 RSP、RIP)指向当前进程的 PCB 或内核栈,但不会将整个 PCB 保存在 CPU 中。

CPU 不直接查找 PCB。它执行内核调度器的代码,调度器从运行队列中找到目标进程的 PCB 地址,然后通过 context_switch 函数将目标 PCB 中的寄存器值和页表地址加载到 CPU 硬件中,从而完成进程切换。

===================================================================================================
linux系统如何进程调度?与win区别?
Windows采用基于优先级的抢占式多任务调度。它的决策核心是线程的优先级。
调度器永远会选择当前优先级最高的就绪线程来运行

自2.6.23版本起,Linux内核采用了完全公平调度器(CFS, Completely Fair Scheduler)。
CFS的核心思想是“公平”。它摒弃了固定时间片,而是引入一个虚拟运行时间(vruntime) 的概念
。可以把它理解成每个进程“应该”获得的CPU时间。CFS总是选择vruntime最小的进程来运行,让所有进程的vruntime互相追赶,从而实现“完全公平”

数据结构:为了高效地找到vruntime最小的进程,CFS使用了一棵红黑树(Red-Black Tree) 来组织所有可运行的进程,这棵树能保证调度的效率为O(log N),即便有大量进程也能高效工作
优先级:进程的优先级(nice值)不影响vruntime追赶的目标,但会影响其增长的速率。高优先级进程的vruntime增长得慢,从而能获得更多的CPU时间。
多核优化:每个CPU核心都有自己的运行队列和红黑树,调度器还会定期进行负载均衡,避免部分核心过载而其它核心空闲。

系统 时间轮换机制 实现方式
Linux (CFS) 基于“虚拟时间片”的轮转 vruntime 追赶,动态计算时间片
Windows 基于“时间配额”的轮转 固定时间配额,优先级动态调整

Linux 和 Windows 都有时间轮换机制,但实现方式不同:Linux 通过 CFS 的“动态时间片 + 公平调度”实现轮转;Windows 则通过“固定时间配额 + 优先级轮转”实现,让高优先级进程可以插队执行。你所在的操作系统,其调度器本质上都是“时间轮换 + 优先级”的混合体。

====================================================================================================
时间片轮转原理解释?
时间片轮转是最直观、最公平的 CPU 调度算法。它的核心思想是:给每个进程分配一个固定的时间片,进程用完时间片后,强制让出 CPU,排到队列末尾,让下一个进程运行。
时间片轮转 = 所有进程排成队列,每个进程轮流执行一个固定时间片,时间片用完就排到队尾,让下一个进程执行。它公平、响应快,是分时操作系统的核心调度算法。

====================================================================================================
多核处理器如何处理进程线程?
多核处理器的核心任务就是如何将多个进程/线程高效地分配到多个CPU核心上执行。这主要涉及三个方面:调度、亲和性和同步。
进程:是系统资源分配的基本单位(拥有独立的虚拟地址空间、文件描述符等)。进程间的切换成本较高。
线程:是CPU调度的基本单位(同一进程内的线程共享进程的资源,但拥有独立的栈和寄存器上下文)。线程间的切换成本相对较低。
同一个进程的多个线程可以被调度到不同的CPU核心上并行执行。
不同的进程也可以被分配到不同的CPU核心上并行执行。

操作系统内核(如Linux)中的调度器负责决定哪个进程或线程在哪个CPU核心上运行。
Linux采用的是全局调度策略。它维护一个全局的运行队列(Run Queue),但为了提高效率,每个CPU核心都有自己的本地运行队列。调度器会实时监控各核心的负载(队列长度),并通过负载均衡机制,将任务从繁忙的核心迁移到空闲的核心,以确保所有核心都“有事可做”,最大化整体吞吐量。

“这个进程/线程最好只在这个核心上运行”。这种设置被称为CPU亲和性(CPU Affinity)。
可以充分利用CPU缓存(L1/L2/L3 Cache)。当一个线程在一个核心上运行时,它的数据会缓存到该核心的私有缓存中。如果频繁迁移核心,缓存就会失效(Cache Miss),导致性能下降。

多核并行执行最大的挑战是数据竞争。当多个核心同时读写同一块内存时,如果没有保护,数据就会出错。
1. 缓存一致性协议(如MESI)
每个CPU核心都有自己的L1/L2缓存。当核心A修改了变量X,而核心B的缓存中还存着X的旧值时,就产生了不一致。硬件层面通过MESI协议等缓存一致性协议来保证:当一个核心修改了某个缓存行,其他核心的对应缓存行会立即失效,强制它们从主存重新读取最新值。
2. 内存屏障(Memory Barrier)
编译器或CPU可能会为了优化性能而重排指令。在多核环境下,这种重排可能导致错误。内存屏障(如mb()、rmb()、wmb()) 是一种指令,它告诉CPU:屏障前面的内存访问必须全部完成,才能执行屏障后面的操作,从而保证了内存访问的顺序性。

多核处理器处理进程线程的核心,就是“并行调度”:操作系统调度器将就绪的进程/线程分发到不同的CPU核心上物理并行运行,同时通过负载均衡最大化吞吐量,并通过缓存一致性协议和内存屏障来保证多核间数据同步的正确性。

==================================================================================================
单核与多核的区别?
单核与多核处理器的本质区别在于物理上能同时执行的任务数量,
单核:
采用时间片轮转调度
每秒切换几百上千次(上下文切换)
切换有开销(保存/恢复寄存器、缓存失效)
用户感觉“同时运行”,实际上是高速交替
多核:
物理并行:两个任务真的在同时执行
每个核心有独立的寄存器、缓存、调度队列
多核之间共享内存(但需要通过缓存一致性协议保持同步)

单核是靠“快速轮流”制造“同时运行”的假象(并发),多核是真正“同时运行多个任务”(并行)。多核性能更强,但也引入了缓存一致性、锁竞争、负载均衡等新问题。

===================================================================================================
什么是进程?与程序有什么区别?
程序:存放在外存中的一段代码的集合,不动的,只是一个文件
进程:程序动态执行的过程,包括进程的创建、调度、消亡

一个程序可以有多个进程,各种软件的多用户同时登录

===================================================================================================
进程空间如何划分?每一个区域存放哪些东西?
进程空间是进程的虚拟地址空间,包含代码段、数据段、堆、栈等,是进程实际使用的内存布局,通过页表映射到物理内存。上下文是进程在 CPU 上运行的全部现场信息,由硬件上下文(所有寄存器值,包括通用寄存器、RSP、RIP、RFLAGS 以及 CR3 页表基址)和软件上下文(内核栈、浮点寄存器、信号屏蔽字等)构成。PCB(进程控制块)是内核中管理进程的核心数据结构,它存储了进程的所有元数据(PID、状态、优先级等),并通过 mm 指针指向进程的页表基址来关联进程空间,同时通过 regs 字段保存进程的上下文。三者关系为:进程空间是进程的“数据仓库”,上下文是进程在 CPU 上的“运行现场”,PCB 是连接两者的“管理档案”——它既通过页表指针定位进程空间,又通过保存和恢复上下文来管理进程在 CPU 上的执行,进程切换时,CPU 的当前上下文被存入 PCB,再从新进程的 PCB 中恢复上下文,同时切换 CR3 寄存器以切换地址空间。
上下文 = 寄存器值(主要) + 其他 CPU 相关状态 + 内存中的辅助信息
寄存器详情间以上

页表基址属于上下文,进程运行时保存在CR3寄存器中,并将对应进程的页表加载到TLB缓存,以供CPU读取进程空间信息
TLB是进程的页表在CPU中的缓存,为了快速访问进程空间
上下文是进程在 CPU 上运行时所依赖的全部寄存器值。这些值在进程运行时就在 CPU 中,在进程被切出时保存到 PCB,在被切入时从 PCB 恢复到 CPU。

PCB 主要包含四大部分:进程标识信息(PID、PPID)、状态与调度信息(状态、优先级)、上下文(寄存器值、RSP、RIP、CR3)、资源管理信息(内存页表、打开文件、信号)。其中上下文是 PCB 的核心,用于进程切换时保存/恢复 CPU 现场。
上下文包含寄存器值及其他 CPU 状态,CR3 页表基址是上下文的组成部分之一,进程运行时上下文在 CPU 中,切出时保存到 PCB,切入时从 PCB 恢复。PCB 存储在内核空间,通过调度器管理切入切出,MMU 根据 CR3 指向的页表将进程的虚拟地址转换为物理地址,从而访问进程空间。

===================================================================================================

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/27 20:45:16

少儿编程怎么选?

先说结论&#xff1a;别问"哪家最好"&#xff0c;这个问题没有答案。 我从今年春天开始给孩子挑编程课&#xff0c;试听上了不少&#xff0c;销售电话接了一堆。前期完全是懵的——每家都说自己课程体系科学、师资强、能培养思维&#xff0c;听完全都一样。 后来我摸…

作者头像 李华
网站建设 2026/8/27 20:45:09

大促期间被DDoS攻击怎么办?618、直播、秒杀,五个高危时刻的防护要

对电商、直播和在线发布业务而言&#xff0c;风险最集中的时刻&#xff0c;往往是活动开始后的关键窗口。正常用户、接口请求和攻击流量同时涌入&#xff0c;带宽、连接数、应用接口和源站资源可能一起出现异常。618、双11、直播开播、秒杀、新品发布和会员日又有明确时间点&am…

作者头像 李华
网站建设 2026/8/27 20:45:06

谷歌云上部署Claude Code:从零构建AI生成Web应用

很多同学第一次把 Claude Code 这类 AI 编程助手和谷歌云&#xff08;Google Cloud&#xff09;放在一起时&#xff0c;首先要面对的问题往往是“环境到底怎么搭”“AI 生成的代码到底怎么部署到云服务器上”“部署完怎么让它一直在线”。本文就围绕这条完整链路&#xff0c;从…

作者头像 李华
网站建设 2026/8/27 20:43:44

rust rbatis查询表

在 Rust 中使用 rbatis 查询表主要有三种方式&#xff1a;自动 CRUD、Wrapper 条件查询、以及 自定义 SQL&#xff08;htmlsql/pysql&#xff09;。以下是完整的实用指南&#xff1a;1. 添加依赖toml[dependencies] rbatis "4.5" # 或当前最新版本 rbdc-sqlite &q…

作者头像 李华
网站建设 2026/8/27 20:43:37

rust Sized trait什么意思

在 Rust 中&#xff0c;Sized trait 是一个非常基础且重要的 trait&#xff0c;它标志着类型的大小在编译时是已知的。简单来说&#xff1a;绝大多数类型都是 Sized 的&#xff0c;而“不定长类型”&#xff08;DST&#xff09;则不是。下面从几个方面来深入理解&#xff1a;1.…

作者头像 李华