XV6操作系统:proc机制学习笔记

news2026/4/18 22:22:23
梳理struct proc的结构如下通过分析一个父子进程的程序关系来理解process的工作原理#include stdio.h #include stdlib.h #include unistd.h #include fcntl.h #include sys/wait.h #include string.h int main() { int fd; char buffer[64]; const char *msg Hello!\n; pid_t pid fork(); if (pid 0) { exit(1); } else if (pid 0) { fd open(test.txt, O_WRONLY | O_CREAT | O_TRUNC, 0644); write(fd, msg, strlen(msg)); close(fd); exit(0); } else { wait(NULL); fd open(test.txt, O_RDONLY); read(fd, buffer, sizeof(buffer)); printf(父进程读取到子进程写下的: %s, buffer); close(fd); } return 0; }1.阶段一 fork... pid_t pid fork(); ...1.1 单核CPU情形一个父进程fork出子进程pid 0的过程首先父进程需要在内存中遍历进程表找到UNUSED的闲置进程。接下来为子进程复制父进程的各种资源openfile、cwd分配属于它自己的pagetable页表、trapframe。再把state从USED设置为RUNNABLE进入调度器就绪队列。调度器scheduler()一直在寻找RUNNABLE的进程通过上下文切换内存地址和寄存器的存储值指的是struct context状态state变为RUNNING。1.2 多核CPU情形单核CPU是不需要考虑多核的冲突问题的实际上要是另外的CPU闲着就有很大概率会来插手可能会导致proc槽位浪费亦或者程序冲突。这就是spinlock的价值。CPU0会首先调用 acquire(p-lock) 获取自旋锁, p是我们当前process的结构体名。此时如果其他 CPU 核心想要动这个 proc 结构体就会进入spin原地打转直到 CPU0 把进程状态安全地改写为 USED 并释放锁。仔细来看spinlock这里有一个误区需要辩解。proc结构体中有spinlock结构体spinlock结构体中有cpu结构体cpu结构体中又有proc结构体。那不是内存要被无限套娃撑爆。然而spinlock结构体中保存的只是指向cpu结构体的指针cpu结构体中也是这样的。struct spinlock { uint locked; // Is the lock held? char *name; // Name of lock. struct cpu *cpu; // The cpu holding the lock. }; struct cpu { struct proc *proc; // The process running on this cpu, or null. struct context context; // swtch() here to enter scheduler(). int noff; // Depth of push_off() nesting. int intena; // Were interrupts enabled before push_off()? }; struct proc { struct spinlock lock; enum procstate state; // Process state void *chan; // If non-zero, sleeping on chan int killed; // If non-zero, have been killed int xstate; // Exit status to be returned to parents wait int pid; // Process ID struct proc *parent; // Parent process uint64 kstack; // Virtual address of kernel stack uint64 sz; // Size of process memory (bytes) pagetable_t pagetable; // User page table struct trapframe *trapframe; // data page for trampoline.S struct context context; // swtch() here to run process struct file *ofile[NOFILE]; // Open files struct inode *cwd; // Current directory char name[16]; // Process name (debugging) };这样的三角设计的设计导致1. proc 包含 lock锁保护进程状态保护进程内部成员变量2. lock 指向 cpu防止其他CPU篡改数据3. cpu 指向 proc内核随时能知道当前在跑哪个进程2.阶段二 trapframe子进程开始运行并调用了 open 和 write() 系统调用。... else if (pid 0) { fd open(test.txt, O_WRONLY | O_CREAT | O_TRUNC, 0644); write(fd, msg, strlen(msg)); ...2.1 用户态切换内核态trapframe.Suser程序没有权限直接操作硬盘。调用 open() 或者 write() 时RISC-V 处理器会执行 ecall 指令硬件立刻提高特权级通过蹦床内核态虚拟内存页表和用户态虚拟内存页表完全相同的地方这样进行转换时具有绝对地址才不会出问题跳入kernel态。以下为一次write系统调用示意图#include ritrampoline.Sscv.h #include memlayout.h .section trampsec .globl trampoline .globl usertrap trampoline: .aglign 4 .globl uservec .globl userret uservec: csrw sscratch, a0 # a0写入sscratch,用户态数据暂存 li a0, TRAPFRAME # 用trapframe替代a0 # save the user registers in TRAPFRAME sd ra, 40(a0) sd sp, 48(a0) sd gp, 56(a0) sd tp, 64(a0) sd t0, 72(a0) sd t1, 80(a0) sd t2, 88(a0) sd s0, 96(a0) sd s1, 104(a0) sd a1, 120(a0) sd a2, 128(a0) sd a3, 136(a0) sd a4, 144(a0) sd a5, 152(a0) sd a6, 160(a0) sd a7, 168(a0) sd s2, 176(a0) sd s3, 184(a0) sd s4, 192(a0) sd s5, 200(a0) sd s6, 208(a0) sd s7, 216(a0) sd s8, 224(a0) sd s9, 232(a0) sd s10, 240(a0) sd s11, 248(a0) sd t3, 256(a0) sd t4, 264(a0) sd t5, 272(a0) sd t6, 280(a0) csrr t0, sscratch sd t0, 112(a0) ld sp, 8(a0) ld tp, 32(a0) ld t0, 16(a0) ld t1, 0(a0) sfence.vma zero, zero //刷新TLB csrw satp, t1 // sfence.vma zero, zero // jalr t0 //JUMP TO t0(trap.c) userret: sfence.vma zero, zero csrw satp, a0 sfence.vma zero, zero li a0, TRAPFRAME # restore all but a0 from TRAPFRAME ld ra, 40(a0) ld sp, 48(a0) ld gp, 56(a0) ld tp, 64(a0) ld t0, 72(a0) ld t1, 80(a0) ld t2, 88(a0) ld s0, 96(a0) ld s1, 104(a0) ld a1, 120(a0) ld a2, 128(a0) ld a3, 136(a0) ld a4, 144(a0) ld a5, 152(a0) ld a6, 160(a0) ld a7, 168(a0) ld s2, 176(a0) ld s3, 184(a0) ld s4, 192(a0) ld s5, 200(a0) ld s6, 208(a0) ld s7, 216(a0) ld s8, 224(a0) ld s9, 232(a0) ld s10, 240(a0) ld s11, 248(a0) ld t3, 256(a0) ld t4, 264(a0) ld t5, 272(a0) ld t6, 280(a0) # restore user a0 ld a0, 112(a0) # return to user mode and user pc. # usertrapret() set up sstatus and sepc. sret2.2 保存现场uservec进入内核态的第一件事就是把子进程此刻用户态的所有寄存器如 a0 存放的文件路径指针a1 存放的打开模式等一股脑地保存到 p-trapframe陷入帧中。struct trapframe { /* 0 */ uint64 kernel_satp; // kernel page table /* 8 */ uint64 kernel_sp; // top of processs kernel stack /* 16 */ uint64 kernel_trap; // usertrap() /* 24 */ uint64 epc; // saved user program counter /* 32 */ uint64 kernel_hartid; // saved kernel tp /* 40 */ uint64 ra; /* 48 */ uint64 sp; /* 56 */ uint64 gp; /* 64 */ uint64 tp; /* 72 */ uint64 t0; /* 80 */ uint64 t1; /* 88 */ uint64 t2; /* 96 */ uint64 s0; /* 104 */ uint64 s1; /* 112 */ uint64 a0; /* 120 */ uint64 a1; /* 128 */ uint64 a2; /* 136 */ uint64 a3; /* 144 */ uint64 a4; /* 152 */ uint64 a5; /* 160 */ uint64 a6; /* 168 */ uint64 a7; /* 176 */ uint64 s2; /* 184 */ uint64 s3; /* 192 */ uint64 s4; /* 200 */ uint64 s5; /* 208 */ uint64 s6; /* 216 */ uint64 s7; /* 224 */ uint64 s8; /* 232 */ uint64 s9; /* 240 */ uint64 s10; /* 248 */ uint64 s11; /* 256 */ uint64 t3; /* 264 */ uint64 t4; /* 272 */ uint64 t5; /* 280 */ uint64 t6; };2.3 恢复现场userret当内核态子进程在硬盘上建好文件后会把文件描述符比如 3写进 trapframe-a0 中。随后执行 sret 指令退回用户态子进程醒来仿佛什么都没发生只是拿到了返回值 3。3.阶段三 打开文件表ofile当程序执行 open 时操作系统在底层构建了一条三级跳的映射链条这种设计实现了用户态与物理硬件的绝对隔离。文件描述符fd程序拿到的是一个简单的整数 fd。这个 fd 仅仅是该程序专属的 ofile 数组的下标。程序只能操作这个数字无法越权触碰内核的内存指针以此保证系统安全。动态运行时的 struct file内核通过 ofile 数组的下标找到对应的 struct file。设置这一层是因为同一个文件可以被并发访问。struct file 独立记录了本次 open 操作的专属上下文例如当前拥有的是只读还是读写权限以及具体的 off 偏移量。物理文件真身 inodestruct file 内部的 ip 指针最终指向内存中唯一的 inode。inode 包含了物理文件在底层磁盘上的真实扇区分布信息与元数据。4.阶段四 inode表、sleeplock当程序调用 write 准备将数据刷入磁盘时必须面对 CPU 高速运算与外设低速运转之间绝大的速度差。获取独占写入权内核找到目标 inode 后程序必须申请该 inode 绑定的 sleeplock。如果此时有其他任务正在写这个文件当前程序绝对不能使用 spinlock。因为 spinlock 会导致 CPU 空转在漫长的磁盘 I/O 期间空转是对算力的极大浪费。主动让出 CPU 核心拿不到 sleeplock 的程序会被内核变更为 SLEEPING 状态同时内核将目标 inode 的内存地址记录在该程序的 chan 字段中以此标记它具体在等待哪把锁。随后程序调用 swtch 触发上下文切换让出当前 CPU 核心去执行其他处于 RUNNABLE 状态的任务。详细实现见kernel/sleeplock.c硬件中断唤醒外设完成写入动作后会向 CPU 触发硬件中断。内核的中断处理例程随即介入检索所有处于 SLEEPING 状态且 chan 字段匹配该 inode 地址的程序将它们的状态回写为 RUNNABLE。调度器随后会重新安排其执行。5.阶段五 清理资源... close(fd); exit(0); } else { wait(NULL); ...子进程调用 exit()内核再次获取 p-lock自旋锁遍历 p-ofile 数组将所有打开的 struct file 的引用计数减一如果减到0就清理 inode。最后把状态改为 ZOMBIE。父进程调用 wait()由于父子并发父进程可能早就在 wait() 里等待了。发现子进程没死父进程会通过 sleep() 机制主动交出 CPU底层依赖 p-lock 保证检查状态和睡眠的原子性。当子进程变成 ZOMBIE 后唤醒父进程父进程终于读取子进程的残存状态并将其进程表项彻底抹平为 UNUSED。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2503024.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…