26-CPU进程

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 -信号编号 进程PIDSIGSEGV Segmentation Violation段错误信号编号11含义程序试图访问不允许访问的内存区域结果程序崩溃产生 Segmentation Fault 错误SIGKILL Signal Kill强制终止信号信号编号9作用立即终止进程强制结束特点无法被捕获、阻塞或忽略./a.out : 后台执行任务jobs: 查看后台的任务ps: 查看终端下的前后台任务fg 编号: 将后台任务放到前台进程优先级别NIlinux中值越小优先级越高win中相反默认优先级为10nice -n 10 ./a.out 以指定优先级开启进程renice -n 优先级 进程PID 更改运行中的进程优先级3.进程的创建:虚拟空间32位系统4GB2^3264位系统16EB2^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.hpid_t fork(void);功能 创建一个子进程空间调用的进程被认为子进程的父进程子进程是父进程的完整副本代码、数据、堆、栈、文件描述符缓存等数据一样但是相互独立堆区地址一样也就是虚拟地址一样但物理地址内存不一样同一个教室上午下午晚上都有同学但是同学不一样。父子进程从 fork() 返回后各自独立运行参数缺省返回成功父进程返回子进程的PID子进程返回0失败返回-1,虚拟内存与物理地址每个进程都有一个独立的页表存储该进程的虚拟地址到物理地址的映射。进程切换时CPU 加载对应进程的页表基址CR3 寄存器实现地址转换的切换。因此同一虚拟地址在不同进程中指向不同的物理地址但不会混淆。父地址与子地址相同当子进程复制父进程虚拟地址后物理地址权限更新为只读子进程按照虚拟地址写入时触发COWCopy-on-Write复制新的物理地址给子进程的独立页表的虚拟地址子进程与父进程的地址一样但指向不同的物理地址。刚 fork() 完子进程是直接读取父进程的物理内存地址共享的并没有复制数据只有当子进程或父进程试图修改时才会触发复制复制出一份独立的物理页。#include stdio.h#include sys/types.h#include unistd.hint 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 (PID1) 收养3.调用两次forkfork();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-2550 表示成功非 0 表示失败exit(status) 终止当前进程刷新缓冲区执行 atexit() 清理函数返回 status 给父进程。与return 在 main() 中等效但普通函数中只返回调用者。子进程用 _exit() 避免刷新父进程缓冲区。status 由子进程在退出时主动设置如 exit(1) 表示错误父进程通过 wait() 读取这个值从而知道子进程的执行结果并作出相应处理。常用状态码0 程序正常结束成功1 一般性错误2 使用错误如参数不正确EXIT_SUCCESS 成功定义在 stdlib.h值为 0EXIT_FAILURE 失败定义在 stdlib.h值为 1wait()#include sys/types.h#include sys/wait.hpid_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) 参数是 int32 位但内核只保留最低 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) 0xffWCOREDUMP(status) 位 7 ((status) 0x80)WIFSTOPPED(status) 位 6 (((status) 0xff) 0x7f)*WTERMSIG(status) 位 5-0 (status) 0x7fWSTOPSIG(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 要等待的进程IDstatus 存储退出状态的指针options 控制行为的选项pid 0 等待特定的子进程PID pidpid -1 等待任意子进程等价于 wait()pid 0 等待同进程组的任意子进程pid -1 等待进程组 ID pid 的任意子进程options参数0 阻塞等待默认一直等着结束WNOHANG 非阻塞如果没有子进程结束则立即返回 0WUNTRACED 捕获已停止的子进程成功返回回收进程的ID失败返回-1, 如果 非阻塞下 子进程没有结束返回0wait() 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 GB64位 Linux 48 位实际使用 256 TB64位 Linux 64 位理论 16 EB32位系统2^32 4,294,967,296 字节 ≈ 4 GB64位系统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快几十到几百倍。暂存数据 临时存储计算结果避免频繁访问内存地址计算 存储内存地址用于寻址指令执行 保存当前正在执行的指令程序控制 控制程序执行流程跳转、循环、函数调用状态记录 记录运算结果的状态进位、溢出、零标志等架构 通用寄存器数量x8632位 8 个x86_6464位 16 个ARM32位 16 个ARM64位 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 内部 极快PCBProcess 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 指向的页表将进程的虚拟地址转换为物理地址从而访问进程空间。