我对OS的理解

被喷的对象集

这篇文章或许看起来戾气太重，所以我不推荐任何中老年人观看， 同时假如这篇文章所提及的有你不认同的部分，请评论我，并告诉我详细的原因谢谢～。

什么是操作系统

用于处理人于硬件交互的软件 这是我对操作系统的定义，操作系统和 IE 浏览器一样都是软件，他们有比烂的可能性。

一般情况下操作系统都具备一个在不同的硬件上都表现得差不多的这么一个行为，但这不是必须的 其实我并不是很 care 兼容性，但是隔离硬件细节统一软件接口还是对于开发者而言能让自己的软件 port 到更多的平台确实是不错的一件事。

现在操作系统阵营

远古阵营不具备现实意义，solaris 也就剩下个 zfs unix 已经死了 minix... 永远活在教科书里

操作系统一般都有标准，我们现在有三个标准

POSIX 标准

GNU/LINUX 事实标准

WINDOWS/MAC/ 其他 自己玩标准

首先我们说说 POSIX 是什么东西，我觉得 POSIX 就是为了让 原来老旧的 UNIX 上的软件得以迁移到新的平台，在我看来就是 UNIX 操作系统的历史包袱强迫大家去遵守的一个标准。

所以 POSIX 标准就是比烂，对于这个我没有什么好说的， 而且事实上大家都是部分兼容，这个可比完全兼容吓人多了。

然后我们再说说 LINUX， 其实 LINUX 本身的代码写的已经够意大利面了， 但是如果抛开 GNU 不谈，userland 使用 FreeBSD 所提供的那一套环境， 你会发现你的核心是 LINUX 核心，但是什么软件都运行不了，这就是为什么 我把他们两个绑在一起说的原因，LINUX 没有 GLIBC 和 GCC 基本就是一个铁废物。

LINUX 现在已经成了服务器开发的事实标准，甚至再具象化一点 CentOS8 兼容， 这个其实也无可厚非，反正商用服务器运维自己的就行了，但是操蛋就操蛋再这群傻逼 开源了自己的代码挤死了其他开源项目然后占据了用户的心智从而被迫被绑定在 Cent 这条船上。

WINDOWS 标准: 有文档的标准都是好标准！

MAC 标准：就是粪，拒绝一切讨论。

API: Shell? 那是什么恶心东西

哦我的上帝，真的有人喜欢写 shell 嘛？ shell 本质上是对一堆 C 程序 (或者说 libc) 还有操作系统的 interface 接在一起，提供了一个非常别扭的语法。

shell 之所以能够存在就是因为 UNIX 的设计者那一帮人并没有动脑子设计出一个良好的抽象去对接各种各样的 C 代码 同时 C 语言的贫瘠的语义也不足以支持这个系统的复杂度

然后我们就看到现在的 shell 活在了他妈的 k8s 的 yaml 里，非常的吓人。

私以为操作系统的 API 的整洁度来看 Windows 虽然之前有会碰到 DLL 地狱这种东西，但是这一点上来确实吊打了所有 * nix 的设计。 API 本应该使用一种通用的描述格式，Windows 现在使用的是类似.net IL 的方式去生成的接口的各种描述，然后成功的 对接到了诸如 Rust，C#，C++ 之类的，抛弃了 header 那种蠢驴格式还有 shell 这种蹩脚玩意儿。

安全

如果操作系统不使用 C 写，而是使用一个稍微安全一点点的语言，那么现在一半以上的安全问题都不复存在

-- 我说的

操作系统的安全问题在我看来就是为 C 进行无限续命，我们把它带入到战锤的魔怔世界观里面居然有一丝吻合

每天把千个灵能者 优秀的程序员 带到黄金王座 各种操作系统 给帝皇 C 续命，而那些被献祭的灵能者都认为这是一种无上的光荣，曾经将引导人类走向辉煌的科技 UNIX 变成了迷信的偶像。混沌 syscall 的力量无处不在，无魂的虫族 bug 妄图吞噬掉整个银河。

首先我们先不谈现在 CPU 的硬件设计，先谈谈 ring 的设计

ring 就是一个环，从 0-N 越小等级越高，ring 0 在一般情况下是核心的位置，然后我们开发者一般用 syscall 去调用 ring 0 的东西，ring 1 ring 2 一般是不希望用户碰的驱动的位置，ring 3 才是用户在的地方，乍听起来好像有点道理，可是事实上现在看起来并没有那么必要而且同时还增加了 context switch 的开销，相信稍微写过点底层的东西的人都知道要想快就减少 syscall，打个比方 mutex -> futex, vfs -> fuse, io -> spdk, 我们总是知道我们写的东西绝对的安全，从而为了增加速度，我们放弃了这个设计，全部把常见的东西都拉到了 user space，那么...ring 这个设计是不是该过时了？

有一些学的更深入的人可能会想说 欸你看 ring -1 不是用来做虚拟化嘛？

我不反感虚拟化，我就认为可能就这个东西需要 ring 包一下，剩下的没必要。

系统调用（syscall）咋了

另外我们再说下我们刚才提到的系统调用，不知道这里有没有玩过 ebpf 的人，如果有，那么你应该已经知道我的思想了，我主要介绍给没有玩过 ebpf 的人。 我们知道 C 语言没有闭包，假如 C 语言有闭包也不能够跨进程甚至跨内存空间跨 ring 相互传递，所以才发明了这么一套拧巴的 syscall 机制用来调用内核里面的东西，甚至 syscall 在一群 OS 里面还要做什么 POSIX 兼容，我可去你妈的吧，假如我能够直接写一个闭包传递给操作系统的内核（ebpf 的二进制那种）操作系统进行安全验证并编译通过后直接在 ring0 运行在搭配上潮流的 pure async IO，那才看起来更像是正常人写的程序而不是说手动保存一堆状态还得记线程安不安全之类的。

什么是文件？

我成天看到有人吐槽 00后居然不知道什么是文件 我想嘴他们 你就知道什么是文件了？ 我想绝大多数人自以为知道什么是文件，然后给了我一个肤浅到我懒得反驳他们的回答， 而我本人的观点是: 我们00后为什么要为你们的傻逼设计来买单？

我们回归最朴素的文件定义：一张 A4 纸，他看得见摸得着容量有限 (你只能写有限的东西)，你可以给文件取名比如这个 A4 纸叫《柠檬的 8 月份第四周周报》 但这个跟计算机里面的文件有什么联系吗？

很显然最一开始的时候我们就是按照这个方向去抽象的，然而现在好像看起来确实没有太大的联系了，如果要描述一个现代操作系统里的文件，我们... 尝试描述一下

在 linux 系统中文件就是一个马桶 (明示 flush 冲水) 你可以往里拉屎也可以把屎冲进下水道，你可以往里面倒剩菜剩饭只要它不堵塞就行 你在这个马桶上想蹲多久蹲多久，正常情况下马桶上只允许有一个人拉屎 马桶有很多种形式，所以文件也有很多种形式，比如抽水的，木桶的，还有村里的化粪池等，打个比方你有 tmpfs，你有 rootfs，你有 procfs

所以这其实跟我们的 A4 纸模型相去甚远，所以我觉得我们必须重新好好抽象一下文件的概念了，打个比方: procfs

在 linux 的垃圾操作系统里，有个东西叫 procfs，这个东西存放了一堆动态的玩意儿，且通常放在 /proc 目录下如 /proc/cpuinfo 我们知道正常的 fs 都是可以写的，而 procfs... 有那么一点点特殊，你看着他是一个马桶，当你真的拉屎的时候才发现他居然是一个艺术品。

为什么现在的 fs 会设计的这么拧巴？ 因为前面我们提到的 shell，shell 更多情况下他是一个字符串处理语言，而处理非字符串的时候就变得力不从心， 所以 unix 这帮人会设计出一切皆文件的哲学，因为要不然万能的 C+Shell 就不能处理这个数据了！

计时器，信号，信号量，进程，网络设备，驱动：文件就是歌姬吧！

打个比方我们正常人想去读到某个操作系统的信息的时候会想有没有什么 API，而 unix 程序员喜欢 cat /proc/xxx | sed -i '.*aaa.*/bbbb/ | grep yyy 所以这些到上个世纪为止都被奉为神圣的大道至简，但是假如有一天有一个信息他并不是以文本的形式存在的... 比如某个有向图，哦 shell 不行了 可是有向图可比文本在现实生活中多太多了

文件系统的实现

刚刚我们说到 VFS 这个东西很拧巴，那么它是怎么拧出来的呢？ 为了完成我吐槽了半天的 procfs，我们发现如果这个 VFS 不跟操作系统拧巴在一起我们是无法读取类似 cpuinfo 这种 ring 0 才有权限读到的东西的， 所以说 VFS 依赖操作系统，但是操作系统依赖各种 shell，shell 又依赖各种 VFS 所以这三个构成了互依赖还是跨 ring 的，也就是说从设计上来讲， 这一坨东西其实就已经非常的不安全了...

有些资深用户应该知道最近大家都在卷什么 SPDK 或者 FUSE，为什么会有 FUSE 这种东西，因为 VFS 啊他不安全就算了，他还要假装他很安全， 假如我们执行 cat ~/.README.md 时会发生什么呢？

在 user space 呼叫一个叫 open 的函数

open 是一个 glibc 包装的 syscall

陷入内核

读 VFS

读 XFS

读 block

发送 IO 请求

硬盘读进缓冲区

发起 IO 中断

拷贝到内核缓冲区

拷贝到用户缓冲区

返回

返回

返回

出内核

出 syscall 切回 user space

拿到字符串

哦，我们 tmd 拷贝了两次，哦我们他妈的有两次 context switch，哦我们有三个不知道在干什么的抽象函数调用，哦我们还完成了好几次内存映射，哦我们拿到了一个废文 事实上只有步骤4 6 7 9 11 17 是我们关心的，这几个步骤并不需要任何什么 switch 什么换 user space，那么我们可以理解为 FUSE 就是只有这几个步骤的 FS 实现

但你有没有发现其实 VFS 在 FUSE 系统里只负责对我们要的这个资源担当 ID... 所以说现在 RedoxOS 已经把文件使用 URL 来做了。

顺便一提有了 FUSE 之后文件系统如雨后春笋般遍地开花，因为复杂度降低了，没人再鸟垃圾核心在干啥都把他接到 VFS 自己实现去了。

不过拿 URL 做和那 VFS 做好像对于我来讲并没有解决核心问题

文件一开始用来干什么的？

很多人可能会直接说 存储文件啊！还能干什么？！ 那你们真的错了，远古时期的人类有算 offset 自己写软盘的实力！

其实啊一开始 VFS 和文件这么设计就是因为想给 IPC 做持久化 虽然微软的 VFS 做了个狗屁可是人家好歹是做对了 COM 接口

那你说这个一切皆文件我看下来也不是特别拧巴，还有更刺激的吗？！

有！ PID PID (进程的身份证号) 是一个典型的拧巴的例子，他尝试用一个名字是数字的文件去描述一个正在跑的程序 放在现实生活中这真是妥妥的有病的行为，就好比说我今天早上吃饭举起了我的勺子我管他叫 3

作为计算机功底很好的你应该知道这个数字是有上限的，假设我举勺子算 3，我活着算 5，我呼吸算 6，你会发现这些事件的持续时间从很短到很长都有包括，而且 是可以被无限列举的，那么当 PID 分配完之后就从 0 开始找有没有已经空闲出来的，哦，那假如从 0 到 2^22 次方我都用我活着作为进程，那...

没错，fork failed: Resource temporarily unavailable

那假如从 0 到 2^22 次方我都用我活着作为进程

这东西我们叫僵尸进程因为他确实描述了个狗屁，但是他就是占着一个进程还死不掉

那这个怎么刺激了？

因为我说了一个 PID 是一个文件，文件也有个身份证表示当前打开的文件 FD, 哦... 但好歹这东西是活在进程内部的，不过这东西的数值有趣就有趣在这个数值真的经常变动因为打开 IO 这件事情太过常见，绝大多数 IO 活的确实够短，同时 unix 的哲学是一切皆文件， 于是你拿到这个 FD 之后当场进行操作都可能给你的脚来一枪。

安全 2: 我们完全相信用户和用户是狗的叠加态

书接上文，我们聊完了 FS 这个东西是多拧巴的设计，那么我们现在可以展开说说 一切皆文件的操作系统的一切的安全机制了

Linux 里有一套非常简单且智障的权限系统 文件有所有者

看完刚才那一章的人会发出 啊？！ 啊？！的声音， 没错，就连什么 PID 都有所有者，然后所有者可以设定三个权限，读 R 写 W 执行 X 这三个都开就是 7

Copy
R + W + X
4 + 2 + 1

第一个权限是 User 第二个权限是 Group 第三个权限是 Other 所以 777 就是最高的权限 波音 777 飞了过去

啊？！ Other？对...Other，这么笼统，以至于正常人都不敢给，然后一个应用炸了 说没有权限，然后你开开了吧... 问题来了，小而美这种应用会疯狂扫你...

所以你根本不知道开什么好，你作为用户不知道谁在哪儿访问了你这个东西，你也不能让组里某个特定成员不访问，也不能把他踢出组 (如 video 组这种)...

所以刚才的安全 1 用户是狗，现在又完全相信用户了，不愧是当婊子立牌坊的 * nix。

TOCTOU 问题（检查时和用时不一致）

Copy
if (access("file", W_OK) != 0) {
exit(1);
}
symlink("/etc/passwd", "file");
int fd = open("file", O_WRONLY);
write(fd, buffer, sizeof(buffer));

好欸，尽管我们没有/etc/passwd的权限，我们还是给他写进去了！

这里就要提一下做对了的 Windows 小朋友了

windows 的文件的权限存储在元信息里面，这个元信息有一个叫 Access Control List 翻译过来就是控制访问表，还有一个 Mandatory Access Control，可以事无巨细的控制如进程线程文件目录端口内存设备的权限，而且 Windows 还提供了一个上个世纪的 GUI

有人可能会说啊你看 linux 也有 SELINUX 你敢开嘛？出了锅你背就行

windows 的 ntfs 里面有 TxF（事务系统），可以某些程度上避免这个 TOCTOU 问题（学过数据库知道啥是 transaction 的 Atomic 定义吧，所以不可能出现刚才那个问题） 但是微软不建议使用

柠檬建议你们这群 OS 人赶紧学学数据库吧，比你们领先了两代了都！

IPC 和 Socket 和 Net

相信很多新手根本理不清楚这三个东西是干啥的，IPC 是本机不同进程用来交互的的，Socket 可以是本机的 IPC 的一种形式，同时还能访问别人，Net 是访问别人的。

哦这里又得喷 linux 了，你看看人家 Windows 有一套 COM 有一套 DLL，COM Object 注册完成之后 MSVC 直接写 C 艹继承一下就能完成 IPC，你看看你拧巴到 socket 不行 dbus 行不行，dbus 三个实现还 tmd 用户态，结果就是 114514 次拷贝信息慢的要死，然后安卓表示我不干了，垃圾 dbus，太他妈慢，这种东西你干嘛不做进 kernel，于是发明了 linux 看了眼馋的单次拷贝的 binder。

可是我又懒得展开说网络栈的事情，因为真的心力交瘁，改天单独聊聊吧。

IO 篇章

信号队列 IO 你给我死一死，没人用你

先跟看这篇文章的小白说一下这里的 IO 指的不是纯文件而是所有的 IO 如网络打印机啥的都算

对于现代人已经不可以考究的时期：哎呀不就是 IO 嘛，当场读当场写拉，内存辣么小分配啥呀，慢就等等就行了嘛～

首先我们把目光拉回到 Linux 2 的时代的 select 和 poll 这两个哥们儿... 就是为了搞笑而生的！

我们 for 一下所有 IO，然后对所有的 IO 每次调用一下 select/poll, 然后把所有的 fd 从用户态拷贝到到内核态，然后等呀等，等呀等，等呀等，你的电脑就像死机了一样欸～！

syslet/LCA : 正经人有在用这么小丑的实现吗？

Linux AIO

AIO: 我们 Linux 支持了异步 IO 了！

用户：想使用异步 IO 和一切皆文件的特性操作一个 socket~

AIO: 滚！谁惯着你！

然后 AIO 就被喷爆了！

在无数个堆屎工程师的辛勤劳动下...Linus 本人发表了一些小小的看法

So I think this is ridiculously ugly. AIO is a horrible ad-hoc design, with the main excuse being “other, less gifted people, made that design, and we are implementing it for compatibility because database people — who seldom have any shred of taste — actually use it”.

— Linus Torvalds

呕，这太 TM 恶心了！ AIO 是一种吓人的的玩具，其主要借口是 “这个设计是菜鸡提的，因为我们要兼容数据库人所以不得不实现这坨玩意儿，这些数据库人啊他们真的很没品居然咽的下这口屎！”。

— 莱纳斯・托瓦兹 (实诚柠檬良心翻译)

因为设计了半天，只对O_DIRECT 也就是真的直接访问越过了 cache 的文件是有用的，这几乎就是说为数据库量身打造的一坨在内核里的屎，然后这坨屎看起来是异步的还可以用很多奇技淫巧阻塞它！哇！

epoll 和 kqueue ， 我们把 epoll 开除了算了，epoll 抄作业都能抄烂 kqueue 这种确实是 async 但是我觉得更像是一种通知机制，所以先不做讨论

io_uring （uring: 尿）横空出世！

哦，linux 社区终于可以名正言顺的压 BSD 一头了 （隔壁 windows 的 IOCP 都多少年了？）

这个怎么样你们去看别人的文章吧，到目前为止我觉得这个才凑合，唯一的问题在于什么时候我们放弃一切皆文件设计吧

*nix 人对 UI 的品味

2022 年了！拜托应用开发者请使用 GUI，别 tmd 写一堆 cli 了！ 爷爷辈儿的电脑是因为水平太拉，根本不足以支撑 GUI 所以没用，但你看爸爸辈儿的 Smalltalk 和 LISP machine 不都是 GUI 放在第一位的嘛？你说什么可组合性？你说什么打字更快，哦，那不是不用 GUI 的理由，而是开发者懒得提供快捷方法。

关于 GUI 怎么设计我想我的观点可以再开一个文章单独聊聊

总结

要不是 OS 卖不了钱卷不起来你看看如果能创造 DB 那样的收益 OS 人早就革新了无数代了