如果你平时维护Linux服务器或者玩嵌入式,一定接触过getty这个命令,哪怕你从没注意过它。系统启动后能出现那个 login: 提示,背后靠的就是它。今天这篇是当前Linux命令系列的第六篇,我把getty从原理到配置、从手动调试到systemd服务固化完整走一遍,重点放在实操和排坑,希望能给你一些能直接落地的经验。
很多人遇到串口终端没提示、登录界面反复闪、一登录就崩这些情况,第一反应是查网络、查SSH,绕了半天才发现是getty的问题。所以我这次索性把getty单独拉出来讲透,适合系统运维、嵌入式开发、以及任何需要跟串口控制台打交道的人参考。
1. getty到底是什么?先从登录流程说起
1.1 一次完整登录流程中的getty角色
getty这名字是 get tty 的缩写,字面意思就是“去获取一个终端设备”。它是系统在某个tty终端上等待用户输入的守护进程。系统启动时,负责拉起服务的一层(早期是init,现在是systemd)会在每个需要登录的终端上启动一个getty。
整个登录链路可以拆成五步:
- getty打开终端设备文件,比如
/dev/ttyS0或/dev/ttyUSB0; - getty设置线路参数,比如波特率、数据位、停止位、流控方式;
- getty输出登录提示文件
/etc/issue,然后等待用户输入用户名; - 用户输入用户名后,getty把用户名交给
/bin/login程序; - login程序完成密码验证和会话初始化,成功后启动用户的shell。
一个特别容易混淆的点是:getty本身不验证密码。真正的密码校验发生在login程序里,现在很多Linux环境还会通过PAM这类身份认证模块做更复杂的验证。getty只负责把用户引导到login这道门前。理清楚这一点,排查“输完用户名后没反应”这类问题时,才不会把锅乱扣到getty头上。
用户注销后,shell退出,login跟着退出,getty这个进程也会结束。但systemd或init会立刻检测到终端重新空闲,然后再次spawn一个新的getty,回到等待输入的初始状态。这就是为什么每次登录界面出现时,agetty的PID都可能不一样。getty不是靠自身常驻,而是靠父进程不断拉起,这一点跟传统常驻服务有很大区别。
用个类比帮你记忆:系统里的进程像酒店团队,内核是老板,systemd是前台主管,getty就是门口叫号的接待。接待打开门,递上入住须知(issue),问你要身份证信息(用户名),然后把你的信息转给柜台login去办手续。入住成功,接待就去叫下一个客人。
1.2 getty家族的三个主要成员
如今你在系统里用ps多半看到的是agetty,而不是名字就叫getty的程序。不过getty历史上算是一个家族,不同环境选用过不同实现。主要认识这三个:
| 命令 | 特点 | 适合场景 |
|---|---|---|
| agetty | 功能最全,支持串口、虚拟终端,选项丰富 | 现代Linux发行版的默认选择 |
| mingetty | 非常轻量,只支持本地虚拟控制台,配置简单 | 老系统或极简环境 |
| mgetty | 面向调制解调器和传真,支持拨号、PPP等 | 拨号网络、串行猫等老场景 |
agetty是这几兄弟里最值得学的,因为systemd环境中的 getty@.service 和 serial-getty@.service 默认都在调用它。它支持多种终端类型、波特率列表、硬件流控、自定义issue文件等。比如前面提到的 -L 选项,就是告诉agetty忽略调制解调器的载波信号,直接跑在本地直连线路上,这是串口调试最常用的参数之一。
mingetty在一些轻量级系统里还能看到,它默认认为对面就是本地VGA虚拟终端,所以不要指望它去配置串口参数。mgetty则是老拨号时代的产物,能识别传真音、拨号音,还能拉起PPP链路,现在很少出现在常规环境里,但如果你在某台老设备上遇到,要能认出来它本质上也是getty。
这里提醒一句:网上很多教程把getty、agetty、mingetty混着叫,但它们的参数并不完全通用。比如mingetty根本不认识 -L。所以后面讲命令行时,除非特别说明,一律以agetty为准。
1.3 getty和“网络通讯”的渊源
系列标题把getty放在网络通讯这一类里,有人可能会疑惑:一个终端登录进程,跟网络有什么关系?实际上getty处理的本来就是“通讯”——主机和终端之间通过串行线路建立的通讯。早年Unix终端没有图形界面,只有一根串口线把键盘显示器连到主机,getty在主机端负责这条线路上的登录事务。串口在当时就是远程通讯的一种主要方式。
后来网络远程登录协议普及,人在办公室就能连到机房服务器,getty看起来不再那么显眼。但有两个场景到今天依然离不开getty。第一个是带外管理:不少服务器有独立的管理接口,通过串口输出一个console,就算操作系统网络栈完全挂了,你依然能从串口连上去救急,这个console背后跑的就是getty。第二个是嵌入式开发:开发用的路由器、开发板、交换机通常保留一个UART调试串口,系统启动后由getty在上面提供登录shell。所以说getty并没有消失,只是从“日常远程登录”退位成了“兜底通道”。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. getty的“大脑”:配置和服务单元
2.1 老牌SysV init的inittab写法
看getty配置,先得知道它由谁拉起。在老式SysV init环境里,答案写在 /etc/inittab。这个文件每一行定义一项服务,基本格式是:
id:runlevels:action:process
比如给串口终端配一个getty,可以写成:
bash复制S0:12345:respawn:/sbin/agetty -L 115200 ttyS0
其中 S0 是记录id,12345 表示运行级1到5都生效,respawn 是核心动作——进程即使正常退出,init也会立刻重新把它拉起来。最后那部分是完整命令行。
为什么必须用 respawn?因为getty正常会在登录会话结束后退出,只有配置了respawn,才能继续提供下一次登录提示。如果误写成 once,系统启动后这个终端只能登录一次,之后永远哑掉。这类配置在一些最小化系统、旧内核或嵌入式环境里仍然存在。如果你用的是systemd发行版,直接改inittab通常不会生效,判断方式很简单:看getty进程的父进程是systemd还是init,或者查一下 /sbin/init 指向哪里。
2.2 systemd时代的serial-getty服务模板
现代Linux环境管理getty的方式,已经从“手动写inittab”变成了“服务模板实例”。systemd主要用两个模板管理终端登录:
getty@.service:给本地虚拟控制台(tty1~tty6)使用;serial-getty@.service:专门给串行终端(ttySx、ttyUSBx、ttyAMAx)使用。
模板名里 @ 后面的参数就是终端设备名。比如 /dev/ttyS0 对应服务实例 serial-getty@ttyS0.service。这里有个容易踩的坑:不要拿 getty@ttyS0.service 去启动串口终端。serial-getty 模板对串口做了特殊处理,比如默认波特率列表以及串口环境下不清屏等行为,用错了会产生各种奇怪表现。
查看服务状态用:
bash复制systemctl status serial-getty@ttyS0.service
启动并设置开机自启用:
bash复制systemctl enable --now serial-getty@ttyS0.service
如果你要自定义agetty启动参数,不要直接修改 /usr/lib/systemd/system/ 下的模板文件,否则系统升级会把你改的东西覆盖掉。正确做法是用 systemctl edit 创建drop-in片段:
bash复制systemctl edit serial-getty@ttyS0.service
写入:
ini复制[Service]
ExecStart=
ExecStart=-/sbin/agetty -L 115200 ttyS0 vt100
注意第一行 ExecStart= 不能漏。systemd默认允许一个服务配置多个ExecStart,但agetty必须独占进程,所以先用空的 ExecStart= 清掉模板里的原始命令,再写你自己的那行。命令开头的 - 表示这个命令即使失败,也不会让服务直接进入failed状态,很适合终端这类会话型程序。
2.3 登录提示语和欢迎语别乱改
getty在提示用户名之前,会先输出一个文件,默认是 /etc/issue。很多Linux系统安装后会把系统名称写在这里。issue里的反斜杠转义序列由agetty自己解释,常见的有 \d(日期)、\t(时间)、\n(主机名)、\l(终端名)。比如你写:
text复制这是一个登录入口
主机名:\n
终端:\l
日期:\d
用户登录前就会看到对应的信息。如果不想用默认文件,agetty有个 -f 参数可以指定其他文件。注意mingetty不支持这么细的转义,这类玩法基本只适用于agetty。
另一个容易混淆的文件是 /etc/motd。它在登录成功之后由login程序读取并显示,经常被运维用来放维护通知、系统备份提醒等内容。简单整理:issue在登录前显示,motd在登录后显示,两者别弄混。有些新手把重要通知写进issue,结果用户一登录提示就被冲走,根本没看到;正确做法是放motd。
3. 实操:从零配置一个串口getty终端
3.1 环境准备,先确认终端设备和权限
下面进入实操。我假设你有一台带串口的Linux设备,可能是主板自带的ttyS0,也可能是USB转串口芯片生成的ttyUSB0。第一步,让系统把设备列出来:
bash复制ls -l /dev/ttyS* /dev/ttyUSB* /dev/ttyAMA*
设备文件存在后,再确认当前串口参数:
bash复制stty -F /dev/ttyUSB0 -a
重点看 speed、csize、cstopb、parenb 几项。常规串口参数是“8N1”:8位数据位、无校验、1位停止位。如果之前被别的程序改过,可以用stty先归位:
bash复制stty -F /dev/ttyUSB0 115200 cs8 -cstopb -parenb -crtscts
这条命令把波特率设为115200,并显式关闭硬件流控。为什么要关流控?很多调试场景只连了TX、RX和地线,根本没接RTS和CTS。如果串口流控还开着,getty挂上去后会一直等待对方的流控信号,结果就是“什么输出都没有”。这个坑特别隐蔽,后面排错还会提到。
权限方面,串口设备通常属于dialout组,普通用户没有写权限。不想每次都用sudo,可以把当前用户加进组:
bash复制sudo usermod -aG dialout 当前用户名
加了之后需要重新登录会话才生效。如果你只是临时调试,直接用sudo也是没问题的。
3.2 不依赖systemd,手动起一个agetty做测试
配置服务之前,建议先手动跑一次agetty,这样测试最直观,报错也能直接看到。命令:
bash复制sudo /sbin/agetty -L 115200 ttyUSB0 vt100
参数含义:
-L:本地直连线路,不检测调制解调器载波信号;115200:波特率,也可以写成115200,9600这种列表,让agetty逐个尝试;ttyUSB0:终端设备名;vt100:终端类型,对老串口终端最通用。对面是彩色终端时,也可以设成linux或xterm。
执行后,agetty会一直占住这个终端。此时用另一端的串口调试工具连过去,应该能看到issue文件和 login: 提示。测试完按 Ctrl+C 停掉agetty,再进入下一步。如果看到 Device or resource busy,说明串口已被其他程序占用,最常见的是另一个串口工具,也可能是modemmanager这类服务在做拨号探测。先找到占用者并停掉,再重新执行agetty。
这里补充一点:手动起agetty时,如果当前串口设备名或参数不对,命令会立刻在终端上输出错误。这一步能帮你把“getty问题”和“系统服务问题”分开。
3.3 用systemd固化串口会话
手动测试通过后,就该把会话交给systemd来管理了。启用命令:
bash复制sudo systemctl enable --now serial-getty@ttyUSB0.service
enable --now 等于enable和start同时执行,既设置开机自启,又立刻启动。如果设备名是ttyS0,就把ttyUSB0换成ttyS0。启用后看一下运行状态:
bash复制sudo systemctl status serial-getty@ttyUSB0.service
正常情况下状态是 active (running),命令行里能看到 /sbin/agetty。如果agetty需要自定义参数,按前面说的方法用 systemctl edit 覆盖ExecStart即可。
改完配置后,需要执行:
bash复制sudo systemctl daemon-reload
sudo systemctl restart serial-getty@ttyUSB0.service
daemon-reload 是必须的,systemd只有重新加载配置后才认识新写的drop-in文件。很多人改完配置不重启服务,还抱怨不生效,十有八九就是漏了这步。
3.4 完整登录实测与日志观察
配置完成后,我们要真正连一次。另一端打开串口调试终端,设置成同样的115200 8N1参数。正常情况下,你会看到类似这样的画面:
text复制这是一个登录入口
主机名:dev-box-01
终端:ttyUSB0
登录:admin
密码:
Linux dev-box-01 5.10.0-...
Last login: ...
从“登录:”开始,登录流程就由getty转交给了login。密码验证通过后,进入shell,说明整条链路已经通了。此时回到系统里看进程:
bash复制pgrep -a agetty
会看到agetty还在运行,但它的控制权已经交给了login。这个现象不用担心。如果登录中途失败,不要急着怪getty,先翻服务日志:
bash复制sudo journalctl -u serial-getty@ttyUSB0.service
日志里能看到getty每次被spawn,以及是否成功启动了login。密码错误、用户不存在这类认证失败的信息,通常记录在认证日志里,getty日志里看不到。做一个简单分工:getty日志负责回答“链路通没通”,认证日志负责回答“验证成没成”,排查时不要混在一起看。
4. 常见问题与排查技巧记录
4.1 串口乱码或看不到登录提示
这是最常见的两类问题。乱码多半是波特率不一致,或者数据位、校验位不匹配。比如对端是9600,agetty却按115200跑出来,自然是满屏乱码。其实看到乱码反而是好消息,说明物理链路通了。这时候不要急着换线,先核对两边参数。
bash复制stty -F /dev/ttyUSB0 -a
检查客户端软件的波特率、数据位、停止位、校验位设置。如果agetty启动后什么输出都没有,客户端参数也没问题,那就要怀疑硬件流控没关闭——很多USB转串口模块根本没接RTS/CTS线,流控却默认开启。解决办法是在agetty启动参数里加上 -crtscts,或者先用stty关闭流控。
另一个隐藏较深的坑是USB转串口设备枚举时机。某些芯片在系统启动很久之后才被驱动识别,systemd服务启动太早,agetty打开设备失败后可能不会再自动尝试。这时可以给服务配置 Restart=on-failure 或 RestartSec=1,让getty等在设备出现后自动拉起。
4.2 getty反复重启,登录界面一闪一闪
如果你看到agetty进程反复退出、systemd不断拉起,大概率是启动命令里有问题。最常见的是用 -l 参数指定了一个不存在的登录程序路径,导致agetty无法启动login。另外,同一个串口被两个服务实例同时启用,也会出现端口占用后反复重启的情况。
排查步骤:
systemctl status serial-getty@ttyUSB0.service,看Active字段是否显示auto-restart;journalctl -u serial-getty@ttyUSB0.service,看有没有关于login program的报错;- 用
lsof /dev/ttyUSB0确认没有其他进程占用同一设备。
如果是系统其他实例抢占了设备,先停掉对方,再重启目标服务。始终记住,getty本身就是“一退就被拉起”的进程,关键是搞清楚它为什么退。
4.3 本地虚拟终端开太多,拖慢低配机器
桌面版Linux上,系统默认会开多个 getty@ttyN.service,常见的是tty1到tty6。但对于纯服务器或串口调试设备来说,这些本地终端可能完全没用,还额外占用内存和进程数。只保留一个本地登录口,可以把多余的全停掉:
bash复制sudo systemctl disable --now getty@tty2.service
sudo systemctl disable --now getty@tty3.service
sudo systemctl disable --now getty@tty4.service
sudo systemctl disable --now getty@tty5.service
sudo systemctl disable --now getty@tty6.service
注意别把tty1停掉,那是默认的本地登录界面。如果你用的是串口服务器,本地tty服务甚至可以不启动,但关闭前一定确认有其他可用的登录方式,别把自己锁在门外。
4.4 嵌入式调试口的agetty坑
嵌入式开发板上配getty,和x86服务器差别很大。首先设备名常见的是 ttyAMA0 或 ttyS0,如果不确定,可以通过 dmesg | grep tty 看内核枚举结果。其次,很多开发板的调试口是TTL电平,不是RS232,不能直接接电脑串口,必须先过电平转换模块。硬件问题不解决,getty怎么配都是白搭。
软件上两个高频坑:第一个是启动参数没加 -L,因为调试口根本没接调制解调器控制线,agetty会一直等待线路就绪,表现为板子没有输出。第二个是内核把同一块串口同时当成console和getty终端,导致启动日志和登录提示混在一起。如果只想当登录终端,可以在内核cmdline里去掉 console=ttyAMA0,115200;如果一定要保留内核console,就需要接受输出交错,但一般不影响登录功能。
4.5 快速测试提示语的一个小技巧
最后分享一个调试时很省事的小技巧。想测试自定义issue文件,又不想动系统的 /etc/issue,可以用agetty的 -f 参数指定临时文件:
bash复制sudo /sbin/agetty -L 115200 -f /tmp/myissue ttyUSB0 vt100
把测试内容写到 /tmp/myissue 里,另一端串口连过去就能直接看到效果,完全不影响系统默认配置。这在调嵌入式设备出厂提示语时特别好用,改完即时生效,不用重启服务。确认满意之后,再把内容落到 /etc/issue 里。
这几年代维生涯里,我围着getty踩过的坑不算少,但核心经验就一条:不要一上来就怀疑getty配置。好多次我花半小时翻systemd日志,最后发现只是客户端波特率选错。所以排查顺序永远是:先确定终端链路本身是通的,再核对两端参数是否一致,最后才进服务配置里找原因。先把链路问题搞定,再谈登录业务。这也是这篇实操最想传递的思路。下次遇到终端登录问题,希望你能少走几步弯路。
