我正在尝试设置 getty 通过串行登录(主要作为实验)。
几乎任何配置都会发生同样的事情。如果我的默认 shell 是bash
,我在登录后会收到以下消息:
-bash: cannot set terminal process group (15297): Inappropriate ioctl for device
-bash: no job control in this shell
然后为了证明它不起作用,我不能使用ctrl+C来停止程序:
$ sleep 30
^C
而且似乎没有发出信号。
这些是我尝试过的配置:
我已经尝试过这两个命令
# copied from raspberry pi:
sudo /sbin/agetty --keep-baud 115200,38400,9600 ttyUSB0 vt220
# something else I read somewhere
sudo getty -L ttyUSB0 9600 vt100
# (I know I'm mixing and matching a lot of differences but the result is the same)
我作为客户端尝试过 screen 和 picocom。
我尝试过使用 rasberry pi 作为服务器,以及两台不同的 ubuntu 笔记本电脑。
我尝试过两个 FTDI、两个 RS-485 USB 适配器,以及 getty 端的内置 RS232 和客户端的 USB RS232。
我还尝试将默认 shell 更改为 sh 和 dash。我没有收到消息,但 ctrl+C 仍然无法按预期工作
有趣的是 - 当树莓派自动配置/dev/ttyAMA0
,并且它完全使用我放置的 getty 命令时,作业控制起作用了!
并且终端设置几乎相同。 (实际上除了-iutf8)
以下是 FTDI 连接和 picocom 运行的终端设置:
$ stty -a -F /dev/ttyUSB0
speed 9600 baud; rows 24; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D;
eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q;
stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W;
lnext = <undef>; discard = <undef>; min = 1; time = 0;
-parenb -parodd -cmspar cs8 hupcl -cstopb cread clocal
-crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr
-icrnl ixon ixoff -iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0
tab0 bs0 vt0 ff0
isig -icanon -iexten -echo echoe echok -echonl -noflsh -xcase
-tostop -echoprt echoctl echoke -flusho -extproc
$ stty -a -F /dev/ttyUSB1
speed 9600 baud; rows 0; columns 0; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D;
eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q;
stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V;
discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 hupcl -cstopb cread clocal
-crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr
-icrnl -ixon -ixoff -iuclc -ixany -imaxbel -iutf8
-opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0
cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo echoe echok -echonl -noflsh -xcase
-tostop -echoprt echoctl echoke -flusho -extproc
我究竟做错了什么?为什么它可以与树莓派上内置串口的内置配置一起使用?
答案1
区别不在于命令,而在于它们运行的环境。
通常 getty 直接从系统服务管理器 (init) 生成 - 在 systemd 中它是一个 .service,在 SysV 世界中它有一个 inittab 条目(而不是 init.d 脚本!)。这与从另一个终端生成的有几个区别:
首先,从终端启动的进程将其继承为它的“控制终端”,这是 shell 作业控制最重要的参数。您可以在ps aux
or ps -ef
– 服务进程中看到这一点,一开始没有 ctty,因此当 getty 打开指示的终端时,一旦 shell 运行,该终端就会成为其用于作业控制的控制终端。
但是从 xterm 启动的 getty 将继续将 xterm pty 作为其控制 tty,尽管它的输入/输出现在被路由到串行端口 – 虽然 getty 本身不介意,但 shell 会还继承错误的控制 tty,这将使作业控制变得不可能。
$ ps -C agetty
PID TTY TIME CMD
1136 tty1 00:00:00 agetty
14022 pts/22 00:00:00 agetty
^-- should be ttyS0!
控制终端定义/dev/tty;它定义了作业控制信号被发送到哪些进程;它定义了终端关闭后哪些进程将被终止(SIGHUP)。如果您的 shell 继承了与其 stdin/out tty 不同的控制 tty,则可能会发生各种奇怪的事情。
进程可以通过多种方式从其先前的终端分离,例如调用setsid()——传统上/etc/init.d脚本这样做是为了“守护进程”——但getty不会自动使用它们,因为它不期望以这种方式运行,所以它没有被编程。(有一个setsid
工具可以用来强制这种情况发生,但是你不应该也可以在这里使用它;你应该从一开始就以正确的方式做事。)
理想情况下你应该systemctl start serial-getty@ttyUSB0
并让getty在干净的环境中运行。如果需要自定义选项,最好使用自定义该服务,systemctl edit [--full]
而不是直接运行 getty。 (如果未使用 systemd,则编辑/etc/inittab
;它通常包含一个示例。运行telinit q
以重新加载 inittab。)
从 shell 启动的进程与通过服务管理器启动的进程之间还有许多其他相对较小的差异 - stdin/stdout/stderr 首先将作为您的终端启动(getty 将关闭并重新打开它们,但并非所有服务都会这样做);环境变量将从您的“sudo”继承; cgroup将被继承,这可能会影响您的资源限制;从 cgroups 中,将继承 systemd-logind 会话,并且不允许串行登录启动自己的会话);您的 SELinux 安全上下文(或 AppArmor 配置文件或 SMACK 标签)将被继承(如果正在使用); ETC。