第49期 | 万能门,开在正门旁边
一、万能门,开在正门旁边
read/write 装不下的东西,都从这里走
1979 年,Unix 第七版。ioctl 第一次出现在系统调用表里,一口气顶掉了 stty 和 gtty 两个老系统调用¹。
它的签名很怪:ioctl(fd, request, ...)。前两个参数人畜无害,第三个 request 是个数字——门牌号。门只有一扇,门后是几百个房间。
为什么需要这扇门?"一切皆文件"是 Unix 最漂亮的口号,可现实不答应:终端的波特率、窗口大小、网卡要不要进混杂模式——这些东西塞不进 read 和 write 的语义。读和写管的是字节流,设备要的是"调"。调字节流之外的东西,就得另开一扇门。
x86-64 上,这扇门的编号是 16;i386 上,是 54²³。门牌号都不统一——ABI 的铁律(第 43 期)管的是"门不能拆",没管"门牌号得一样"。
门牌号里还藏着说明书。1992 年,Linux 0.98p5 把 32 位编号切成了四段:2 位方向、14 位大小、8 位类型、8 位序号¹。比如 TIOCGWINSZ = 0x5413⁴:'T' 是终端家族,19 号房间,方向和大小都是 0——问窗口大小,不需要传数据,进门空手就行。这个解码是本机实测对过的。
还有个梗。敲错门,内核回你 ENOTTY,字面意思是 "Not a typewriter"——不是一台电传打字机⁵。最早只有 tty 设备会报这个错,四十多年了,错误码的名字里还留着电传打字机的味道。门认门牌,也认出身。
照见一层平时看不见的。硬约束:read/write 的语义是字节流,设备控制天生塞不进去——万能门不是设计偷懒,是语义不够用了。谁在买单:每个驱动自己发明编号,碎片化四十年。被大家默认的前提是错的:"好的接口应该干净完整"。不,干净的抽象和真实的世界之间,永远需要一扇不那么干净的门。
二、AI 的门,不该是万能的
如果先有 AI OS,扩展点长什么样
如果操作系统的第一批用户是智能体,ioctl 这条路走不通。agent 递进来的不是文件描述符,是意图:"把这路摄像头的分辨率调高"、"这个任务换个模型跑"。万能门的做法是再塞一个子命令进去,四十年后变成几千个房间,谁也记不全。
推演:AI OS 里没有万能门,只有"能力"。每个操作是一次类型化的意图声明:调什么参数、权限够不够、操作可不可逆、留不留审计。门后不再是黑盒 dispatch,而是一张看得见的契约表。
但也要诚实。ioctl 从 1979 活到今天,因为"未知"永远比"已知"多——设计时没想到的设备,永远会出现。AI OS 的万能门会换个名字:schema 化的扩展点。Lampson 在《Hints for Computer System Design》(1983)里早就写过这类权衡⁶(思想化用):接口要小,逃生口要留。
范式之眼补一句:这扇万能门,在 GPU 时代变成了主力通道。显卡驱动的大部分活——提交命令、分配显存——走的都是 DRM 的 ioctl。线性时代的后门,成了向量时代的正门。第 49 章再算这笔账。
三、今晚,敲一扇打不开的门:ENOTTY 是门在认门牌
手边任何一台 Linux 机器,三行 Python:
import fcntl, termios, array
buf = array.array('h', [0,0,0,0])
fcntl.ioctl(1, termios.TIOCGWINSZ, buf)
实测:OSError,errno 25,ENOTTY。输出没接终端,门牌对了,门不开——TIOCGWINSZ 只认终端。
再用 strace 看现场:
strace -e trace=ioctl python3 winsz.py
ioctl(1, TIOCGWINSZ, 0x...) = -1 ENOTTY (Inappropriate ioctl for device)
顺带会看到,Python 启动自己就敲了几十次 TCGETS 的门,全被挡了回来——解释器在挨个试:stdin、stdout、stderr,哪一扇背后是终端。
落点。做设备接入或智能体平台,扩展点迟早要开,ioctl 的教训现成:编号里写说明书(类型+序号+方向,一眼能解码);不认的门牌,给明确的错(ENOTTY 的精神:错要错得明白);再备一本子命令的账(文档,别让房间变成黑房间)。视频 AI 平台调设备参数、企业私有智能中心给智能体开新能力——开的都是同一扇门,纪律也该是同一套。
觉 然 小 注
是法平等,无有高下。read/write 不比 ioctl 高贵,万能门也不丢人——化用《金刚经》第二十三分"净心行善分"的思想⁷。
引 用 与 出 处
- [1] Linux man-pages 项目. ioctl(2)("The ioctl() system call appeared in Version 7 AT&T UNIX";NOTES 节编号结构:2 direction bits, 14 size bits, 8-bit type, 8-bit number,0.98p5 引入). http://man.he.net/man2/ioctl
- [2] arch/x86/entry/syscalls/syscall_64.tbl, Linux 5.15("16 64 ioctl sys_ioctl",x86-64 编号). https://ipads.se.sjtu.edu.cn:1312/opensource/copier-linux-5.15.131/-/blame/dbc0af056e5ec572cfd31fba461b9cfa6fd6b6bc/arch/x86/entry/syscalls/syscall_64.tbl
- [3] Linux/i386 system calls(i386 编号 "54 sys_ioctl"). http://asm.sourceforge.net/syscall.html
- [4] Linux 内核头文件 <asm-generic/ioctls.h>("TIOCGWINSZ 0x5413";<asm-generic/ioctl.h> 定义 _IO/_IOR/_IOW 编号宏). (无网址,随内核源码发布)
- [5] Ioctl. Wikipedia(ENOTTY:"'Not a typewriter' derives from the earliest systems ... where only the teletype (tty) device raised this error"). https://en.wikipedia.org/wiki/Ioctl
- [6] Lampson, B. W. Hints for Computer System Design. ACM SIGOPS Operating Systems Review, 17(5), 1983. (思想化用)
- [7] 鸠摩罗什 译. 《金刚般若波罗蜜经》第二十三分"净心行善分". 后秦弘始年间(约402年)译本. ("是法平等,无有高下"一句,思想化用)
· · ·