ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux TTY与串口控制台:彻底搞懂ttyS0、tty、console的区别

Linux TTY与串口控制台:彻底搞懂ttyS0、tty、console的区别 1. 先说清楚TTY 到底是什么搞懂 Linux 里这些终端设备最直接的办法不是去背设备表而是先想明白一个场景你拿一根 USB 转串口线把电脑和路由器或者嵌入式板子连起来打开 PuTTY 或者 minicom选择串口回车然后屏幕上出现了登录提示符。这个“登录提示符”能够出现背后就是 TTY 子系统在干活。TTY 这个缩写来自 Teletypewriter也就是电传打字机。几十年前计算机又大又贵操作员就是通过一台类似打字机的设备往机器里敲命令机器把结果打印在纸上。这个东西在 Linux 里演化成了一个抽象概念一套处理“字符输入输出”的机制。今天你打开一个终端模拟器按下一个键字符能进到 shell 里你在 shell 里敲一行命令输出能显示到屏幕上甚至你在嵌入式板子上按住回车u-boot 能中断启动、进入命令行——这些全部依赖 TTY。这篇文章不是什么目录式教程而是把我平时排查 Linux 终端、串口、控制台问题时最常用到的一整套知识梳理出来。重点就是四个特别容易搞混的名字ttys*其实是 ttyS*、tty*、ttyS* 和 console。我还会结合内核启动参数、systemd 的 getty 服务、串口调试这些实战场景把它们的区别和联动关系讲透。如果你是做运维、搞嵌入式、或者刚入门 Linux 玩虚拟机时被“tty1 黑屏”“串口没输出”折磨过这篇文章应该正好对你胃口。2. 设备节点和终端驱动先建立整体框架2.1 Linux 里“终端设备文件”到底是什么在 Linux 里一切皆文件终端也不例外。你想要跟某个“终端”交互本质上就是打开对应的设备文件往里写数据、从里面读数据。这些设备文件一般都在 /dev 目录下名字看起来差不多但背后的驱动、功能、使用场景差了十万八千里。设备文件分两类块设备和字符设备。终端属于字符设备因为它是按字符流处理的不像磁盘那样按块读写。你可以用下面命令看一眼ls -l /dev/tty1 /dev/ttyS0 /dev/console输出大致是crw--w---- 1 root tty 4, 1 ... /dev/tty1 crw-rw---- 1 root dialout 4, 64 ... /dev/ttyS0 crw------- 1 root root 5, 1 ... /dev/console开头的 c 表示字符设备后面两个数字比如 4, 1、4, 64、5, 1 是主设备号和次设备号。Linux 内核靠这两个数字找到对应的驱动。比如主设备号 4 是 TTY 设备通用主设备号次设备号 0 对应 /dev/tty01 对应 /dev/tty164 则开始对应 /dev/ttyS0主设备号 5 是标准设备次设备号 1 是 /dev/console。这里要强调一个观点设备文件名只是用户空间的“标签”真正决定行为的是主次设备号和内核驱动。所以即使在嵌入式系统里把串口设备命名成 /dev/ttyO0、/dev/ttyAMA0、/dev/ttySTM0原理还是同一套。很多人一看到新名字就慌其实只是平台驱动不同罢了。2.2 TTY 子系统的大致分层TTY 可以分成四层我按从下到上给你捋最底层是硬件驱动负责跟实际的串口、USB 转串口芯片、虚拟终端显卡驱动打交道。串口对应的驱动是 8250/serial 子系统虚拟终端对应 vt 驱动USB 转串口对应 usb-serial 驱动。内核的 tty 核心这套代码统一管理设备节点、line discipline线路规程、读写缓冲。line discipline线路规程这层非常关键。它负责在“原始字节流”和“规范模式”之间切换。默认是 n_tty它会把回车、换行、CtrlC、退格这些特殊字符处理成你熟悉的终端行为。如果你设置 raw 模式比如 stty raw那 CtrlC 就不会产生中断信号字符也不会做回车换行转换这串口传二进制数据时常用。最上层是用户空间的终端模拟器、getty/login 程序、shell以及你敲命令时读写的设备文件。知道了这个框架很多问题就好解释了。比如你往串口发一段中文乱码了多数不是 TTY 的问题而是终端模拟器的编码设置不对又比如你希望串口回显但输入字符却看不到那多半是 echo 标志被关了stty -F /dev/ttyS0 echo 可以打开。2.3 为什么这四个名字特别容易乱TTY 相关命名的混乱主要来自历史遗留和“需求不同但名字相似”。tty 是通用前缀几乎所有终端设备都带个 ttyttyS 是 Serial 的缩写专指传统串口console 又是另一个维度它是一个“在内核启动阶段就有输出的目标设备”的概念和登录终端不能画等号。很多人第一次接触是在嵌入式 Linux 的开发环境里。u-boot 启动参数写的是consolettyS0,115200然后你发现板子上电后串口软件里能看到内核日志也能登录 root于是你觉得 ttyS0 就是 console。后来你在 PC 上按 CtrlAltF2 进入黑底白字的字符界面登录进去敲 tty 命令输出是 /dev/tty2标题里说的 tty* 出现了。再后来你 SSH 到一台云服务器上敲 tty输出是 /dev/pts/0又冒出来一个 pty 概念。这三个场景都是“登录到一个终端”但底层设备完全不同一个是串口物理设备一个是虚拟终端显卡上的文本模式一个是伪终端网络会话模拟出来的。它们都在 TTY 子系统里面但角色不同。这就是混乱的根源。3. 逐个击破ttys*、tty*、ttyS*、console 的区别3.1 先纠正一个写法ttys* 其实就是 ttyS*标题里说的 ttys*规范写法是 ttyS*也就是传统 PC 串口对应的设备文件。在很多中文资料和论坛帖子里大家图省事会写成 ttys0、ttys1严格讲不对但大家一看就知道是在说串口。这里我统一用标准写法 /dev/ttyS0、/dev/ttyS1。在 PC 上ttyS0 通常对应主板上的 COM1ttyS1 对应 COM2ttyS2 对应 COM3依此类推。它们通过 8250/16550 这类 UART 驱动工作。注意很多新电脑根本没有物理串口所以 /dev/ttyS0 可能存在但打开之后收不到任何数据因为没有硬件连接到那里。嵌入式平台就五花八门了。树莓派早期是 /dev/ttyAMA0后来版本又出现 /dev/ttyS0 作为默认串口有些 SoC 用 /dev/ttyO0老的德州仪器平台、/dev/ttyMSM0高通、/dev/ttySTM0意法半导体等。这些都是 ttyS* 的“远方亲戚”用法一样名字不同。3.2 /dev/tty*虚拟控制台和“当前终端”/dev/tty1、/dev/tty2 这种带数字的是虚拟控制台Virtual Console也叫虚拟终端Virtual Terminal。在 PC 或服务器上有显卡和显示器时内核的 vt 驱动会在屏幕上提供多个独立文本界面默认 63 个不同内核配置可能不同。按 CtrlAltF1、CtrlAltF2 就能在不同虚拟终端之间切换。在大多数现代 systemd 发行版里tty1 通常给图形登录界面GDM、SDDM用所以你要切字符界面一般是 CtrlAltF2 到 F6。这里有个特殊节点/dev/tty0。它是“当前虚拟终端”的别名。什么叫当前比如你在 tty2 上登录了那么往 /dev/tty0 里写字符会输出到 tty2 的屏幕。这个特性对内核和系统程序很有用。比如当系统要打印一段信息到“前台终端”它不知道哪个 tty 在活跃只要写 /dev/tty0 即可。还有一个更特殊的是 /dev/tty。它表示“当前进程的控制终端”不是指某个固定设备。你在某个终端里跑了一个 shellshell 里的进程打开 /dev/tty得到的就是那个终端设备。如果某个进程没有控制终端比如由 init 或 systemd 直接拉起的守护进程或者从 cron 里跑的任务打开 /dev/tty 会失败报 No such device or address 或者 not a tty。这是判断脚本是否在“有交互终端的会话”里运行的一个经典手段。3.3 /dev/console内核控制台不是给普通用户用的登录入口/dev/console 是最容易误解的设备。很多人以为它是“系统默认登录终端”实际上它主要面向内核消息输出而不是登录会话。内核在启动早期需要输出日志或者系统 panic 时要打印信息总得有个出口。这个出口就是 console。通过内核启动参数 console 可以指定一个或多个控制台设备。常见组合consoletty0 consolettyS0,115200n8意思是内核日志同时输出到虚拟终端和串口。这个参数由内核的 console 子系统解析最后生成一个“内核控制台列表”。所有提交给 printk 的消息会挨个送到列表里的设备。用户空间的 /dev/console 节点就是“首选 console”的设备文件。早期内核只支持一个 console后面扩展成多个后约定最后一个 console 参数成为 /dev/console 的后端。这个细节在配置串口调试时很实用如果你希望系统启动后 shell 提示符出现在串口上同时又让虚拟终端也能看日志通常会把 consolettyS0 写在后面。但要注意/dev/console 并不是 getty 默认监听的设备。即使你在内核命令行里写了 consolettyS0系统起来后串口上也不会自动出现登录提示符除非 systemd 有对应的 serial-getty 服务在监听。很多人只配置了内核参数发现串口能看日志但输入 root 没反应原因就在这里。后面我会专门讲 getty。3.4 /dev/tty0、/dev/tty1、/dev/ttyS0、/dev/console 放一起对比设备节点类型背后驱动/机制主要用途常见场景/dev/tty0虚拟终端当前vt输出到当前活动的虚拟终端内核消息、系统前台输出/dev/tty1~tty63虚拟终端vt独立文本会话支持 CtrlAltF1~F63 切换本地登录、字符界面操作/dev/ttyS0串行端口8250/serial物理串口波特率可配嵌入式调试、路由器 console 口、串口终端/dev/ttyUSB0USB 转串口usb-serialUSB 转串口芯片开发板调试线、console 线/dev/console内核控制台console 子系统内核日志输出、早期引导输出printk、panic、启动日志/dev/pts/0伪终端从设备pty终端模拟器、SSH 会话远程登录、xterm、screen/tmux/dev/tty当前进程控制终端动态别名指向当前会话的终端设备shell 脚本判断交互终端这张表里我故意加了 ttyUSB0 和 pts/0因为它们在实际项目中实在太常见排查问题时常跟上面四个混在一起。比如你拿着一根 USB 转串口线去连路由器Linux 识别出来的往往不是 ttyS0而是 ttyUSB0。这时候如果照着网上教程配 ttyS0肯定连不上。4. 内核启动参数和 console 配置实操重点来了4.1 console 参数支持多控制台顺序有讲究现在说一下配置层面的核心知识。内核启动参数里可以多次写 console每个参数注册一个控制台设备。比如consoletty1 consolettyS0,115200 consolettyS1,9600这样内核日志会同时输出到 tty1、ttyS0、ttyS1 三个地方。实际嵌入式产品中最常见的就是“本地屏幕 串口”同时输出consoletty0 consolettyS0,115200为什么要把 consoletty0 写在前面、ttyS0 写在后面根据内核文档如果指定了多个 console只有最后一个 console 会被用作 /dev/console 后端而且 sysrq、printk 等机制在某些情况下会优先使用首选控制台。虽然新内核已经能支持多控制台同时输出但为了兼容性和可预期建议把“最希望被当作系统控制台”的设备写在最后。串口参数格式一般是consolettyS0,115200n8其中 115200 是波特率n 表示无校验No parity8 表示 8 个数据位。也有的写 consolettyS0,115200内核会按默认的 8N1 处理。常见的组合还有 9600n8、57600n8。老设备很多是 9600新开发的嵌入式板子基本上都是 115200。连不上时先别瞎调确认一下 bootloader 设置的波特率是多少两边不一致出来就是乱码或者一片空白。4.2 内核日志级别和 printk为什么有日志被“吃掉”console 配好了但你说内核日志太吵或者太安静这涉及 printk 的日志级别机制有很多人是不知道的。printk 消息有一个日志级别数字范围从 0 到 7数字越小越紧急。0 是 KERN_EMERG1 是 KERN_ALERT2 是 KERN_CRIT3 是 KERN_ERR4 是 KERN_WARNING5 是 KERN_NOTICE6 是 KERN_INFO7 是 KERN_DEBUG。内核只把“级别小于等于当前 console 级别”的消息输出到控制台。也就是说console_loglevel 当前是 4那么 0~4 的消息会显示级别 5~7 的调试信息就被丢弃到 console 之外了。查看方式cat /proc/sys/kernel/printk输出四个数字比如4 4 1 7第一个数字 4 是当前 console_loglevel第二个是默认级别第三个是最低级别第四个是启动时默认级别。用 dmesg -n 可以动态调整dmesg -n 8这样会把 console_loglevel 设为 8所有 printk 包括调试信息都会输出。嵌入式开发调试 UART 时如果怀疑串口驱动本身打印太少可以试这个。加内核启动参数 loglevel8 或者 ignore_loglevel 可以永久生效但真的非常吵生产环境不建议。4.3 systemd 和 getty串口上有日志但没有登录提示符这是排障时一个高频问题串口能看到内核日志但启动完成后按回车没有登录界面。原因多半是 getty 没有监听串口。getty 是“获取终端”的程序负责打开指定 tty 设备、显示 /etc/issue 文件内容、提示输入用户名然后调用 login 程序验证密码。systemd 把 getty 封装成了服务比如gettytty1.service 负责 /dev/tty1serial-gettyttyS0.service 负责 /dev/ttyS0手动启用串口登录systemctl enable --now serial-gettyttyS0.service如果要修改波特率直接编辑服务对应的 overridesystemctl edit serial-gettyttyS0.service在 [Service] 段里加上ExecStart ExecStart-/sbin/agetty -s ttyS0 115200第一行 ExecStart 是把原来的定义清空因为覆盖 ExecStart 时必须先清空否则会报错。这也算是一个很隐蔽的坑。如果你是要调试嵌入式系统想确认串口服务有没有起来可以看systemctl status serial-gettyttyS0 ss -lntp | grep 22上面第二个命令是看 SSH但串口是物理口不上 tcp/ip。更直接的验证方法ps -ef | grep agetty能看到类似 /sbin/agetty -o -p -- \u --noclear ttyS0 115200 的进程说明 getty 起来了。如果没有就是服务没启用。4.4 butter: UART 的硬件配置和软件配置别搞混在配置串口 console 时有两个层面要区分一是内核 console 参数二是终端设备的 termios 设置。console 参数由内核 console 子系统解析它会把波特率等参数传给串口驱动用于内核准许的注册阶段。而 systemd 起来后agetty 会打开同一个串口设备并用 stty 重新设置终端参数。如果两者波特率不一致会出现这种现象启动阶段内核日志正常显示等 getty 一起来串口反而乱码或者无响应。解决方法是保持两边一致。比如你给内核传了 consolettyS0,115200那么 serial-gettyttyS0 里的 agetty 也设置成 115200。用 minicom 或 screen 连接时下面的配置也要跟它一致。两边一旦都是 115200 8N1基本不会有问题。另外还有一个细节串口的硬件流控RTS/CTS默认是关闭的但如果你的串口线支持硬件流控而 console 配置里没有开启流控有时候会丢失数据。反过来如果接线没有 RTS/CTS却开启了硬件流控可能直接收不到输出。很多嵌入式板子默认关闭硬件流控没问题有的主板 BIOS/设备树里开启了自动流控容易莫名其妙少日志。排查时可以用 stty -F /dev/ttyS0 -a 看看当前参数其中 crtscts 字段表示硬件流控是否打开。5. 实战场景路由器 console 口、带外管理和 USB 转串口线5.1 console 口和 ttyS*网络设备里的“串口控制台”很多路由器和交换机都有一个 RJ45 或者 DB9 的 console 口本质就是一个 UART 串口只不过做成了网络设备通用的物理形态。它跟 Linux 里 /dev/console 的概念高度相似都是“系统控制台”即使设备系统没起来bootloader 阶段就能用。这也是为什么很多厂商设备忘记密码时必须靠 console 口去恢复——因为网络接口在系统起来前根本不可用只有串口这种底层通道能接通引导程序。你用 USB 转 console 线把它连到电脑上Linux 识别出来的设备大概率不是 ttyS0而是 ttyUSB0。为什么因为 USB 转串口芯片常见的有 CH340、CP2102、PL2303、FT232会被 usb-serial 驱动识别注册成 USB 串口设备。如果你插上后 ls /dev/ttyUSB* 没有任何输出先用 dmesg | tail 看看内核有没有识别到 USB 设备再检查是不是驱动没装。注意市面上那些“console 线”的驱动力在 USB 转串口芯片上而不是线材本身。绿联、惠普之类品牌的线用得最多的还是上面几种芯片。芯片驱动在 Linux 内核里基本都是自带的不像 Windows 那么需要手动装。如果 dmesg 里看到 ch341、cp210x 之类的名字说明驱动已经加载了。5.2 带外管理口和 console 重定向服务器上经常看到两个概念“带外管理口”和“串口重定向”。带外管理口比如服务器 BMC 上的专用管理网口可以远程开关机、挂载镜像、查看屏幕这些功能通过专用管理硬件实现不依赖操作系统。很多管理卡还能把系统的串口 ttyS0 重定向到远程会话里让你在网页上看到一个“虚拟串口”。这个时候你在网页控制台里看到的其实就是 Linux 的 serial console。在系统启动早期你甚至可以通过管理卡的 Console 重定向功能修改 grub 启动参数。有些初次接触的人会把“BMC 的管理网口”和“console 口”当成一回事。严格讲管理网口是带外通道console 口是系统控制台通道但现代服务器往往通过管理卡把两者整合进同一个 Web 界面。理解底层思路后你只需要记住只要 Linux 把串口当作 console 来用不管是物理串口线连的还是管理卡虚拟出来的操作方式都一样——请看内核 console 参数、serial-getty 服务这些。5.3 连接串口时推荐哪几个终端软件不管你是连交换机、路由器还是嵌入式板子Linux 下终端串口软件选择就那么几个我实测下来最常用的三个minicom老牌工具配置繁琐一点功能全面。screen很多人不知道它可以当串口终端用一条命令相当快。PuTTYWindows 流行Linux 下也有其实也很好用。比如用 screen 连串口screen /dev/ttyUSB0 115200退出的时候按 CtrlA 然后输入 K再按 y 确认。如果用 minicom先初始化配置minicom -s在 Serial port setup 里填设备名、波特率、流控保存为默认配置。之后每次 minicom 直接打开就行。如果连到 console 口要恢复路由器密码很多厂商的流程是开机瞬间持续按 CtrlB不同厂商不一样可能是 CtrlC、Esc 之类进入 bootloader然后通过命令清配置或跳过密码。这一步必须得通过 console 口进入所以串口软件稳定很重要。6. 排查技巧和避坑经验6.1 串口没输出的三层排查法遇到“串口没输出”我一般按三层来查第一层是物理连接和链路层。线有没有接对TX 和 RX 是不是交叉了。很多 USB 转串口线本身就是交叉好的但还分“直连线”和“交叉线”对不上就完全没反应。另外电平ttl 电平串口和 rs232 电平串口不兼容拿 USB 转 TTL 的线去接 RS232 口大概率烧不坏但也没数据。第二层是设备节点和驱动层。插上 USB 转串口线后dmesg 里有没有 ttyUSB0嵌入式板子 ttyS0 有没有被内核注册。用 ls -l /dev/ttyS* /dev/ttyUSB* 看看设备在不在。权限不对也常导致“能打开但没反应”查看串口设备归属于哪个组ls -l /dev/ttyUSB0一般归 dialout 或 uucp 组当前用户不在组里就需要 sudo或者把用户加进组。加组后重新登录才生效。第三层是软件配置层。波特率、数据位、校验位、停止位、流控。在嵌入式调试里最常见的组合是 115200 8N1但老一点的路由器可能是 9600 8N1还有少量是 19200。如果启动日志根本不出现先把波特率逐个试一遍最稳的是 9600 和 115200 两个值来回切。如果看到乱码那说明波特率大概率已经对上但字节相位不对往往是波特率偏差或者是 CPU 主频漂移严重此时可以把波特率降低到 9600 再试。6.2 TTY 权限问题的快速解决这里单独把权限拎出来说。报错信息一般是Permission denied: /dev/ttyS0或者could not open /dev/ttyUSB0这是因为设备文件权限只允许 root 和 dialout 组成员访问。解决办法sudo usermod -aG dialout $USER然后注销重新登录或者直接使用 sudo 临时访问。不要图省事去 chmod 777 /dev/ttyUSB0这样不安全而且重新插拔后设备节点权限又会恢复不解决根本问题。6.3 getty 服务反复重启的陷阱有时候你启用了 serial-gettyttyS0.service但发现服务状态是 failed 或者在一直重启。最典型的原因是这个串口已经被另一个程序占用了比如内核 console 打开了同一个设备或者你已经开了一个 minicom/screen 占着它。串口是独占设备同一时间只能有一个进程读取。内核 console 在早期注册后并不长期独占文件描述符但如果你用了类似 agetty 的 keep 选项它会一直占用。排查命令是systemctl status serial-gettyttyS0.service如果报 Device or resource busy说明有别的进程占用了。用 fuser 可以看到谁在访问这个设备fuser -v /dev/ttyS0必要的时候把多余进程关掉。实际调试时我经常是开了一个 screen 占住串口然后又顺手去 systemctl start serial-getty结果怎么都不成功就是被自己占住了。这是很典型的自踩坑。6.4 常见问题速查表现象可能原因排查/解决方向串口完全没有输出接线错误、电平不匹配、USB 转串口未识别检查 dmesg、lsusb、TX/RX 是否交叉启动日志有但无法登录getty 未配置systemctl enable --now serial-gettyttyS0日志乱码波特率不对、校验位/停止位不一致试 9600/115200、检查 8N1能登录但键盘输入不回显终端回显被关闭stty -F /dev/ttyS0 echoCtrlAltF1 无法切换到字符界面图形界面占用 tty1或没有显卡驱动试 F2~F6检查 vt 驱动看不到 /dev/ttyS0内核未启用串口驱动或设备树配置错误dmesg 看 uart 是否注册检查设备树/dev/tty 打开失败进程没有控制终端确认是否在交互终端里运行用 script 分配 ptymini 网络设备 console 密码忘了需要进入 bootloader/紧急模式接 console 口开机中断引导恢复默认配置“must be run from a terminal”当前会话没有 ttySSH 用 -tcron 任务里改用 setsid/script 解决这个表覆盖了我平时被问到的 90% 问题。如果你遇到“串口连上了但内核日志比预期短很多”还要检查 loglevel 是否为默认值结合前面讲的 /proc/sys/kernel/printk 一起看。如果“initramfs 阶段有输出但切换 rootfs 之后没输出”可能是内核把 console 移交给了用户空间后initrd 里的 udev/systemd 把控制台设备重新配置成了 /dev/console或者 getty 配置里设备名写错了。这些在长期维护嵌入式 BSP 时会遇到。7. 最后分享几个我自己长期在用的习惯整套 TTY 东西看下来我最大的体会是不要把 tty、console、串口当成三个割裂的概念去背而是把它当成一条链路。从硬件 UART 到 tty 驱动再到 line discipline再到 getty/login再到你的 shell是一条有序的通道。ttyS0 是链路底层的物理入口console 是内核在上面贴的一个“日志出口”标签getty 是一个坐在这里等用户的程序。想通了这条链路绝大多数配置问题都能自己推出来。我自己调试嵌入式板子时内核命令行必加两样consolettyS0,115200 和 root/dev/mmcblk0p2 之类然后把 printk 级别用 dmesg -n 8 调到全开串口日志看得越多越容易定位问题。产品量产时再把 loglevel 调回默认避免调试消息刷屏导致串口性能下降。另外每次换新开发板先别急着跑系统先建立“串口通路”的概念检查用屏幕和串口同时作为 console万一屏幕驱动坏了串口也能把现场信息拉出来这是一种极其重要的调试保险。如果你刚入门建议找一个有串口的板子哪怕没有虚拟机里也能模拟一个 ttyS0 进行练习。配好 console 参数以后用 screen 连上看着启动日志一行行刷出来那种“系统控制台”的概念一下子就有实感了。祝各位踩坑愉快少走弯路。
返回列表