One character,
all the way to
a serial pin
一个字,
怎么走到串口线上

No kernel background needed. About five minutes to read. 不需要内核基础。这一页大约 5 分钟讲明白。

A A 串口线 the serial wire
Both ends of the trip sit on one machine. Between them sit three layers of kernel code. This page opens them one at a time. 这一趟的两头都在同一台机器上。中间那段是三层内核代码,这一页一层一层打开它。

What is the trip like? 这一趟像什么?

串口 serial portserial port one wire; bits leave it in single file 一根线,字符一位一位排着队出去

Your program only reaches the first window. Every layer below is handed the byte by the one above. 你的程序也只走到第一道窗口。往下每一层,都由上一层交给它。

It's like…就像……

With a parcel, you only reach the front desk. Every leg after that is one pair of hands passing to the next. 寄快递,你只走到前台。剩下的每一段,都是上一段的人交给下一段。

你敲的命令 your command 系统调用 the syscall tty 子系统 the tty layer 串口驱动 the serial driver 硬件 the hardware 1 2 3 4 THR TX 排进队列就返回 returns once queued
Five bands, four arrows — each crosses exactly one boundary, and none skips a band. The three screens below zoom legs 1, 3 and 4. Leg 2 — the kernel handing it to the tty layer — lives only here. The lavender block is the kernel. Violet is that one character, drawn at each place it passes. The landing point is THR, the transmit holding register; the pin that byte finally leaves by is TX. The dashed line is the return: write comes back once the byte is queued. At that moment it has not left the pin. 五条带,四段箭头,每段只跨一条边界,没有一段跳层。下面三屏放大的是第 1、3、4 段。第 2 段(内核把它交到 tty 层)只在这张图上。淡紫那块就是内核。紫色画的是同一个字符,它每经过一处就画一次。落点是 THR,也就是发送保持寄存器;这个字节最后从 TX 那根脚出去。虚线是返回:字节排进驱动的队列,write 就返回了,那时它还没出引脚。
1

Your program hands it in 你的程序把它递进内核

系统调用 syscallsyscall knock at the window, hand the slip in 敲一下窗口把条子递进去

A program cannot walk into the kernel; it hands the byte in at a window. 程序进不了内核,只能在窗口把字节递进去。

用户态 user space 系统调用 the syscall write A A 内核自己的内存 the kernel's own memory
The thick line is the user–kernel boundary, the only thick one on the page. The sign says write, the name of that syscall. Your character stays put; the kernel takes a copy. 那条粗线是用户态和内核的边界,全页只有它是粗的。墙上写的 write 就是这个系统调用的名字。你那个字符没动,内核抄走的是一份副本。
2

The tty layer finds the driver tty 层照名字找到驱动

驱动 driverdriver speaks this chip's own dialect 会说这台机器方言的那个程序

The tty layer owns terminal devices and picks the driver. tty 层管的是终端这类设备,它挑出该由谁接手。

tty 子系统 the tty layer 串口驱动 the serial driver pts/3 ttyUSB0 ttyS0 write 就返回了 write returns
Three pigeonholes, three kinds of terminal; ours enters ttyS0. tty is short for teletype, the old printing terminal. What the kernel matches is really the device number behind that name. The ringed row is the driver's send queue — about 4096 bytes, two pages of plain text. The dashed arrow is write returning, the moment the byte is queued. 架子上三个格子是三种终端设备,字符进的是 ttyS0 那格。tty 这三个字母来自老式电报打字机 teletype。内核照的其实是这个名字背后的设备号。黄圈那排是驱动的发送队列,常见 4096 字节,大约两页纯文本。虚线是 write 的返回:字节一排进队列它就回去了。
3

The driver writes the send register 驱动写进发送寄存器

The driver writes one byte to one address, and hands it over. Ordinary programs stop short of here; that takes a privileged door like /dev/mem, UIO or VFIO. 驱动往一个地址写一个字节,这个字节就交出去了。普通程序走不到这里,要 /dev/mem、UIO、VFIO 这类特权口子才行。

串口驱动 the serial driver 硬件 the hardware THR 移位器 shift register TX
There is no separate data port here. THR, the transmit holding register, is the way out — writing it IS the send. The shift register then pushes the bits out at the baud rate — bits per second on the wire. 115200 divided by 10 bits per byte is 11,520 bytes a second. One blink, about 0.1 s, carries over a thousand bytes. The wave is the letter A (0x41), 8 data bits, no parity, one stop bit. A low start bit, then 1,0,0,0,0,0,1,0 lowest bit first, then a high stop bit. A real 16550 also keeps a 16-byte hardware queue after THR; it is not drawn. 这种串口没有另外的数据口。THR 是发送保持寄存器,它这个地址就是数据出口,写它就是发送。之后是移位器的活:按波特率一位一位挤出去——每秒往线上推多少位。115200 除以每字节 10 位,是每秒 11520 个字节。眨一次眼约 0.1 秒,这根线就送得出一千多个字节。线上画的是字母 A(0x41),8 位数据、无校验、1 位停止。先一个低的起始位,再从最低位起送 1、0、0、0、0、0、1、0。最后是一个高的停止位。真芯片上 THR 后面常还有一个 16 字节的硬件队列,这里没画。