深入理解进程概念:从冯诺依曼体系到Linux进程管理

📅 发布时间:2026/8/15 23:32:28
深入理解进程概念:从冯诺依曼体系到Linux进程管理
1. 冯诺依曼体系结构现代计算机的硬件基础冯诺依曼体系结构是现代计算机的硬件体系结构它将计算机硬件划分为五大类输入设备如键盘、鼠标、扫描仪等输出设备如显示器、打印机、音响等存储器内存主存和外部存储器运算器执行算术和逻辑运算控制器协调和控制计算机各部件工作在计算机中数据流是围绕内存工作的。这意味着CPU要处理任意数据都需要先把数据放到内存中。因此要运行一个程序先把这个程序的指令数据加载到内存中。2. 操作系统的功能层次划分2.1 操作系统是什么操作系统是一个软件负责对计算机上的软硬件资源进行管理。2.2 系统调用接口操作系统内核提供给上层用于进行计算机操作的接口。2.3 库函数库函数是对系统调用接口进行的二次封装接口如write、printf等。库函数与系统调用接口的关系库函数内部调用了系统调用接口。3. 进程概念3.1 不同角度的进程定义站在用户的角度进程就是运行中的程序站在系统的角度进程就是对于程序的动态运行中描述这个描述在Linux下是一个结构体struct task_struct{ }3.2 进程描述了什么信息进程控制块PCB中包含了丰富的信息标识符进程IDpid进程调度信息描述了如何调度一个程序的运行进程状态对进程不同运行状态进行描述让进程在合适的时候做合适的事情进程的内存管理进程内部所有数据都会占据内存了解进程内部内存管理方式内存内打开的文件信息通过了解这块信息更加深入地了解系统调用I/O进程间通信资源管理学习进程间通信进程信号信息描述进程的软中断相关内容4. 进程标识符与调度4.1 标识符进程ID进程ID其实就是一个数字。在Linux下可以通过ps指令查看进程信息ps -ef4.2 进程的调度并行多个进程可以同时运行串行有多个进程但是同一时间只能运行一个并行与串行在计算机上取决于CPU核心数量每个CPU核心上都有一套独立的寄存器保存程序运行的上下文信息来执行指令。计算机上运行的程序非常多但是CPU核心很少如何让它们同时运行CPU分时机制一个程序运行的时候只能在CPU上运行很短的一段时间时间片时间片运行完毕则切换下一个进程运行分时机制实现了多个程序在宏观上的并行但我们要理解微观上它们实际上是串行的一旦程序是切换运行的等切换回来的时候CPU又怎么知道该从哪里开始执行为了解决这个问题在struct task_structPCB-进程控制块中就保存了一系列程序的运行上下文pc寄存器内容即将要执行的指令地址一系列上下文数据正在处理的数据即将要处理的数据......因此一旦CPU切换轮转回来了就可以通过pid找到task_struct从其中取出这些上下文数据重新放到CPU寄存器中CPU就可以从原来的位置继续向下执行处理原来没有处理完的数据。5. 进程状态5.1 三态模型就绪、运行、阻塞5.2 Linux下的进程状态运行态就绪运行拿到时间片就能运行以及正在运行的都属于运行态。可中断休眠态可以被打断的阻塞状态阻塞态都有一个唤醒条件只有唤醒条件达成的时候才会转为就绪态。不可中断休眠态只能被条件唤醒不能被中断打断的阻塞状态。停止态让进程停止运行只能通过特定方式唤醒。追踪态调试的时候涉及到的状态。死亡态进程退出后资源全部释放完毕处于的一个短暂的状态。僵尸态一个进程退出后但是资源没有完全被释放的状态。5.3 僵尸态详解产生原因由于子进程退出但是父进程没有关注子进程的退出状态没有等待也没有忽略SIGCHLD信号所导致的。SIGCHLD信号就是子进程退出时操作系统给父进程发送的信号。忽略这个信号就是告诉操作系统子进程退出直接释放资源我不管他的返回值。危害会造成系统资源泄露。解决kill -9强制杀死。避免进程等待wait/waitpid等待子进程退出获取他的返回值。或者忽略SIGCHLD信号。6. 进程的内存管理程序地址空间6.1 程序地址空间概念程序地址空间进程的虚拟地址空间。进程的地址空间是虚拟的地址空间并不是给每个进程都分配了4G内存。程序地址空间实际上是系统给我们进程所描述的一个虚拟的地址空间并不是分配了这么多内存而是虚拟的地址分配。这个描述在pcb中是mm_struct结构体。6.2 虚拟地址空间作用实现数据在物理内存中的离散式存储提高内存利用率并进行内存访问控制提高进程独立性。6.3 虚拟地址的实现虚拟地址的实现依赖了一个关键性技术页表内存的分页式管理。页表信息虚拟地址和物理地址的映射关系访问权限缺页中断标志......虚拟地址组成页号针对页面的编号 页内偏移32位系统下地址大小4字节32个比特位页号占据地址中的高20位页内偏移占据地址中的低12位6.4 进程的独立性每个进程操作系统都会为该进程创建task_struct(pcb)其中就包含虚拟地址空间描述和页表信息。因此每个进程内部数据存储所分配的地址都是虚拟地址。这也是为什么进程间通信需要操作系统提供进程间通信资源的原因——让操作系统给多个不同的进程分一块大家都能访问的空间。6.5 内存置换当内存不够用了操作系统如何处理内存置换操作系统认为内存中的很多数据并不是一直在访问热数据。因此当内存不够用的时候操作系统就会根据一定的算法将指定内存中的数据置换出去存放到硬盘中。存放的这块硬盘区域有个专业名称交换分区。常见置换算法LRU最久未使用LFU最少未使用FIFO先进先出6.6 缺页中断当访问内存数据的时候通过页表将虚拟地址转换为物理地址但是转换的时候发现页表中置位了缺页中断位当前这个虚拟地址对应的那块内存数据没有在内存中。缺页中断的处理从交换分区将数据重新置换到内存中更新页表信息。6.7 分段式与段页式内存管理分段式内存管理将代码分为代码段、数据段等对于程序的内存管理比较友好。段页式内存管理对内存分段在每个段内进行分页管理。7. 进程控制创建、退出、等待、程序替换7.1 创建pid_t fork()功能通过复制父进程的方式创建一个子进程。复制了什么缓冲区调度切换相关的上下文信息虚拟地址空间页表信息文件描述符信息......pid_t pid fork(); if (pid 0) { // 错误处理 } else if (pid 0) { // 子进程运行的代码 } else { // 父进程运行的代码 }1. 复制了虚拟地址空间复制处理的子进程要执行的代码、要处理的数据跟父进程是一样的。创建子进程这里使用了写时拷贝技术创建子进程后子进程与父进程指向同一块内存区但是当任意一方对一块内存区域数据进行修改则给当前进程重新开辟一块空间拷贝新数据进去。这大大提高了创建子进程的效率。2. 复制了程序调度上下文信息pc寄存器保存的是即将要执行的指令地址程序运行到了哪里以及其他切换信息正在执行的指令正在处理的数据...7.2 退出终止程序的运行正常退出在main中return在任意位置调用exit()函数——exit()函数是一个库函数在任意位置调用_exit()函数——_exit()函数是一个系统调用函数库函数是对系统调用函数在特定场景下的一些功能补充库函数内部调用了系统调用函数。库函数在exit退出的时候会进行用户态空间的一些资源清理操作例如缓冲区的数据刷新。异常退出程序在运行中的时候遇到了某种异常内存访问错误导致程序崩溃。异常退出的情况下进程的返回值是没有参考意义的。7.3 等待父进程创建子进程之后等待子进程退出为什么要等待捕获子进程的退出状态获取他的返回值释放子进程所有资源避免产生僵尸进程。pid_t wait(int *wstatus); pid_t waitpid(pid_t pid, int *wstatus, int options);wstatus内部包含两个信息1. 进程的退出原因2. 进程的退出码处理逻辑先取低7位判断是否为0为0则表示正常退出status 0x7f再取低16位中的高8位当作退出码进行处理(status 8) 0xff。7.4 程序替换默认创建子进程之后子进程与父进程的代码段是一样的可以通过fork返回值进行分支控制但是这样会导致两个不同的功能放在一个程序中实现代码较为臃肿。如何让子进程重新运行另一个程序呢使用程序替换将新的程序指令 数据加载到内存中。将子进程的页表映射信息初始化并更新映射到新的内存区域加载到内存的新程序。初始化调度上下文信息让当前进程pc寄存器指向新程序指令的起始位置。Exec函数族execl, execlp, execle, execv, execvp, execvel和v的差别设置程序运行参数的差别l是通过不定参逐个给予v是组织成数组一次性给予有没有p的差别是否会到系统path环境变量指定的路径下去找这个程序execl(/bin/ls) vs execlp(ls)有没有e的差别子进程中是否自定义环境变量8. 内存内打开的文件信息8.1 进程的I/O文件的输入输出系统调用open, read, write, lseek, close文件描述符的本质是一个数组的下标一个进程内部能够打开的文件数量是有上限的重定向就是修改描述符所对应的描述信息而实现的。重定向修改数据的输出位置每个进程运行起来后默认会打开三个文件标准输入-键盘描述符0标准输出-显示器描述符1错误输出-显示器描述符29. 进程间通信资源进程间通信就是让操作系统为进程之间提供一个公共内存访问区。原因进程之间具有独立性。根据不同的应用场景操作系统提供了多种不同的方式管道数据传输、共享内存数据共享、消息队列数据传输、信号量同步控制、套接字。9.1 管道1. 管道的本质内核中的一块缓冲区匿名管道没有名字的管道没有标识符。其他进程怎么找到这个管道跟我通信呢特性只能用于具有亲缘进程关系的进程间通信创建子进程子进程会复制父进程的信息。操作在创建子进程之前创建管道然后创建子进程子进程就复制进程内部打开的文件信息。int pipe(int fd[2]); // 接收两个描述符 // fd[0]用于读fd[1]用于写命名管道有名字的管道可以被所有进程找到可以用于任意进程间通信。名字一个管道文件这个文件只是一个标识名字本质上管道就是内核的缓冲区。操作mkfifo(filename, flag);9.2 公共特性管道的特性是半双工通信可以选择方向的单向通信管道的数据写满了write就会阻塞管道中没有数据read就阻塞管道所有读端关闭则write就会触发异常-SIGPIPE所有写端关闭取完所有数据后继续read不再阻塞而是返回0数据传输是字节流传输以字节为单位进行数据传输