【问题标题】:QSerialPort effect on `/dev/ttyS*` after process end?进程结束后 QSerialPort 对 `/dev/ttyS*` 的影响?
【发布时间】:2015-05-12 13:05:13
【问题描述】:

当使用QSerialPort 的Qt 应用程序遇到非干净关闭(例如由于接收而不处理SIGINT)时,串行端口的文件描述符如何受到影响?

在运行一个在/dev/ttyS0 上打开QSerialPort 的应用程序,然后以Ctl-C 退出后,我发现cat < /dev/ttyS0 会立即返回(不打印任何内容)而不是等待数据(就像通常那样) .

我希望如果这是由于一个打开的文件句柄悬而未决,它会出现在lsof 的输出中,但lsof | grep ttyS0 什么也不返回。 (我不确定如何在特定文件描述符上搜索句柄。)

我意识到这是一个 XY 问题,因为我可以通过重写我的应用程序以正确处理 SIGINT 来完全避免这个问题,但我想更深入地了解这里发生的事情以及是否有一种在此状态下恢复串口的方法。


编辑:根据要求,这是strace cat /dev/ttyS0的输出:

execve("/bin/cat", ["cat", "/dev/ttyS0"], [/* 17 vars */]) = 0
brk(0)                                  = 0x91ce000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb76fb000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=72063, ...}) = 0
mmap2(NULL, 72063, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb76e9000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/i686/cmov/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\240o\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1446056, ...}) = 0
mmap2(NULL, 1460600, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7584000
mmap2(0xb76e3000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15e) = 0xb76e3000
mmap2(0xb76e6000, 10616, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb76e6000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7583000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb75838d0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb76e3000, 8192, PROT_READ)   = 0
mprotect(0x8054000, 4096, PROT_READ)    = 0
mprotect(0xb771a000, 4096, PROT_READ)   = 0
munmap(0xb76e9000, 72063)               = 0
brk(0)                                  = 0x91ce000
brk(0x91ef000)                          = 0x91ef000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=1534672, ...}) = 0
mmap2(NULL, 1534672, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb740c000
close(3)                                = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
open("/dev/ttyS0", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFCHR|S_ISVTX|0660, st_rdev=makedev(4, 64), ...}) = 0
fadvise64_64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
read(3, "", 32768)                      = 0
close(3)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?

这是stty -a -F /dev/ttyS0的输出:

speed 57600 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; flush = ^O; min = 0; time = 0;
-parenb -parodd 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

【问题讨论】:

  • 我可以告诉你,肯定没有一个打开的文件句柄挂在周围:当进程终止时以任何方式(SIGINT,@987654334 @,无论如何),内核会自动关闭它仍然打开的每个文件。但是,存在与终端本身相关的 状态,而不是在终端上打开的任何文件,这可能会持续存在。如果您可以在终端处于此状态时发布strace cat /dev/ttyS0 (NOT strace cat &lt; /dev/ttyS0) 的输出,以及在将终端放入之前和之后的stty -a -F /dev/ttyS0 的输出,这将非常有用这个状态。
  • @zwol 我已将其添加到问题中。
  • 好的,这确认终端处于某种不稳定的状态。请问stty -a -F /dev/ttyS0 的输出怎么样?
  • @zwol 添加。顺便说一句,波特率有多持久?它是持续到下一次重新启动(或直到它被明确设置为其他东西)的端口属性吗?
  • 我真的不知道,我只是和你一起在手册页中摸索。 stty -F /dev/ttyS0 sane 是否将终端恢复到 Qt 程序崩溃之前的状态?

标签: linux qt file-descriptor filehandle qtserialport


【解决方案1】:

在 POSIX 下,终端设备(即串行端口和伪终端)具有a whole bunch of settings,它使计算机能够说出现有或已经存在的基本RS-232 协议的多种变体。这个 API 的大部分是在恐龙和 teletypewriters(因此是“tty”)统治地球的时代设计的,我们不会以同样的方式再做一次,但我们现在坚持下去了。

终端设置是持久的;一旦一个程序设置它们,它们就会保持这种状态,直到另一个程序更改它们。命令行实用程序stty 可以打印或更改这些设置; stty sane 将它们全部重置为“合理”的默认值; stty -a 将它们全部打印出来。

以下是在我的计算机上应用stty saneQSerialPort 对您的串行端口所做的所有终端设置不同。 (那些只是一个神秘的标签,可能前面有一个破折号,是布尔标志;前导破折号表示“关闭”,没有前导破折号表示“开启”。)

  QSerialPort        stty sane    
----------------  ----------------
speed 57600 baud  speed 38400 baud
min = 0           min = 1         
clocal            -clocal         
-brkint           brkint          
ignpar            -ignpar         
-icrnl            icrnl           
-ixon             ixon            
-imaxbel          imaxbel         
-opost            opost           
-isig             isig            
-icanon           icanon          
-iexten           iexten          
-echo             echo            

许多QSerialPort 设置是异常的,因为在这种状态下连接到串行端口的面向行或面向文件的程序会出现异常。 (但是,它们非常适合知道它正在与串行端口通信并准备处理转动这些特定旋钮的后果的程序;大概QSerialPort 的作者知道什么他们正在这样做。)导致cat 立即退出的是min = 0,它(连同默认的time = 0)意味着“如果没有待处理的输入,read() 应该返回零字节。”在正常情况下,从read() 返回的零字节表示文件结束,因此cat 立即退出,因为它认为它已收到一个空文件。 (这种模式很可能早在O_NONBLOCK 之前就发明了。)

stty sane 是您正在寻找的“恢复串行端口的方法”。文档没有说任何一种方式,但是如果从您的 SIGINT 处理程序调用的 QSerialPort::close() 确实将终端恢复到其原始状态,我会认为这是 Qt 中的一个错误。你也应该在收到SIGHUPSIGQUITSIGABRTSIGTERM 以及可以说是SIGTSTPSIGTTINSIGTTOU 时执行此操作(但这更复杂,因为这些不是致命的)。确保恢复默认处理程序并在之后重新引发信号,以便退出状态正确。

【讨论】:

  • 好的,很好。由于我实际上只是一遍又一遍地重新运行 Qt 应用程序,而不是真正需要通过 cat 访问,看起来我什至不需要“恢复”路径;无论之前是否运行过 Qt 应用程序,每次都应该以相同的方式运行。由于让程序运行完成并调用close() 确实让cat 工作,我认为毕竟没有错误。感谢您的帮助!
猜你喜欢
  • 2014-09-07
  • 2022-01-25
  • 2019-08-19
  • 2014-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-06
相关资源
最近更新 更多