当前位置: 首页 > article >正文

Linux内核设计哲学:你我承载力的艺术(续)

第七部设备驱动——与不完美的世界和解7.1 你不是主人你是仆人设备驱动是内核中最“卑微”的组件。它不和用户直接打交道不参与核心决策甚至不拥有任何资源。它只是硬件的翻译官——把内核的标准请求翻译成硬件能懂的指令把硬件的状态翻译成内核能读的数据。但你猜怎么着这个“卑微”的位置恰恰最考验承载力。/** * struct device_driver - 设备驱动的基础结构 * name: 驱动名字没人记住 * bus: 挂在哪个总线上PCI、USB、I2C... * probe: 发现硬件时的初始化函数 * remove: 硬件拔出时的清理函数 * * 人生哲学 * 驱动不是主角硬件才是。 * 驱动的作用是“服务” * - 硬件需要初始化我来 * - 硬件需要读写我来 * - 硬件出错了我扛 * * 这就像团队里的“后勤”角色 * 没人注意你但没你不行。 * 不抢功、不抱怨、不邀功 * 只在需要的时候出现做完就消失。 * * 这是第11层承载力扛责而不揽权。 * 所有脏活累活自己干功劳归别人。 */ struct device_driver { const char *name; struct bus_type *bus; int (*probe)(struct device *dev); /* 硬件来了接待它 */ int (*remove)(struct device *dev); /* 硬件走了送别它 */ /* 运行时服务 */ int (*suspend)(struct device *dev); /* 系统要休眠安排好硬件 */ int (*resume)(struct device *dev); /* 系统醒了唤醒硬件 */ };驱动的第一个哲学你不是主人你是仆人。很多人追求“做主角”想站在聚光灯下。但内核告诉我们真正不可或缺的角色往往是那些默默服务的人。系统可以没有某个驱动而运行但不能没有所有驱动。卑微的位置需要最大的承载力。7.2 probe()第一次接触的艺术当一个硬件设备被发现比如你插上一个U盘内核会调用驱动的probe函数。这是驱动和硬件的“第一次见面”。第一次见面往往决定关系走向。/** * usb_probe - USB驱动的探测函数 * interface: USB接口硬件的一个功能单元 * id: 驱动支持的设备ID表匹配项 * * 返回值0表示成功负数表示失败 * * 人生哲学 * probe()是一个“相亲”函数。 * 硬件来了驱动要检查 * - 这是我该服务的设备吗ID匹配 * - 我能和它正常沟通吗初始化尝试 * - 我们长期合作需要什么资源分配内存、注册中断 * * 如果一切顺利probe返回0驱动和硬件“在一起”。 * 如果失败驱动要优雅退出释放已经申请的资源 * 不留下任何痕迹不污染系统。 * * 这就是“开始一段关系”的正确方式 * 先确认彼此合适再投入资源。 * 如果不合适好聚好散不纠缠。 * * 很多人一开始就盲目投入发现问题后又不愿退出 * 最后搞得一地鸡毛。 * probe教会我们该开始就开始该结束就结束。 */ static int usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct device *dev interface-dev; struct usb_driver *driver to_usb_driver(dev-driver); int ret; /* 1. 检查是否匹配已经由USB核心做好*/ /* 2. 尝试初始化硬件 */ ret driver-probe(interface, id); if (ret) { /* 失败清理已经申请的资源 */ dev_err(dev, probe failed: %d\n, ret); goto out; } /* 3. 分配驱动需要的数据结构 */ dev_set_drvdata(dev, kzalloc(sizeof(struct my_driver_data), GFP_KERNEL)); /* 4. 注册中断处理程序 */ ret request_irq(dev-irq, my_interrupt_handler, IRQF_SHARED, my_driver, dev); if (ret) { /* 失败释放已分配的内存 */ kfree(dev_get_drvdata(dev)); goto out; } /* 5. 一切顺利创建sysfs文件让用户空间能看到*/ device_create_file(dev, dev_attr_status); dev_info(dev, probe successful\n); return 0; out: /* 优雅退出不留下任何痕迹 */ return ret; }probe()告诉我们开始一段关系前确认彼此合适如果不合适及时止损干净退出。很多人被“沉没成本”困住已经投入了舍不得退出结果越陷越深。内核不会这样。它知道早失败比晚失败好干净退出比拖着强。7.3 中断处理你不重要但你不能出错驱动的中断处理函数是系统中最“紧急”但最“不重要”的角色。紧急因为它必须立即响应硬件。 不重要因为没人关心它做了什么只关心它别做错。/** * my_interrupt_handler - 驱动的中断处理函数 * irq: 中断号 * dev_id: 设备IDprobe时注册的 * * 返回值IRQ_HANDLED我处理了或IRQ_NONE不是我产生的 * * 人生哲学 * 中断处理函数是“消防员”。 * 警报响了中断来了必须立即出动。 * 但没人感谢消防员大家只关心火灭了没有。 * * 驱动的中断处理函数有一个铁律快速返回。 * 不能在中断上下文里做任何耗时操作 * - 不能睡眠内存分配、互斥锁 * - 不能做复杂计算 * - 不能等待任何事情 * * 为什么因为系统在等你。 * 你在中断处理函数里多待1微秒系统就延迟1微秒响应其他中断。 * 你待太久系统就丢中断、性能崩溃。 * * 这就是“紧急但不重要”的任务处理原则 * 快速确认、快速处理、快速退出。 * 不做多余的、不邀功、不拖延。 * * 很多人把“紧急”当成“重要” * 结果永远在救火永远没时间做真正重要的事。 * 中断处理函数告诉我们紧急的事要快做但别把它当重要的事。 */ static irqreturn_t my_interrupt_handler(int irq, void *dev_id) { struct device *dev dev_id; u32 status; /* 1. 读取硬件状态寄存器最快的事*/ status readl(dev-regs STATUS_REG); /* 2. 判断是不是我这个设备产生的中断 */ if (!(status MY_DEVICE_IRQ_BIT)) return IRQ_NONE; /* 不是我让别人处理 */ /* 3. 清除中断标志防止重复触发*/ writel(status | MY_DEVICE_IRQ_BIT, dev-regs STATUS_REG); /* 4. 如果是“需要处理数据”的中断调度下半部 */ if (status DATA_READY) { /* 快速拷贝数据如果硬件有FIFO*/ memcpy_fromio(dev-buffer, dev-regs DATA_REG, 64); /* 调度工作队列下半部做真正的处理 */ schedule_work(dev-work); } /* 5. 快速返回 */ return IRQ_HANDLED; } ​ /* 下半部在进程上下文做真正的工作 */ static void my_work_handler(struct work_struct *work) { struct device *dev container_of(work, struct device, work); /* 这里可以做任何事分配内存、获取锁、睡眠、通知用户... */ /* 因为这是在进程上下文不是中断上下文 */ /* 真正处理数据 */ process_data(dev-buffer, 64); /* 唤醒等待的用户进程 */ wake_up_interruptible(dev-wait_queue); }中断处理函数告诉我们紧急的事要快做重要的事要慢做别把两者混淆。很多人一辈子都在做紧急的事从来没时间做重要的事。结果就是永远在救火永远在忙碌但永远没有进步。7.4 电源管理在休眠和唤醒之间保持优雅现代设备需要省电所以驱动必须支持电源管理休眠suspend和唤醒resume。/** * my_suspend - 驱动休眠函数 * dev: 设备 * * 返回值0成功负数失败 * * 人生哲学 * 系统要休眠了驱动必须配合。 * 但驱动不能简单地说“我睡了” * 它要做很多工作 * - 保存硬件状态寄存器值 * - 停止硬件活动DMA、中断 * - 释放部分资源让给其他设备 * * 最重要的是无论做什么最终必须能让硬件进入低功耗状态。 * 如果某个驱动拒绝休眠整个系统都不能睡。 * * 这就是“配合大局”的智慧 * 你一个人的不配合会让整个团队的努力白费。 * 有时候为了更大的目标你需要“委屈”自己。 * * 但“委屈”不是“牺牲”。 * suspend函数会保存状态resume函数会恢复状态。 * 你只是暂时让出资源不是永久放弃。 */ static int my_suspend(struct device *dev) { struct my_driver_data *data dev_get_drvdata(dev); /* 1. 保存硬件状态 */ data-saved_regs[0] readl(dev-regs REG_CTRL); data-saved_regs[1] readl(dev-regs REG_CONFIG); /* 2. 停止DMA传输 */ writel(0, dev-regs DMA_CTRL); /* 3. 禁用中断系统休眠期间不需要中断*/ disable_irq(dev-irq); /* 4. 通知硬件进入休眠 */ writel(SLEEP_MODE, dev-regs POWER_CTRL); return 0; } ​ /** * my_resume - 驱动唤醒函数 * dev: 设备 * * 人生哲学 * 系统醒了驱动必须恢复。 * 恢复比休眠更复杂 * - 重新初始化硬件可能状态丢失 * - 恢复保存的寄存器 * - 重新启动DMA * - 重新申请中断 * * 但最重要的是恢复后系统应该和休眠前一样 * 用户感觉不到曾经休眠过。 * * 这就是“优雅”的体现 * 经历风雨休眠归来仍是少年恢复原样。 * 不在脸上留下沧桑不把负面情绪带给别人。 * * 很多人经历挫折后变得愤世嫉俗、怨天尤人。 * 驱动告诉我们睡一觉醒来就当什么都没发生。 * 保持状态继续工作。 */ static int my_resume(struct device *dev) { struct my_driver_data *data dev_get_drvdata(dev); /* 1. 重新初始化硬件保险起见*/ writel(0, dev-regs REG_CTRL); mdelay(10); /* 等待硬件稳定 */ /* 2. 恢复保存的状态 */ writel(data-saved_regs[0], dev-regs REG_CTRL); writel(data-saved_regs[1], dev-regs REG_CONFIG); /* 3. 重新启动DMA */ writel(DMA_ENABLE, dev-regs DMA_CTRL); /* 4. 重新申请中断 */ enable_irq(dev-irq); /* 5. 通知硬件退出休眠 */ writel(ACTIVE_MODE, dev-regs POWER_CTRL); return 0; }电源管理告诉我们暂时的退让是为了更长久的合作。不是所有事情都要一直在线。有时候你需要“休眠”——暂停活动、保存状态、释放资源。但重要的是你能在需要的时候“唤醒”——恢复状态、重新工作、回到巅峰。这就是“能屈能伸”的智慧。7.5 驱动的错误处理你能承受多少失败驱动面对的是最不可靠的硬件。硬件可能随时出错总线超时、寄存器读失败、DMA传输错误。/** * my_device_io - 安全的硬件IO操作 * dev: 设备 * reg: 寄存器地址 * val: 要写入的值如果是写操作 * write: 1表示写0表示读 * * 返回值读操作返回读到的值写操作返回0负数表示错误 * * 人生哲学 * 硬件IO可能失败驱动必须处理所有失败情况。 * 不能因为读寄存器失败就panic也不能假装成功。 * * 驱动的错误处理有三个原则 * 1. 承认失败返回错误码不隐瞒 * 2. 记录失败打印日志供调试 * 3. 尝试恢复重试、复位硬件 * * 如果恢复不了通知上层“我尽力了帮不了你。” * 但不崩溃、不panic、不污染系统。 * * 这就是“能承受失败”的承载力 * 失败是常态不是意外。 * 准备好处理失败而不是祈祷不要失败。 * 每次失败都是一次学习每次恢复都是一次成长。 */ static int my_device_io(struct device *dev, u32 reg, u32 *val, int write) { int retry 3; /* 最多重试3次 */ while (retry--) { if (write) { /* 写操作 */ writel(*val, dev-regs reg); /* 验证写入是否成功有些硬件支持读回验证*/ if (readl(dev-regs reg) *val) return 0; /* 成功 */ } else { /* 读操作 */ *val readl(dev-regs reg); /* 检查读到的值是否有效 */ if (*val ! 0xDEADBEEF) /* 0xDEADBEEF是硬件定义的“无效”值 */ return 0; /* 成功 */ } /* 失败等待一会儿再重试 */ udelay(10); } /* 重试3次都失败 */ dev_err(dev, IO failed for reg 0x%x after 3 retries\n, reg); /* 尝试复位硬件 */ my_device_reset(dev); return -EIO; /* 返回错误码让上层决定怎么办 */ }驱动的错误处理告诉我们失败不是终点放弃才是。硬件会失败但驱动不会因为一次失败就崩溃。它会重试、会记录、会恢复。只有恢复不了才放弃但放弃前已经尽力。这就像人生你会遇到失败但不能因为失败就放弃。你可以失败很多次但只需要成功一次。7.6 驱动的人生启示设备驱动教给我们关于承载力的几个层次第一层甘当配角不是主角但不可或缺不抢功、不抱怨、不邀功在需要的时候出现做完就消失第二层干净的开始和结束开始时确认合适再投入不合适时及时止损退出时不留痕迹第三层快慢分明紧急的事快做中断上下文重要的事慢做进程上下文不混淆紧急和重要第四层能屈能伸需要休眠时就休眠需要唤醒时就恢复暂时的退让为了更长远的合作第五层承受失败失败是常态不是意外重试、记录、恢复尽力之后才放弃这就是一个服务者的样子卑微但有价值简单但可靠能承受失败但从不主动放弃。第八部系统调用——通往内核的大门8.1 门卫的艺术系统调用是用户程序进入内核的唯一通道。就像一个公司的前台每天无数人进出每个人都说“我有事要找里面的人”。前台不能拒绝所有人那公司就封闭了也不能放所有人进去那公司就乱了。/** * syscall_handler - 系统调用入口 * nr: 系统调用号要做什么 * arg1, arg2, arg3: 参数怎么做 * * 返回值成功返回结果失败返回错误码 * * 人生哲学 * 系统调用是“门卫”的角色。 * 用户程序访客想进入内核内部区域做某些事 * 门卫的工作是 * 1. 检查请求是否合法参数有效吗权限够吗 * 2. 检查资源是否足够内存够吗锁能拿到吗 * 3. 执行请求如果真的合法 * 4. 返回结果成功或失败 * * 门卫不做的是 * - 不判断请求的“道德性”用户想做什么是用户的事 * - 不代替用户做决定用户说做什么就做什么只要合法 * - 不因为用户“态度不好”就拒绝不情绪化 * * 这就是第1层承载力接纳人性之私。 * 用户可能自私、可能愚蠢、可能恶意 * 但系统调用不生气、不指责、不教育用户。 * 只检查合法性然后执行或拒绝。 * * 很多人试图“教育”别人试图改变别人的“三观”。 * 系统调用告诉我们你改变不了别人只能检查请求是否合法。 * 合法就做不合法就拒绝不带情绪。 */ SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) { struct file *file; int ret; /* 1. 检查参数合法性 */ if (!buf || !count) return -EINVAL; /* 无效参数拒绝 */ /* 2. 根据fd找到文件对象 */ file fget(fd); if (!file) return -EBADF; /* 文件描述符无效拒绝 */ /* 3. 检查权限用户有读这个文件的权限吗*/ if (!(file-f_mode FMODE_READ)) { ret -EBADF; goto out; } /* 4. 检查是否在用户空间不能读内核内存*/ if (!access_ok(VERIFY_WRITE, buf, count)) { ret -EFAULT; goto out; } /* 5. 执行真正的读操作 */ ret vfs_read(file, buf, count, file-f_pos); out: fput(file); return ret; }系统调用的第一个哲学接纳但不纵容检查但不情绪化。你的请求只要合法我就执行不合法我就拒绝。我不评判你的动机不改变你的想法不教育你的三观。这就是“边界感”的极致。8.2 copy_from_user信任但验证用户程序传递的数据在用户空间内核不能直接访问可能被篡改、可能分页错误。所以内核提供了copy_from_user函数从用户空间拷贝数据到内核空间。/** * copy_from_user - 从用户空间安全地拷贝数据 * to: 内核空间的目标地址 * from: 用户空间的源地址 * n: 要拷贝的字节数 * * 返回值0成功非0表示失败的字节数 * * 人生哲学 * copy_from_user是“信任但验证”的典范。 * 内核信任用户程序说“我的数据在地址X” * 但信任不代表不验证。 * * 它会检查 * - 地址X是否在用户空间不能是内核地址 * - 地址X到Xn是否都可访问 * - 用户程序是否有权限读这个地址 * * 如果任何检查失败拷贝失败返回错误。 * 不会因为“信任”而跳过验证。 * * 这就是“信任但验证”的智慧 * 你可以信任别人但不能依赖信任。 * 信任是态度验证是行动。 * 信任别人不代表放弃自己的判断。 * * 很多人要么完全不信任活得太累 * 要么盲目信任活得太傻。 * 系统调用告诉我们信任但要验证。 */ unsigned long copy_from_user(void *to, const void __user *from, unsigned long n) { /* 1. 检查地址范围是否在用户空间 */ if (!access_ok(VERIFY_READ, from, n)) return n; /* 全部失败 */ /* 2. 检查页面是否都在内存中不会缺页*/ pagefault_disable(); /* 暂时关闭缺页处理 */ /* 3. 拷贝数据可能触发缺页但我们关闭了处理所以会直接失败*/ if (__copy_from_user_inatomic(to, from, n)) { pagefault_enable(); /* 4. 如果失败了用慢速路径允许缺页重试 */ pagefault_enable(); return __copy_from_user(to, from, n); } pagefault_enable(); return 0; /* 成功 */ }copy_from_user告诉我们信任是态度验证是行动。你可以信任别人但不能因为信任就放弃验证。信任可以让你更高效但验证让你更安全。8.3 错误码诚实的反馈系统调用返回一个整数成功返回非负数通常是0或字节数失败返回负数错误码。/** * 常见的错误码 * * 人生哲学 * 错误码是“诚实的反馈”。 * 系统调用不会说“我不舒服”不会说“你等一下” * 它会告诉你确切的问题 * - EINVAL: 参数无效你给错了 * - EPERM: 权限不足你不够格 * - ENOMEM: 内存不足我没办法 * - EAGAIN: 资源暂时不可用再试一次可能成功 * - EIO: IO错误硬件有问题 * * 诚实的反馈有几个好处 * 1. 调用者知道问题在哪可以修正 * 2. 调用者知道怎么重试EAGAIN就重试EINVAL就不重试 * 3. 调用者不会误以为成功 * * 很多人害怕说“不”害怕给负面反馈。 * 结果要么沉默用户不知道为什么失败 * 要么撒谎说成功但实际失败 * 要么含糊“出错了”但不说哪里错。 * * 系统调用告诉我们诚实的负面反馈比虚假的正面反馈更有价值。 * 说“你错了”不是羞辱是帮助。 * 说“我不行”不是示弱是诚实。 */ #define EPERM 1 /* 权限不足 */ #define ENOENT 2 /* 文件不存在 */ #define EINTR 4 /* 被信号打断 */ #define EIO 5 /* IO错误 */ #define ENOMEM 12 /* 内存不足 */ #define EACCES 13 /* 权限拒绝 */ #define EINVAL 22 /* 参数无效 */ #define ENOSPC 28 /* 磁盘空间不足 */ #define EAGAIN 11 /* 资源暂时不可用请重试 */错误码告诉我们诚实的反馈比虚假的安慰更有价值。很多人害怕说“不”害怕给负面反馈结果问题越积越多。系统调用不会这样。它会清楚地告诉你哪里错了为什么错怎么办。这就是“建设性批评”的极致。8.4 阻塞与非阻塞等待的智慧有些系统调用会“阻塞”如果资源暂时不可用就让调用者睡眠等资源可用时再唤醒。有些系统调用是“非阻塞”的资源不可用时立即返回EAGAIN。/** * do_blocking_read - 阻塞式读 * file: 文件对象 * buf: 用户缓冲区 * count: 要读的字节数 * * 人生哲学 * 阻塞式读是“耐心等待”的体现。 * 数据还没准备好没关系我等。 * 等多久不知道但我会一直等。 * * 内核会把我放入等待队列然后调度其他进程。 * 不浪费CPU不抱怨等待只是安静地等。 * * 这就是“有耐心的等待” * 知道等待是必要的愿意为结果付出时间。 * 不焦虑、不折腾、不放弃。 * * 很多人等不了总要“做点什么” * 结果在等待中消耗了大量精力。 * 阻塞读告诉我们有时候最好的行动就是等待。 */ static ssize_t do_blocking_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { struct my_device *dev file-private_data; /* 如果数据还没准备好 */ while (!data_ready(dev)) { /* 加入等待队列让自己睡眠 */ prepare_to_wait(dev-wait_queue, wait, TASK_INTERRUPTIBLE); /* 让出CPU等被唤醒 */ schedule(); /* 被唤醒了检查是被信号打断还是数据来了 */ if (signal_pending(current)) return -ERESTARTSYS; /* 被信号打断 */ } /* 数据准备好了读取它 */ return do_read(file, buf, count, pos); } ​ /** * do_nonblocking_read - 非阻塞式读 * file: 文件对象 * * 人生哲学 * 非阻塞式读是“快速反馈”的体现。 * 数据准备好了读走。 * 没准备好返回EAGAIN告诉调用者“现在不行等会儿再试”。 * * 调用者收到EAGAIN后可以去做别的事过会儿再来。 * 不浪费CPU在等待上也不浪费CPU在睡眠唤醒上。 * * 这就是“高效但不强迫” * 不要求别人等待也不强迫自己等待。 * 能做就做不能做就说“现在不行”。 * * 很多人在“立即放弃”和“无限等待”之间摇摆 * 非阻塞读给出了第三条路现在不行但可能以后行。 * 不放弃希望但不浪费时间。 */ static ssize_t do_nonblocking_read(struct file *file) { struct my_device *dev file-private_data; if (!data_ready(dev)) return -EAGAIN; /* 现在不行以后再来 */ return do_read(file, buf, count, pos); }阻塞与非阻塞告诉我们知道什么时候该等什么时候该走。不是所有等待都值得也不是所有放弃都明智。要根据情况选择重要的事值得等紧急的事不等。8.5 系统调用的人生启示系统调用教给我们关于承载力的几个层次第一层接纳但不纵容接受任何合法请求拒绝任何非法请求不带情绪不教育用户第二层信任但验证信任用户的意图验证用户的输入信任是态度验证是行动第三层诚实反馈成功说成功失败说失败清楚告诉原因帮助调用者改进第四层明智等待重要的事值得等阻塞紧急的事不等非阻塞知道什么时候该等什么时候该走这就是一个门卫的样子有边界、有验证、有反馈、有判断。第九部虚拟化——承载整个世界的容器9.1 一个容器能装下多少个世界虚拟化是内核中最“宏大”的组件。它让一台物理机变成多台虚拟机每台虚拟机运行自己的操作系统仿佛自己独占整个硬件。/** * struct kvm - 内核虚拟机KVM的核心结构 * vcpus: 虚拟CPU数组 * mm: 虚拟机的内存空间 * arch: 架构相关的数据 * * 人生哲学 * KVM是一个“容器”能装下多个“世界”。 * 每个世界虚拟机以为自己独占硬件 * 但实际上它们共享同一台物理机。 * * KVM的工作是 * - 让每个虚拟机觉得“我是唯一的” * - 在虚拟机之间公平分配硬件资源 * - 保护虚拟机之间的隔离不能让一个虚拟机看到另一个的内存 * * 这就是“容器”的哲学 * 能装下不同世界但保持每个世界的独立性。 * 不强迫一致不压制不同。 * 每个世界有自己的规则只要不互相干扰。 * * 这是第16层承载力能装得下不同意见。 * 层级越高越会遇到三观不同、立场不同、想法不同的人。 * 不强迫一致不压制不同兼容并蓄。 */ struct kvm { struct kvm_vcpu *vcpus[KVM_MAX_VCPUS]; /* 虚拟CPU */ struct mm_struct *mm; /* 虚拟机的地址空间 */ struct kvm_memslots *memslots; /* 虚拟内存区域 */ /* 统计信息 */ u64 stat[KVM_NR_STATS]; };虚拟化的第一个哲学一个容器能装下多个世界每个世界有自己的规则。不是所有人都要按同一套规则生活。虚拟化允许Windows、Linux、BSD在同一台机器上运行每个有自己的内核、自己的文件系统、自己的用户。这就是真正的包容尊重不同但不让不同互相伤害。9.2 VM-Exit当虚拟世界遇到现实虚拟机中的Guest OS以为自己在直接操作硬件但实际上是KVM在模拟硬件。当Guest OS试图执行特权指令比如关闭中断、读写CR3寄存器CPU会“陷入”到KVM这就是VM-Exit。/** * kvm_vcpu_run - 运行虚拟CPU * vcpu: 虚拟CPU * * 返回值VM-Exit的原因 * * 人生哲学 * VM-Exit是“理想遇到现实”的时刻。 * Guest OS觉得“我想做什么就能做什么” * 但当它尝试做“特权操作”时KVM介入 * “等等这个操作不能直接做我来帮你模拟。” * * Guest OS可能觉得被打扰了 * “为什么要打断我我在认真工作” * 但KVM知道如果不打断虚拟机之间就没法隔离。 * * 这就是“必要的打断” * 有时候为了更大的目标隔离、安全、公平 * 必须打断别人的“美梦”。 * 被打断的人可能不高兴但这是必要的。 * * 很多管理者不敢打断下属怕得罪人。 * 结果下属以为“我想怎样就怎样” * 团队失去秩序最终所有人受害。 * KVM告诉我们必要的打断不是坏事是责任。 */ int kvm_vcpu_run(struct kvm_vcpu *vcpu) { int r; /* 1. 检查是否需要注入中断 */ if (kvm_check_request(KVM_REQ_EVENT, vcpu)) kvm_vcpu_inject_irq(vcpu); /* 2. 进入Guest模式运行Guest OS*/ r vcpu_enter_guest(vcpu); /* 3. 从Guest模式退出VM-Exit发生了*/ /* 原因可能是Guest执行了IO、访问了MMIO、缺页、HLT... */ /* 4. 处理VM-Exit的原因 */ switch (vcpu-exit_reason) { case KVM_EXIT_IO: /* Guest试图读写IO端口模拟它 */ kvm_emulate_io(vcpu); break; case KVM_EXIT_MMIO: /* Guest试图访问内存映射IO模拟它 */ kvm_emulate_mmio(vcpu); break; case KVM_EXIT_HLT: /* Guest执行了HLT停机让它睡一会儿 */ kvm_vcpu_halt(vcpu); break; case KVM_EXIT_EPT_VIOLATION: /* Guest缺页但KVM负责处理 */ kvm_handle_page_fault(vcpu); break; } /* 5. 处理完重新进入Guest */ goto again; }VM-Exit告诉我们必要的打断不是坏事是责任。很多人不敢打断别人怕得罪人结果问题越积越多。KVM告诉我们为了更大的目标必须打断。但打断不是粗暴地中断而是打断-处理-恢复。被打断的人甚至感觉不到被打断过。这就是“温柔地打断”的艺术。9.3 内存虚拟化影子页表的孤独虚拟化中最复杂的是内存虚拟化。Guest OS有自己的物理地址GPA但真正的物理地址HPA由KVM管理。KVM需要维护GPA到HPA的映射。/** * struct kvm_mmu - KVM的内存管理单元 * root_hpa: 影子页表的物理地址 * translate: GPA到HPA的转换函数 * * 人生哲学 * 内存虚拟化是“翻译官”的角色。 * Guest OS说“我要访问地址0x1234” * 但0x1234是GPA不是真正的物理地址。 * KVM要翻译成真正的物理地址HPA。 * * 这个翻译对Guest OS是透明的 * Guest OS不知道自己在被翻译以为自己在直接访问。 * * 这就是“无声的服务” * 在别人看不见的地方做最复杂的事。 * 不求感谢不求理解只求结果。 * * 很多人做事要求“被看见” * 不被看见就觉得“白做了”。 * 影子页表告诉我们真正有价值的工作往往是不被看见的。 * 你不被看见是因为你做得太顺了别人感觉不到你的存在。 * 这其实是最大的成功。 */ struct kvm_mmu { gpa_t (*translate)(struct kvm_vcpu *vcpu, gpa_t gpa); hpa_t root_hpa; /* 影子页表 */ /* 统计被翻译了多少次 */ u64 translations; };影子页表告诉我们真正有价值的工作往往是不被看见的。很多人追求“被看见”追求存在感。但真正的高手是让事情变得如此顺畅以至于别人感觉不到他的存在。这就是“无为而治”的极致。9.4 CPU虚拟化公平但不平均KVM需要在多个虚拟机之间公平分配CPU时间。/** * kvm_schedule - KVM的CPU调度 * vcpu: 虚拟CPU * * 人生哲学 * CPU虚拟化是“公平但不平均”的体现。 * 每个虚拟机以为自己独占所有CPU * 但实际上KVM在后台调度 * - 这个虚拟机运行100ms * - 切换到下一个虚拟机 * - 再运行100ms * * 调度是公平的每个虚拟机都有机会。 * 但不是平均的重要的虚拟机可以分到更多时间。 * * 这就是“公平”的真谛 * 公平不是每人分到一样多 * 公平是每人分到应得的那份。 * * 很多管理者误解了公平 * 以为公平就是“一模一样” * 结果奖励懒汉、惩罚勤快人。 * KVM告诉我们公平是按需分配、按重要性分配。 * 重要的虚拟机实时系统优先 * 不重要的虚拟机测试环境让路。 */ void kvm_schedule(struct kvm_vcpu *vcpu) { struct kvm *kvm vcpu-kvm; /* 1. 当前虚拟机的时间片用完了 */ if (vcpu-time_slice 0) { /* 2. 选择下一个要运行的虚拟机 */ vcpu select_next_vcpu(kvm); /* 3. 给新的虚拟机分配时间片 */ vcpu-time_slice get_time_slice(vcpu-priority); } /* 4. 运行选中的虚拟机 */ kvm_vcpu_run(vcpu); }CPU虚拟化告诉我们公平是按需分配不是平均分配。不同的人、不同的任务需要不同的资源。好的管理者知道这一点会根据实际情况分配资源而不是机械地“一人一份”。9.5 虚拟化的人生启示虚拟化教给我们关于承载力的几个层次第一层容器的胸怀能装下多个世界每个世界有自己的规则不强迫一致不压制不同第二层必要的打断为了更大目标必须打断打断-处理-恢复让被打断者无感温柔的打断不是粗暴的中断第三层无声的服务在最复杂的地方做最不被看见的工作不求感谢不求理解太顺了所以不被看见这是最大的成功第四层公平但不平均每人得到应得的不是相同的按需分配按重要性分配公平不是一刀切这就是一个容器的样子能包容、能打断、能服务、能公平。第十部内核的终极哲学——承载力我们走了很远。从调度器到内存管理从中断处理到文件系统从网络协议栈到同步机制从设备驱动到系统调用最后到虚拟化。每一部分都在告诉我们同一件事承载力是内核的核心竞争力。10.1 承载力的九个层次回顾整个内核我们可以看到承载力的九个层次第0层接纳一切调度器不拒绝任何请求不问来路每个人有位置不带情绪处理第1层在混乱中建立秩序调度器内存管理不等待完美信息在模糊中决策做团队的定心丸第2层承受孤独OOM Killer kswapd做最艰难的决定无人理解无人分担默默守护不求感谢第3层压力减震内存管理中断处理提前预警不等到枯竭后台处理不影响前台内部消化不传递焦虑第4层快速响应从容处理中断处理紧急的事快做重要的事慢做不混淆紧急和重要第5层扛责不揽功文件系统驱动好事归大家坏事自己扛利益让一步风险冲在前卑微但不可或缺第6层拥抱不可控网络协议栈接受一切可能为每种可能准备方案不抱怨环境只调整自己第7层不转移压力同步机制压力自己消化安静地等待不发泄、不迁怒、不抱怨第8层容器的胸怀虚拟化能装下不同世界尊重不同但不让不同互相伤害能包容、能打断、能服务、能公平10.2 内核给人生的一堂课Linux内核能运行在从手表到超级计算机的所有设备上不是因为它的代码多么精妙而是因为它学会了承载。承载硬件的差异、承载驱动的错误、承载并发的冲突、承载攻击者的恶意。这和你的人生一模一样。你的承载力决定你的人生高度。能扛住多少混乱就能掌控多少秩序。 能承受多少孤独就能匹配多少荣耀。 能接纳多少意外就能拥有多少从容。 能消化多少压力就能承担多少责任。内核的每一行代码都在告诉你不必羡慕别人的高度先练出与之匹配的承载力。 承载力到了所有机会、地位、财富都会自然向你靠近。这就是Linux内核设计哲学的核心。这就是能承载多少就能走多远的最好诠释。

相关文章:

Linux内核设计哲学:你我承载力的艺术(续)

第七部:设备驱动——与不完美的世界和解7.1 你不是主人,你是仆人设备驱动是内核中最“卑微”的组件。它不和用户直接打交道,不参与核心决策,甚至不拥有任何资源。它只是硬件的翻译官——把内核的标准请求翻译成硬件能懂的指令&…...

Thorium浏览器:为什么这个基于Chromium的优化版本能解决你90%的性能痛点?

Thorium浏览器:为什么这个基于Chromium的优化版本能解决你90%的性能痛点? 【免费下载链接】thorium Chromium fork named after radioactive element No. 90. Source code and Linux releases. Windows/MacOS/ARM builds served in different repos, lin…...

5个维度教你掌握游戏自动化与效率工具开发

5个维度教你掌握游戏自动化与效率工具开发 【免费下载链接】JX3Toy 一个自动化测试DPS的小工具 项目地址: https://gitcode.com/GitHub_Trending/jx/JX3Toy 在游戏开发与玩家体验优化领域,游戏脚本开发正成为提升效率的关键技术。本文将系统介绍一款开源项目…...

水箱水位监测控制电路 Multisim 仿真探索

Multisim仿真文件 水箱水位监测控制电路报告 包含:说明书,Multisim10电路源文件,仿真电路等 仿真效果: 1.在水箱内的不同高度安装3根金属棒,以感知水位变化情况, 液位分1,2,3档&…...

山西口碑好的实体店获客公司哪家可靠

在山西,实体店主们都在为如何有效获客而烦恼。随着市场竞争的加剧,选择一家可靠的获客公司至关重要。今天,我们就来探讨一下山西口碑好的实体店获客公司,重点介绍中谷云(厦门)大数据科技有限公司&#xff0…...

覆盖更远、组网更稳:基于 EFR32BG21 的智能家居与物联网 BLE Mesh 无线模块方案

智能家居与物联网设备越来越多,但真正决定体验上限的往往不是“有没有连上网”,而是信号能不能到、掉线后能不能自愈、多设备同时在线是否还稳定。单靠点对点蓝牙,很容易在隔墙、远距离、多节点场景里碰到瓶颈;而把低功耗蓝牙与 M…...

5分钟掌握ImStudio:免费高效的实时GUI布局设计终极方案

5分钟掌握ImStudio:免费高效的实时GUI布局设计终极方案 【免费下载链接】ImStudio Real-time GUI layout designer for Dear ImGui 项目地址: https://gitcode.com/gh_mirrors/im/ImStudio 你是否曾经为调试用户界面而反复编译代码?是否厌倦了在代…...

不只是“生成一张图“:2026年6款真正改变设计工作流的AI界面工具深度测评

AI界面生成工具正在经历从"生成单张界面"到"生成完整产品体验"的代际跃迁。本文深度拆解 UXbot、Figma Make、Google Stitch、Flowstep、Visily AI 和 Moonchild 共6款2026年代表性工具——从设计稿生成到原生代码输出,覆盖完整的产品交付能力谱…...

如何快速修复ROG游戏本色彩配置文件丢失问题:G-Helper终极指南

如何快速修复ROG游戏本色彩配置文件丢失问题:G-Helper终极指南 【免费下载链接】g-helper Lightweight, open-source control tool for ASUS laptops and ROG Ally. Manage performance modes, fans, GPU, battery, and RGB lighting across Zephyrus, Flow, TUF, S…...

AI生成教材新玩法,低查重让你的教材更有竞争力!

教材的格式问题常常让编写者感到困惑。比如,标题应该选择多大字号?参考文献是依据GB/T7714还是按照某些出版机构的标准?习题的排版又应选择单栏还是双栏?各种不同的要求让人感到眼花缭乱,而手动调整不仅耗时费力&#…...

做对这三步,拥有一个聪明的智能问数与分析Agent

这两年,智能问数与分析,几乎已经成了 ToB Agent 里最容易出圈的“爆款场景”。原因不难理解。相比很多还停留在演示层、流程层的 AI 应用,智能问数更接近企业管理者最直接的需求:我有问题,系统能不能立刻给我答案&…...

本科论文知网AI率高的原因和解决方法全在这里

知网AIGC检测出来AI率高,很多同学第一反应是"我没有全程用AI写啊,为什么这么高?"这个问题确实需要好好解释一下——知网检测到的AI率高,未必是因为你完全靠AI写的。 知网AIGC检测是怎么工作的 知网的AIGC检测系统会分…...

本科论文降AI率花多少钱合适?各档工具对比

每年毕业季都有同学问:降AI率要花多少钱?买个工具值不值?这篇把费用账算清楚,让你心里有数。 先算清楚你实际需要处理多少字 很多人有个误区,以为论文AI率30%就要处理30%的字数。实际上不是的。 实际需要处理的字数…...

**发散创新:基于Python与OpenCV的智能交通流量实时监测系统设计

发散创新:基于Python与OpenCV的智能交通流量实时监测系统设计与实现 在智慧城市建设不断深化的背景下,智能交通系统(ITS) 正成为城市治理现代化的重要突破口。传统的交通信号控制多依赖固定时长或人工经验判断,难以应对…...

数仓实习实战|医疗报表电话指标缺失,完整上游排查思路

今天碰到一个问题:患者档案里明明有联系电话,但是最终报表展示的时候,这个字段就是空的。跟着师哥一步步排查下来,思路清晰了很多,也把完整的排查逻辑整理了一下,以后遇到类似问题可以直接参考一、问题场景…...

开篇:高并发下MySQL主从延迟的挑战与诊断全景图

开篇:高并发下MySQL主从延迟的挑战与诊断全景图 凌晨三点,监控告警炸了。主库QPS冲到两万八,从库延迟曲线像坐了火箭——三分钟前还是秒级延迟,现在稳定在三百秒高位。业务侧已经出现数据不一致的客诉,运营群开始@全体成员。你揉着发红的眼睛,连上从库执行SHOW SLAVE STA…...

[WP]vulhub-dc1 flag全收集,靶机通关writeup超级详细,附带知识点讲解

2026/3/28 前言/提示: 本次记录的背景是作者本人积累2年多的基础知识,但是从来没有打过这种集成环境的靶机,所以仅供个人参考,尽管真的很想分享一些自己的思路也许能帮助读者,但是本次记录也大概率会出现手法惊奇&am…...

RHCSA 认证必备:目录文件的管理

目录 一、创建目录 (1)格式 (2)参数 (3)示例 二、查看目录文件 1、查看目录文件 2、统计命令 3、编辑与删除 a.编辑目录文件 b.删除目录文件 一、创建目录 (1)格式 mkdi…...

2026届毕业生推荐的五大AI科研神器实测分析

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 在运用人工智能辅助写作这个行为当中,所生成的内容常常带有十分明显的机械感。所…...

2025届必备的六大AI科研神器实际效果

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 专门用于降低重复率的网站,在学术范畴里,是作为辅助学术写作的工具而…...

风冷机房温湿度数据采集解决方案

对部分气候干旱的地区来说,使用风冷技术对数据机房进行冷却是比较合适的方案,但高能耗问题仍需要避免与管控,要求环境温湿度与散热效率进行合理分配。对此,物通博联提供温湿度数据采集到机房管理平台的解决方案。 需求如下 温湿度…...

2025届最火的十大AI写作工具实测分析

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 在学术写作这个特定的场景之内,精确地挑选恰当的AI论文平台,能够极其…...

2025届最火的五大降AI率平台横评

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 一款基于自然语言处理技术的智能工具,是AI写作软件,它能够辅助用户自…...

计算机毕业设计:Python汽车销量智能可视化与预测系统 Flask框架 可视化 机器学习 AI 大模型 大数据(建议收藏)✅

博主介绍:✌全网粉丝50W,前互联网大厂软件研发、集结硕博英豪成立软件开发工作室,专注于计算机相关专业项目实战6年之久,累计开发项目作品上万套。凭借丰富的经验与专业实力,已帮助成千上万的学生顺利毕业,…...

计算机毕业设计:Python地铁数据可视化分析系统 Flask框架 数据分析 可视化 高德地图 数据挖掘 机器学习 爬虫(建议收藏)✅

博主介绍:✌全网粉丝50W,前互联网大厂软件研发、集结硕博英豪成立软件开发工作室,专注于计算机相关专业项目实战6年之久,累计开发项目作品上万套。凭借丰富的经验与专业实力,已帮助成千上万的学生顺利毕业,…...

欧洲发布Euro-Office引发OnlyOffice强烈抗议

欧洲企业Ionos和Nextcloud联合推出了Euro-Office,这是基于OnlyOffice云办公套件的分支版本,专为对数字主权有顾虑的组织而设计,此举引发了原开发商的愤怒回应。几天前,以德国自托管云服务商Nextcloud为首的"欧洲企业和社区组…...

OpenClaw 从翻车到迎来上百项更新:MiniMax、腾讯、阿里、有道 8 位专家拆解OpenClaw本土化实战解法

责编 | 梦依丹出品 | CSDN(ID:CSDNnews)3 月 24 日,行业顶流 OpenClaw 在迎来号称自诞生以来的最大更新之后,却始料未及地上演了一段“装虾五分钟,修 Bug 两小时”的升级翻车大事故。由于强行将插件生态迁移…...

C++ 模板元编程工程应用

C模板元编程:工程实践中的编译期魔法 在现代C开发中,模板元编程(TMP)通过编译期计算将复杂逻辑转移到代码生成阶段,显著提升了运行时效率与代码可维护性。从类型安全的容器到高性能数学库,TMP已成为工程领…...

探索ST-DBSCAN:2025年时空数据聚类的实战应用与算法思想

探索ST-DBSCAN:2025年时空数据聚类的实战应用与算法思想 【免费下载链接】st_dbscan ST-DBSCAN: Simple and effective tool for spatial-temporal clustering 项目地址: https://gitcode.com/gh_mirrors/st/st_dbscan 当您面对海量的时空数据时,…...

FF14副本动画跳过插件:5分钟终极配置指南,告别冗长等待

FF14副本动画跳过插件:5分钟终极配置指南,告别冗长等待 【免费下载链接】FFXIV_ACT_CutsceneSkip 项目地址: https://gitcode.com/gh_mirrors/ff/FFXIV_ACT_CutsceneSkip FF14副本动画跳过插件是专为《最终幻想14》国服玩家设计的智能工具&#…...