Matheus Moreira (blog) 构建了 lone lisp,直接基于 Linux 系统调用实现。在本次采访中,他分享了自己在 C 语言和 Linux 内核方面的知识。
最初是如何进入计算机领域的?在发现 Lisp 等技术之前,你的路径是什么?
我一直喜欢计算机,但真正让我进入编程领域的是电子游戏。小时候,我玩过一个叫《洛克人EXE》(Mega Man Battle Network)的系列游戏,并从中获得了灵感。正是这些游戏让我学会了英语;我曾以非英语母语者的身份加入论坛并尝试社交。起初很艰难,但我进步了!我的第一门语言 C++ 也是如此。
你开始学习 C++ 时有多大?
在巴西有“技术学校”,我读的就是这种学校。它们提供普通高中课程,同时增加专业课程。我不确定国外是否常见这种学校,以及是否有对应术语。还有其他课程,例如化学、机电一体化……至于质量,课程属于基础水平,但对高中生来说已经相当不错了。这确实让我开始自学;6 个月内,我就已经学完了整个课程。最终他们聘请我担任助教,帮助其他同学。
因此,当我开始读高中时(大约 13-14 岁),我选择了信息学课程,并开始使用一个非常老的 IDE Dev-C++(它至今仍在 SourceForge 上存在!)学习 C++。我还浏览了 cplusplus.com 上的教程。我尝试制作游戏,但并不成功!这相当困难,尤其是因为我当时还没有学习到需要模拟的物理知识 :) 我最终做的是学习了更多语言。在掌握基本的 C++ 之后,我学了不少 Java,然后是 Ruby、Python……这最终引导我接触了 Lisp 和 Scheme!我记得第一次看到 Scheme 时,觉得它极其优雅。 不过 Ruby 是我的最爱语言。当时我写了一些 gem。其中一个实际上获得了少量关注:Acclaim,一个类似 git 的命令行参数解析器。
很酷!你是否很开心地跳进新语言,还是先试图坚持使用 C++?
我想我开始学习更多语言是在我把 cplusplus.com 上的教程学完之后。我觉得如果那个网站上已经没有我可以学的东西了,那我应该继续前进,对吧?Sun 公司当时有一个非常大的 Java Tutorials 网站。我想我读完了整个站点。那时我学会了面向对象编程。我在青少年时期写了一些 Java 代码……一些“实用程序库”。回想起来,那只是 Apache Commons 的 1% 的糟糕实现!我想我甚至发布过它,可能现在还在某处……我还记得大量实验 Java Swing 应用程序。我曾经非常喜欢 Nimbus 外观,并为我的数学教授做了一个圆形/弧形绘图应用程序。
是什么促使你进行所有这些探索?是什么激励你不断尝试不同的语言等等?你也坚持使用 Ruby 吗?
在早期的学校年份,学校项目是一个很大的动力,但到了某个阶段,这更多是出于好奇心,以及对“正确的事物”的渴望。我想要一门可以称之为家的语言。我使用 Ruby 的时间最长。它是一门非常不错的语言。我总是安装它,并且最近开始了一个 Rails 项目!但它也有自己的不完美之处。我坚持使用的另一种语言是 C。它低级的简洁性对我来说真的很有吸引力。
我没有忘记 C++,但也没有更新我的知识。当前的 C++ 本质上是一种全新的语言,所以我不再声称自己懂 C++。从某种意义上说,我用 C 取代了它。C 总是出于某种原因吸引我,尽管存在所有问题和遗留代码,但当我开始探索 CRuby VM 源代码时(当时还是青少年),它真正开始在我脑海中扎根。我有一个非常有趣的消遣:我会留意 StackOverflow 上那些有人问“为什么 Ruby 的行为像 X?”的问题,然后深入源代码去找出答案。
几年前我尝试转向 Rust,但并没有坚持下来。我也是 Zig 及其创造者的巨大粉丝。自从我观看了他发表“通往 Zig 1.0 的道路”主题演讲,阐述人们“用 C 重写事物”的原因(速度、ABI……)以来,我一直很喜欢。
是什么优缺点让你坚持使用 C 和 Ruby?
Ruby 是一种极具表现力的语言,它“正好符合”我的心智模型。我经常能猜出一个方法名,它就能工作。它有一些不错的东西,比如方法名的单复数形式、同义词。我们的大脑使用这些词,而 Ruby 支持它们全部。
Matklad 提到好的 API 应该是可猜测的,如果他猜测的东西不存在,他经常会提交 bug 报告说 API 需要它!
Ruby 还允许组合出异常地类似 shell 的处理管道…… items.each.with_index.map…… 它可以不断组合,你可以用很少的字符完成大量工作。
对于 C,主要优势是输出的简洁性。你写一个函数,它会编译成一个 ELF 符号和相关的汇编代码。他们说当你写 C 时可以看到编译器输出,这是真的。使用 C++ 时,编译器会添加大量机制,很难理解这一切是如何组合在一起的。对于动态语言来说也是一样,但我发现探索虚拟机的源代码比探索编译器的源代码更容易。
那么你的 C++ 风格改变了,去掉了糖,直到你用 C 编写?
是的。随着我阅读更多其他开发者编写的代码,我的风格也发生了变化。我探索过 CRuby 代码、CPython 代码、JVM 代码、一点 Guile 代码,甚至一点 V8 代码……不过当我开始阅读 Linux 内核源代码时,我开始真正钦佩专家级的 C 程序员。它真的很棒,可能堪称黄金标准。
从一开始,你就读了很多代码?尤其在今天,人们真正阅读他人代码的情况相当罕见。你是如何处理这个问题的?有什么技巧或技术吗?
我读了很多代码。我一直都是那种好奇的人,想要知道事情是如何运作的。我不满足于只是使用某个库,我想要知道它在底层做了什么。我想知道驱动程序如何与硬件通信。我想知道所有隐藏的有趣内容。那正是我感兴趣的那类事情。我尝试制作的真正应用程序,最终会偏离到这些无关的探索中。其他程序员会告诉我“为什么要浪费时间在那个上面?它已经被解决了”,但我想知道它是如何完成的,最终我也开始发展出一种傲慢,导致我想重新发明轮子,而且要做得更好。
不过我确实得到了这个信息。我觉得由于这种怪癖,我不适合从事软件开发职业,所以最终我没有追求这个方向。至今仍将其作为我的爱好。
当你第一次查看驱动程序代码时,你是如何理解它的?
起初……我完全无法理解它。这很令人沮丧,但我坚持了下来,最终我至少能够理解一些东西。我肯定不是硬件工程师……但我设法逆向工程了我笔记本电脑的一些功能。这仍然是我最引以为豪的项目之一。我的笔记本电脑是 Clevo 裸机型号之一。它有一个非常不错的键盘,带有 RGB LED,大多是单独可寻址的。有一个非常糟糕的 Windows 应用程序来控制这些 LED。我发誓它花了超过一分钟才在屏幕上显示一个窗口,这真的很烦人。我使用 Wireshark 截获它发送到键盘的 USB 数据包,并为它编写了一个 Linux 用户空间驱动程序。我捕获了数据包,盯着屏幕直到弄清楚它们的结构,然后编写了一个小 C 程序来为我配置键盘。把它扔到 GitHub 上就忘了……有一天我回去看, somehow 人们在使用它,一个人甚至为它做了一个 GUI。那是一种非常好的感觉。我通过尝试逆向工程笔记本电脑的其他功能,学到了更多关于驱动程序和计算机技术的知识。ACPI 电源管理相关的东西。不过我并没有完全弄清楚所有东西,直到今天。
你有一篇关于 Linux 系统调用的文章,是那个时刻让你走上 Lone 的方向吗?
系统调用的代码非常简洁,它们就像普通函数调用一样工作。我想说有趣的部分在于它允许你绕过 libc 并直接针对 Linux。Linux 以用户空间 ABI 的稳定性而闻名;用户空间组件则不那么稳定。它允许人们直接依赖 Linux 内核,而无需其他中间层。
我开始时有一种普遍的感觉:人们不希望我使用它们。我会阅读手册,它们会说“这个系统调用不受 glibc 支持”。我会阅读维基百科,上面会有一个信息图显示 glibc 包装 Linux,就好像它是 Linux 的 CRT 而其他 libc 不存在一样。然后我读到 LWN 上的一篇文章,提到 getrandom 系统调用花了大约两年时间才出现在 glibc 中。
CRT?
C 运行时。标准 C 库:glibc、musl、uclibc。
正是这个包,每当它们破坏兼容性时,整个世界都会崩溃。因为一切都依赖它。我记得那篇文章提到类似“也许 Linux 应该有一个 liblinux.a,只包含系统调用,然后人们可以在 libc 没有帮助时通过向编译器传递 -llinux 来绕过 libc”。我觉得这是一个非常酷的项目想法,所以我创建了 liblinux,它正是如此:一个用于独立 C 编程的 Linux 系统调用包装器集合。事实证明,针对 Linux 系统调用接口编程非常有趣。C 的许多问题在独立模式下都消失了。
在独立模式下,C 的哪些问题消失了?
最基本的一个:errno。C 有“当前错误”的概念,它是一个全局变量。每个函数都设置它,你应该检查它。Linux 只是像普通函数一样返回给你那个错误。C 库会获取那个值,并将其放入全局变量中。这就是 C 的方式。但在独立 C 中不是这样。C 的遗留问题也消失了。C 库中有大量遗留代码。例如,locale 相关的东西极其成问题。它也是全局状态(一个反复出现的主题!),可能会根据环境变量等导致非常微妙的错误。Liblinux 教会了我独立 C,以及 C 可以多么干净……如果我们能删除所有它的遗留代码的话。这很痛苦。我不得不编写我自己的所有函数。从基本的字节复制函数,到整数到字符串的转换函数……不过问题最终并没有我想象的那么难以克服,我坚持了下来。我知道当有疑问时,我总是可以阅读代码。我可以看到前人是如何做的。glibc 仓库很难解释,但 musl libc 源代码非常干净且易于深入研究。它一直非常宝贵!它也激发了一点创造力。例如,如何在没有 C 库、没有内存分配、只有静态内存的情况下创建自动化测试套件?我设法在 lone 中做到了!
我记得我甚至在 Reddit 上向 Greg Kroah-Hartman 询问为什么 Linux 从来没有一个“官方”的 C 库。他回复说 Linux 曾经有一个 klibc 项目!liblinux 的历史到此结束:某个时刻我发现 Linux 内核开发者正在做完全相同的事情!他们创建了一个 nolibc.h 文件,他们在自己的工具中内部使用它。我认为它比我自己想出来的更好,也更容易使用。所以我归档了这个项目,并开始了别的东西:Lone Lisp。一个用那种范式编写的完整编程语言。内心深处,我只是想向自己证明,我认为这是可能的想法并不是疯狂的。如果我能做出一个直接在 Linux 上运行的独立 Lisp,它将证明任何程序都可以用那种方式编写。
现代 Linux 用户空间是庞大而迷宫般的。Linux 本身不是。我发现它是一个非常干净的系统。一切的基石实际上是 Linux 系统调用二进制接口。我们实际上可以扔掉整个用户空间,只用 Linux 从头开始。我们甚至可以扔掉无处不在的 GNU 东西,如果我们愿意的话。
为什么我们不能有一个内置系统调用支持的编译器?只需添加一个 system_call 关键字,使用提供的参数内联 Linux 系统调用代码。不需要在中间插入 libc 的废话。不需要 C 或任何特定语言。有人今天就可以创建一门语言,而这个单一特性就会让它在系统编程方面与 C 一样强大。他们可以编写软件并直接引导 Linux 进入它。- Matheus 2021
总之,独立 C 移除了一些有趣的 C 瑕疵:所有全局状态:errno、rand/srand 状态、locale 状态、strtok(不是 reentrant!)、偶尔会咬人的隐式文件缓冲;设计不良的 API 如 gets(站不住脚)、atexit、signal,甚至 malloc;lone 的分配器要好得多。math.h 在处理 NaN 时会变得非常混乱,当涉及 errno 时情况会更糟。有些东西永远不会离开。C 的神秘整数提升规则仍然存在。还有缺少溢出检查,但我使用编译器内置函数来处理这些。我也很担心我可能会引入自己的瑕疵……傲慢无疑总有一天会咬我。
你知道为什么 nolibc.h 的方法不更受欢迎吗?
截至目前,nolibc.h 已成为内核树中的一个庞大目录,它已成为一种临时的 libc。它似乎被视为一个内部项目,用于编译 Linux 自己使用的内部工具。我认为 Linux 内核开发之外很少有人知道它的存在。人们完全可以使用它来构建自己的项目,但我不知道 Linux 在这方面提供什么样的稳定性保证。
相关地,我向 Cosmopolitan 发送了一些补丁,例如帮助实现浮点解析。我是这个项目及其作者的粉丝。即使那些称它为“愚蠢”黑客的人,在我看来也承认它是一个天才的黑客。这是一个令人惊叹的项目,它将大量非常神奇的知识凝聚成一种形式,即 APE 格式。能够在具有不同可执行格式的各种平台上解析为有效可执行文件是一件非常奇妙的事情。而且它在此基础上还做了很棒的事情,支持在可执行文件中嵌入存档。
至少思考傲慢以及如何避免瑕疵,这很重要,希望如此。许多人从不内省……浮点是 lone 唯一未检查的特性,之后会发生什么?你想在其中构建特定的东西吗。
正如 Steve Yegge 所说,仅仅创建一个解释器就创造了一个婴儿,一生的工作。浮点将是一个难题,但前面的工作实际上是无限的。
就在这个周末,我实现了错误处理系统。它是一个 Common Lisp 风格的可重启条件系统。在此之前,每个错误路径都是从解释器硬退出(255)。我的下一个任务是用 Lisp 中可以处理的异常替换所有那些调用站点。我也刚刚推送了正确的尾调用优化支持……浮点将是一个困难的特性,如果我发送给 Cosmopolitan 的补丁有任何指示的话。这是我必须实现的东西。我也想最终开始实现来自 Erlang/BEAM 系统的想法。我构建它是为了有一天可以有多个解释器并行运行 Lisp 代码。这些都是语言级特性。
用语言本身构建什么?我想用 lone 做的第一个项目……是替换我的静态站点生成器。我用我自己维护的 pugjs 分支编写我的网站。我的长期目标是用 lone Lisp 编写我自己的系统来替换它。之后,我想编写我自己的 shell。我自己的实用程序。开始用我的形象重制传统的 Linux 用户空间。
这些是我在开发 lone 时脑海中浮现的想法。我的一篇文章甚至提到了 env 的实现,它只是将环境作为 lone Lisp 表转储出来。当时 Lone 没有任何迭代功能,所以这是我能做的最好的。现在 lone 有生成器了!我可以做更多……当我实现某些东西时,那是一种非常好的感觉,它解锁了很多其他东西。嵌入段特性是我专门为“Linux 实用程序”用例设计的。Lone 解释器有一个特性,它读取一个特殊的 ELF 段并直接运行那里的代码,就好像它是编译进去的一样。所以我可以通过复制解释器、向副本添加一些 Lisp 代码来制作 lone 程序,它就变成了一个自包含的程序。它现在有了生成器、定界续延,而我刚刚引入了错误处理。
新的错误系统与 Common Lisp 的有多相似?
错误系统在许多方面相似,但没有 Common Lisp API,其中发出信号的代码提供重启选项,诸如 use-value 之类的选项。我的系统直接构建在定界续延工作之上。在实现它们之前,续延只是那些神秘的 Scheme 特性之一,但我发现每个人其实已经在使用它的某个版本。异常机制本质上是一种续延形式,它们只是没有被捕获成一个值。而魔法在于捕获续延。Alexis King 关于定界续延的演讲为我揭开了谜底,它展示了最终这只是 memcpy 栈。制作栈的副本,然后在其他地方“重放”那个栈,就像 VCR 一样。可恢复的异常。错误发出信号系统最终就是这个,上面有一个处理程序机制。我不得不实现“catch ErrorType -> code for ErrorType”部分。我选择了一个简单的系统,只是映射,比如一个符号如 'not-found,到一个 lambda。所以它测试 lambda 的 arity,如果它接受一个参数:(lambda (error) ...),它就像其他语言一样工作。但如果它接受两个:(lambda (error continuation) ...),那么它提供了一种通过续延恢复代码的方法。
Lone 的错误系统使 throw 返回一个值给调用代码。每个异常处理程序可以选择性地接收一个定界续延。如果它们接收到,它们可以调用它通过返回一个值来恢复错误的代码。
你是否拒绝了任何特定的系统或方法?
我倾向于拒绝其他语言解决这些问题的通常重量级方式。例如,我明确拒绝了 Common Lisp 风格的“重启 API”,它让代码枚举若干种重启方式。我希望一切都用求值器本身中的简单语言机制完成,所以重启的行为就好像 signal/throw/transfer 返回了一个值。我还拒绝了求值器中的“特殊情况”,而传统的 Lisp 实现让 eval 明确支持特殊形式如 if。在 lone 中,求值器没有那些特殊情况。它支持 FEXPR,它们本质上是求值器插件。这使得某些事情比它们需要复杂得多,只是为了那种优雅(至少在我看来)。例如,尾调用优化适用于纯 Lisp 函数,但不完全适用于原语。原语的 TCO 从那时起已经修复,但它曾经有 bug,最终是因为我做出的将它们从 eval 中分解出来的设计选择。我不得不为它们引入特殊的机制来尾返回。
我也一直在尝试提出用语言内方式做常见事情的方法,利用求值器机制。例如,我让向量(数组)和表(字典)可调用。([1 2 3] 0) 返回 1。然后 ([1 2 3] 0 4) 修改向量使其变为 [4 2 3]。续延和生成器也可以直接调用。有两个原语:control 和 transfer。Control 就像 begin,但它支持续延。Transfer 从代码中跳出到最近的 control 块。它返回一个可调用的续延值,编码了跳出点的程序。所以恢复它就是 (continuation 'some-value)。(control (transfer 0) (lambda (value continuation) continuation)) 然后你可以通过调用 (continuation 'something-else) 使那个 (transfer 0) 调用返回其他东西。它的工作方式像一个函数(相同的调用约定),你也许可以在它上面实现柯里化。
Lone 实际使用了多少个系统调用?
当 lone 启动时,父进程只有一个 execve 系统调用。然后大约有十几次 mmap 系统调用用于内部内存管理。然后它从标准输入读取脚本文件,运行它,然后退出。最小化。在执行过程中,它也可能导致更多 Linux 系统调用内部触发,mmap、munmap、mremap(!)。模块系统还使用 openat 和 close。在 Linux 300 多个系统调用的广泛列表中,lone 只使用了很小的一部分。它在 Lisp 级别提供了一个系统调用原语。所以 Lisp 代码可以做任何事情。许多那些系统调用也需要内核数据结构。创建/操作那些 C 结构的支持也待完成。例如,ioctl 是一个重要的系统调用,它将需要那个结构支持工作。它允许做诸如获取终端大小之类的事情。
你认为那些东西现在离实现还有多远?你在考虑扩展“路线图”吗?
老实说,我不知道。我不太擅长组织。不过我在 AI 帮助下变得好多了。我一直在使用 Claude 作为某种管理者。我已经独自编程超过 15 年了。让另一双眼睛审视我的项目,批评我的代码,发现问题,欠发达的领域,对我来说真是改变世界。我经常把整个 lone 代码库指向 Claude 并告诉它要严厉,指出所有问题,识别未来开发领域。与 AI 讨论想法也非常有用,它使我能够在几个实例中完善我的想法。我改进了垃圾收集器和内存分配器,因为 AI 能够教给我改进得多的算法。我仍然不信任它为我编写代码。我太在乎 lone 了,不会仔细检查进入它的每一行。自己编写代码通常对我来说也是有教育意义的。但我肯定有罪于把 Claude 指向我的基于 JavaScript 的静态站点生成器,让它为整个特性“vibecode”。
当我要求 Claude 审查整个代码库时,它发现问题,也建议未来的方向,然后将它们编译成文档。“用户想要 X”,“用户拒绝了方法 Y”,它对管理大量只存在于我脑海中的临时上下文非常有用。它本质上是在自动为我管理待办事项列表。我经常会说类似“我们现在不做这个,为将来记一下”而 Claude 会将其存储在项目的记忆中。例如,我计划的一个特性是对 GCC/clang 编译器 API 的支持。最基本的一个是栈保护器函数。我有一个存储了关于那些 API 所有已知信息的笔记。
还有什么启发了这种语言?你使用“现代”Lisp 语法,这现在似乎是 普遍的。
Ruby 和 Scheme 启发了我。我记得编写 Ruby 时感觉有多棒,以及 Scheme 作为一门语言有多干净。我想把两者混合起来看看会得到什么。Lisp 真的很容易解析,这也帮了大忙。我手写了解析器,突然有了所有那些我可以玩的嵌套列表,这正是我真正感兴趣的。不过直到我在求值器中实现 FEXPR 支持,我才真正理解 Lisp。函数只是接收列表本身,这让它终于点击了。
我认为用 [1 2 3] 表示数组、用 {k v} 表示字典非常直观且不错。除了 Clojure 和 newLisp,Ruby、Python、JavaScript 也有这种语法。不采用这种语法可能是个问题!它会为熟悉流行动态语言的程序员增加很多不必要的摩擦。Common Lisp 的字面量也有些难看:#(1 2 3) 是一个列表,但由于 # 而不完全是?
对你来说,什么是好的编码风格?
我的 C 风格是 Linux 内核风格的修改版本。我倾向于紧密遵循它,但在某些情况下打破我自己的约定。例如,他们真的喜欢统一的代码以使其在视觉上易于解析。我喜欢更多地对齐和装饰代码。我的 Ruby 风格偏好缺少标点。method 'arg' 而不是 method('arg')。这个想法是让代码读起来更像英语散文。设计良好的 API 也有这个属性:collection.each.with_index { |item, i| ... }。现在对我来说对 Lisp 风格发表意见可能还太早,但我一直在发展对更少语法嵌套的强烈偏好。简单例子:let。在 lone 中,let 的工作方式是:(let (a b c d ...) body...) 而不是历史悠久的 (let ((a b) (c d) ...) body...)。我这样做的原因与函数应用实现相呼应。((lambda (x y)) 10 20) -> x 10, y 20。使用 (x 10) 和 (y 20) 列表只是感觉不自然,它们不是真正的对,它们对我来说更像一个有序序列:x 10 y 20。
性能如何?
有一个基准 lone 获胜:启动速度。它可以启动并完成程序的速度比 Ruby 加载 Rubygems 更快。临时基准测试显示 lone 比 Python 慢大约 10-100 倍……它们属于不同的性能类别:lone 是一个列表级解释器,就像 SICP 的第 5.4 章。Python 和 Ruby 是字节码解释器。我勇敢地抵制字节码编译,只是因为我觉得如果我把列表转换成字节码,我会失去 Lisp 的精髓。如果求值器不处理列表,它真的是列表处理吗?
我记得读过一篇文章,关于 CPU 分支预测器变得如此复杂,即使是普通的朴素解释器也变得快速,它们现在显然可以预测通过间接分支。这给了我一些希望!我长期的性能计划是保持 C 解释器作为终极基线用于引导,然后在 lone 本身内编写一个 JIT 编译器。我可以 mmap 一些可执行页面并发出指令。那就是编译开始的时候……Python 经常因为其糟糕的性能而受到羞辱,所以我担心 lone 会受到更多欺凌。但事实是……它对于用户空间工作应该是出奇地可行的。我对 bash 进行了一些临时基准测试并完全碾压了它,人们一直在使用 bash!(不过那可能是一个测试伪影,因为递归斐波那契不是 bash 的强项,它可能在每次迭代时产生新进程或什么的……)但对于脚本,启动速度很重要。你不能有一个需要半秒来引导的虚拟机,而 lone 在这里真正闪耀。
我也可以进一步改善 lone 的启动。例如,Ruby VM 会运行相当多的代码来引导自己。它会做诸如在各处初始化哈希表之类的事情,诸如将某些变量映射到 Ruby 对象的环境。对于 lone,我将不得不添加一个构建步骤来预先计算所有哈希表并将它们编译成 C 初始化器!然后 Linux 会 mmap 可执行文件,它就准备好了,就这样!对脚本来说很棒!Zig 的 comptime 会使这变得非常容易,但我开始 lone 时并不真正了解 Zig。
Lone 有一些“内在”模块;顶层环境正好包含两个符号:import 和 export。从那里,所有模块引导它们的环境。即使是 if 和 quote 也必须被导入,因为我希望用户对他们环境中的每一个名称都有完全控制。你必须导入 一切,除了列表、字面量和函数调用机制。目前它非常斯巴达;我只是实现了实现本身需要的东西,例如 flatten。总共有大约 50 个函数,主要在 lone 模块中,它包含语言特性:if、let、begin、control/transfer、intercept/signal……
mmap 到底是如何工作的?
mmap 本质上是一个函数,它请求内核给你内存的“页面”。它很像 malloc,但更底层。每个页面(最常见)是 4096 字节的内存,你可以请求其倍数。你可以在那些页面中请求特定的保护。可读页面、可写页面和可执行页面(JIT 编译器使用的)。内存分配器在其上构建更高级的接口,因为当你需要 16 字节时请求 4 KiB 会浪费大量内存。所以分配器会请求比如 128 MB,然后将其切成块来服务程序的需求。
在测试中,你有各种示例,但还没有语言文档(只有实现内容)。当时间到来时,你将如何处理这个问题?
它非常不完整,但事实是我即将用尽可以添加到语言中的东西;我很快将不得不处理标准库。我会向 Ruby 和 Scheme 寻求灵感。Ruby 的标准库在我看来是无与伦比的,同时我也喜欢 Scheme 的命名方案。迭代协议是列表上的下一个。它会是类似 (for (each collection) (lambda (item) ...)) 的东西。我已经有一些 each 函数,试图在那里复制 Ruby。它们应该重新设计了。它们都应该返回生成器。然后是字符串操作函数。甚至一个允许高效字符串连接的基本字符串构建器也会让 lone 更有用。我打赌如果我添加这个,并完成 lone 的迭代故事,我可以在 lone 中编写一个 hiccup 风格的 Web 框架。
当我到达那个阶段时,我有被 ADHD 困扰的危险。现在我正在处理所有这些有趣的底层东西。一旦完成,那是“哦,现在我只需要做标准库”的时候,我可能会在管理它时遇到麻烦……到时候我会让 Claude 鞭策我保持状态。
我认为 APL 和 Lisp 更简单,并试图传播,以赋权并展示事情有多容易……我很少成功。我试图用“哦,那只是 X Y Z”来让人们惊叹,就好像它真的很简单,但我的效果相反,它反而让人害怕。
我只是享受这些洞见,对吧?比如续延。哦,它只是复制栈帧。它以前是神奇的,现在是正常的事情。我感觉人们不喜欢这样的洞见,因为最终他们不在乎。内心深处,lone 的使命之一是激励人们拥有自己的系统并探索。写手册的人告诉我我不能。你不能使用这些 syscall 东西,它不适合像你这样的凡人。我想使用被禁止的东西,我想成为制作库的人,而不仅仅是消费它们。我希望 lone 能激励其他人做同样的事情。我认为人们不这样做的一个原因是责任。拥有你自己的系统,你必须拥有它。如果它坏了,你必须修复它。人们认为他们已经在生活中承担了太多责任,计算机只会再增加一个。我希望更多的人选择那种责任并享受它带来的所有回报。
编写 lone 让我拥有完全的控制,甚至控制我周围的名字。感觉计算充满了遗留。所有这些根深蒂固的代码,它是错误的,它是丑陋的,但没有人触碰它,因为如果你删除它,它会破坏一切。我想看到人们变得大胆并开始删除坏东西。也许重制 Linux 用户空间的不是我,也许是 Rust 或 Zig 的人。我摆脱了 libc,他们警告我它会破坏东西,它会破坏 DNS,它会破坏一切。我本可以被吓倒,但我坚持了下去。我只想向世界展示,看,没有什么好害怕的。你可以剥去所有这些层。它是安全的。也是一段有趣的旅程!即使 lone 没有任何成果,如果我至少能激励那个,我会很高兴。
我喜欢这个关于接管整个栈的激进所有权的消息,因为你可以做到。这与 Decker 或 Fennel 及其游戏的启发性火焰相同。
是的。我真的很喜欢自由软件的用户赋权价值观。计算机是改变世界的科技,我们应该拥有它们,我们不能让公司或政府控制它们。当我看到人们不关心所有权,让公司“管理”他们的计算机时,我感到非常难过。因此,通用计算机变成了电器,某种更低级的东西。很多人讨厌 systemd 之类的项目,但在某种程度上,它们正在做我想要激励的 Linux 用户空间的“重新想象”。也许比我希望的更强硬,但我不能争辩 systemd 有多好。他们重新发明了它,而且很棒。我想看看一旦人们摆脱传统的 *nix 系统,他们还会想出什么。选择自己的战斗也很重要。如果我试图让 lone 成为裸机 Lisp,我不确定它是否会引导。我会让比我聪明得多的人做那个。jart 的 sector lisp 是一个很好的例子……在引导扇区代码的 512 字节以下的 Lisp。
我每天使用 Termux 来自动化很多事情。如果你告诉我 lone 是在 Termux 里诞生的,你会相信吗?我用手机写了很多它。尽管 Android 阻止了一些 syscall(statx 是咬我的一条),lone 可以在 Android 上运行,测试套件 100% 通过!
你决定不从事软件职业,你在做什么?
我是一名医生。我于 2019 年从医学院毕业。它始于 AI。在高中时,我开始喜欢生物学,因为我在阅读关于编程神经网络的内容。有些日子我连续工作 10 到 14 小时,从事医学工作。其他日子我根本不工作。今天就是这样一个日子。日程表是空的,今天没有预约。医学提供了一定的自由来安排自己的时间。有许多职业路径可供选择。外科医生实际上生活在医院里。我想要一种更轻松的生活方式,因为我需要时间来放松。我在一家受欢迎的诊所工作,有电子记录。这真的很高效,尤其是对我这样的技术人员来说。即使系统很慢或没有功能,我也会用我自己的技能来弥补。即使是一些保存在计算机上的小模板也能让事情加快 100 倍。因此我可以快速咨询并与患者保持良好的关系。甚至能够盲打也有帮助!我可以在看着人们的眼睛的同时输入一切!我的同行做不到!我之前提到的 Rails 项目正是如此。我正在制作我自己的电子病历系统来挠自己的痒。即使它失败了,我也会得到很好的服务。
非专业程序员非常少。你当然很早就开始了,但你认为为什么更多专业人士不挠自己的痒?
我认为很多人实际上不喜欢计算机。要改变这一点,我们必须让他们对计算感兴趣,计算机可以多么棒。学习确实需要努力,但大多数人的心态似乎专注于结果。我在高中时已经在选择参加编程课程的人中看到了那种态度。好奇心是必要的,以关心结果之外的事情,深入了解香肠是如何制作的。我不知道如何激励人们。如果你找到了方法,我很想知道。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.