foobar2024

很讨厌一个四字短语:人到中年,所以不想用这四个字开头。

人到中年,发现最宝贵的东西是时间,因为你不管怎么用到头来都会觉得有些浪费。所以,我都浪费钱买了个域名(一年能解析10000次不?给域名商省钱了),难道还要我浪费更宝贵的时间写 blog?/doge

只要稍做分析一下投入时间的产出比,就能轻易得出结论:blog 的更新频率只能是单调递减的,原来本就不期望笔耕不辍,现在就能更心安理得的越写越少。

不过单调归单调,还是不能趋近于零。因为多少写写,留给将来的自己review(鄙夷)一下,还是有点价值的。

Boring Tech

这几年流行一股实用主义风潮:Just use the BORING tech! 因为 Boring Tech 经过时间验证,坑少特性全,皮实耐用。人力成本低不说,产出还不低,就两个字 —— 好用

确实,从 Run A Business 角度来说,使用 Boring Tech 绝对是首选。

BUT,这里总是有个 BUT,从个人的角度,这里却有个最大的问题 —— Boring。就拿最常见的 web 开发来说,无聊具体体现在:前端一顿折腾就输出了一个text/html,其他都交给浏览器就行;后端一顿折腾最后就写了一条SQL,其他的交给 framework 交给其他的 component 就行。

响应式、渐进式、高可用永远都是名词,只能远远的望见森林,却一棵树都不认识。

现代代码开源的繁荣,确实降低了广义上程序员的门槛,不需要,只要会。不过本来社会大分工的目的就在于提高生产力,专业的人做专业的事,没那么专业的人做不需要太专业的事,这是符合社会发展的自然规律的。

但是只用不懂,等于浪费了开源。

?书读百遍其义自见。

Read The Code

读代码永远比写代码难,这个毋庸置疑。原因无非:

  1. 语言原因,一个你都无法写的编程语言,必然也是不能读的。
  2. 还是语言原因:编程语言的易写和易读是相悖的,写的越舒服,就表明 explicit 的元素越少,读的也就越困难。现而今流行的 javascript python 之类弱类型的语言,本来就为写起来舒服服务的。
  3. knowledge 原因:缺乏现代计算机基础的 knowledge,比如不知道 fs 的抽象,基础的 io 没概念,那必然读不懂数据库存储的代码。
  4. 还是 knowledge 原因:缺乏领域相关的 knowledge,连 CNN 的概念就不知道,那读 LLM 的库就跟读天书一个意思。

解决办法也很简单(不简单):

  1. 语言:多写。你想读的代码用的语言,只会写 javascript 必然读不了 sqlite,想要读,简单的数据结构算法啥的总得都用 C DIY 一遍,才能找到些感觉。
  2. knowledge:多学。基础知识写到哪儿学到哪儿,今天了解一个 syscall,明天了解一个 register,多问 AI 多看 manual。而 domain knowledge 最好还是看 paper 这种第一手的,这个门槛真没想象那么高。

还有一个读代码的杀手锏,永远带着问题去读,起点是一个问题,终点可能是一个答案外加一大堆收获。

拿我近两年读的最有启发性的代码为例:

Linux 0.01

我有个很大的问题:x86 的 *nix kernel 是如何把自己和 user space 隔开的?

我不但得到了我的答案,还把很多东西都串联在了一起,有种融会贯通的通畅感。

简单的答案:fork (in init/main.c)

复杂的答案:

fork 这个 syscall 会 copy 当前进程的地址空间,在不执行任何指令的时候,两个进程应该是长得基本一样的。看一个简化的执行序列,从后往前推:你的 process 从 shell fork 而来,shellinit fork 而来,而 init 竟然是从 kernel fork 而来的!

所以,每个 process,其实从根源上都是从 kernel 派生的,证据就是每个 process 的 0xf 地址都住着同一份 kernel 代码(并不是 copy 而是 physical address 层面的一致),这也就是为啥 linux 叫 monolith kernel,因为每个进程觉得自己都有一个大而全的 kernel 只为自己服务。有了这个认知了以后,也就不难理解 micro kernel 的信徒会觉得 monolith kernel 不够安全,因为如果有漏洞导致 process 越权,那么将会影响到这个 os 上的每一个 process。

但实际上越权又非常的困难,因为切换实模式或者 ring code 只有 kernel code 能操作,CPU 只通过 CS 寄存器(正在执行指令的高N位),就可以很容易而且高效的检查出正在执行的指令是否非法:CS > 0xf。而 application 想要做高权限操作,只有 syscall 这一条路。

而 syscall 了以后,程序进入 kernel space,这个时候,事实上只有一份的 kernel code 就可以通盘做进程调度或者其他的全局操作了。(除了 syscall,其他时候也会进入 kernel space,毕竟启动了以后的 kernel code 就 while(1) 在了 schedule() 上)

Redis 7

我有很多问题,但基本上都能在 redis 里找到答案。

咋写一个高效的 event loop?

while(1) 然后阻塞在 epoll_wait 上,对了,你还需要抽象一个跨平台的 epoll family,然后还要记得 epoll_ctl_add 一些仔细构造的 time event ,防超时还能优化调度。

咋实现高效的 hashmap ?

rehashing 的时候用 double buffer + one frame one step,很像传统游戏引擎的玩法。

还有啥更高效的 key-value 数据结构?

hardcode lookup table,关掉大脑里的 linter (和密集恐惧症)。

基本上,redis 的 codebase 已经代替掉了 lua(lua 的 code style 太老,有太多 global state,很容易跟不上上下文),成了我心中的 Best C Codebase,各种问题想起来都可以在里头 rg 一下看能不能找到答案。

Write The Code

读完了还需要写,我依然觉得 get your hands dirty 是最好的学东西的方式。

这两年写过的好玩的东西:

光追

跟着文章完整写了个 ray-tracing 的 demo,写完了才感觉 Rust 终于入了个门。

json serializer

一个Json Parser,铆足了劲儿优化半天,基本跟 Rust de-facto 的 serde-json 差不多性能,比 v8 慢点,但约莫还是一个数量级。

javascript

一个基本能跑(指可以跑递归的 fib 函数)的 javascript 实现

Advent of Code

一年一次(2023, 2022),比 LeetCode 有趣(连 antirez 也这样觉得),写多了屎山代码可以用作调剂。