沐浴在汇编的温暖中:从零开始构建 Web 服务器
在由高级框架和 AI 生成的样板代码主导的时代,回归底层硬件会带来一种罕见且直观的满足感。这就是 ymawky (yuh maw kee),一个完全用 ARM64 汇编编写的 Web 服务器。该项目专为 macOS 开发,是一个“仅使用系统调用、不使用 libc、每个连接一个 fork”的服务器——这一工艺成就既是一次技术练习,也是对现代软件工程抽象层的一种无声反抗。
用汇编编写 Web 服务器并非为了传统意义上的性能优化;而是为了理解应用程序与操作系统之间的基本契约。通过避开 C 标准库 (libc),作者直接与内核交互,通过原始的系统调用来管理从套接字创建到文件 I/O 的一切。
技术架构与功能
尽管是用汇编编写的,ymawky 并非仅仅是一个概念验证。它是一个功能完备的静态文件 Web 服务器,拥有一系列令人惊讶的功能,可以媲美专业实现:
核心功能
- HTTP 方法支持:它处理
GET、PUT、DELETE、OPTIONS和HEAD请求。 - 高级文件处理:服务器支持针对大量扩展名的 MIME 类型检测(从
.wasm和.webp到.opus和.docx),并实现了Range: bytes=请求,从而实现无缝的视频进度拖动。 - 原子上传:
PUT请求通过写入临时文件 (www/.ymawky_tmp_<pid>) 并仅在成功完成后才进行重命名,从而实现原子化处理,防止损坏的文件覆盖现有数据。 - 目录列表:如果请求的资源是一个目录,服务器会自动列出其内容,同时为了维护安全性,会排除根目录。
安全与鲁棒性
用汇编编写代码往往会引发内存安全担忧,但 ymawky 结合了几项“安全”措施来减轻常见的漏洞:
- 路径遍历预防:服务器明确拦截
..序列以防止攻击者逃离文档根目录,同时仍允许文件名中包含多个点(例如ohwell...txt)。 - DoS 缓解:为了抵御 Slowloris 类型的攻击,服务器对接收数据实施了 10 秒超时限制,并对完整的 HTTP 头部实施了总超时限制。
- 资源约束:它将最大并发进程数限制在 256 个,以防止 PID 耗尽,并拒绝超过
PATH_MAX(4096 字节) 的路径。 - 符号链接保护:服务器使用
O_NOFOLLOW_ANY来拒绝包含符号链接的路径。
可移植性的挑战
ymawky 最引人注目的方面之一是它与 macOS 内核的深度耦合。虽然作者的目标是实现可移植性,但实现细节揭示了类 Unix 系统在汇编层面的显著差异:
- 系统调用约定:macOS 使用
x16作为系统调用号,并使用svc #0x80,而 Linux 使用x8和svc #0。 - 错误处理:macOS 在出错时设置进位标志 (carry flag),并将
errno放在x0中,而 Linux 在x0中返回负值。 - 进程管理:
fork()行为有所不同,macOS 将子进程指示符放在x1中,而 Linux 使用x0。 - 重定位:该项目利用了 Mach-O 重定位操作符 (
@PAGE和@PAGEOFF),这与 Linux 的 ELF 格式不兼容。
或许最不寻常的选择是信号处理。ymawky 利用了 macOS sigaction 结构体中的 sa_tramp 字段,直接跳转到处理程序,完全绕过了 libc 的 trampoline 和 sigreturn——这种技术如果用于任何其他 POSIX 系统,都需要进行完全重写。
社区反馈
该项目在 Hacker News 社区引起了强烈共鸣,引发了关于现代编程状态的讨论。许多人将其视为 LLM 时代的一种“人类艺术形式”。
"我为人类艺术形式的消亡而哀悼... 十年前,我就会向足够精英的人致敬,因为他们能构建出这样的东西。今天,我只是在想,'LLM 需要多长时间才能写出这个?'"
其他人则指出了实际的性的权衡,指出虽然汇编语言提供了完全的控制权,但由于存在大量的边缘情况,它对于大规模 PC 应用来说本质上是不可持续的。正如一位评论者所言,你往往最终会构建出一种由宏和库组成的“元语言”,仅仅是为了让代码变得可维护。
关于 macOS 系统调用稳定性的技术警告也随之而来。正如一位用户提到的,Go 团队在 1.12 版本中著名的转向了不再直接使用系统调用,而是选择使用 libSystem.dylib,因为与 Linux 不同,macOS 的系统调用号并不保证是稳定的。
结论
ymawky 不仅仅是一个 Web 服务器;它提醒了人们“黑客”精神——即渴望构建一些困难的事情,不是因为它是通往产品的最高效路径,但因为构建它的过程能让人对机器提供更深的理解。在一个“氛围感编程” (vibe coding) 和智能体 LLM 的世界里,手动映射寄存器和管理栈帧的行为,仍然是寻找机器意义的一种强大方式。