当使用QSerialPort
的Qt应用程序遇到非干净closures(例如由于接收和不处理SIGINT
)时,串行端口的文件描述符如何受到影响?
运行一个在/dev/ttyS0
上打开一个QSerialPort
的应用程序,然后用Ctl-C
退出,我发现cat < /dev/ttyS0
立即返回(不打印任何东西),而不是等待数据(正如通常那样)。
我期望如果这是由于打开的文件句柄留在左右,它会出现在lsof
的输出中,但是lsof | grep ttyS0
lsof | grep ttyS0
什么都不返回。 (我真的不知道如何search特定文件描述符上的句柄。)
我意识到这是一个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
在POSIX下,终端设备(即串口和伪终端)具有大量的设置 ,使计算机能够说出存在或已存在的基本RS-232协议的众多变体。 当恐龙和电传打字机 (因此“tty”)统治地球的时代,这个API的设计很多,而且我们也不会以同样的方式再做,但是现在我们一直在坚持下去。
终端设置是持久的; 一旦一个程序设置他们,他们保持这种方式,直到另一个程序改变他们。 命令行实用程序stty
可以打印或更改这些设置; stty sane
重置他们全部“合理的”违约; stty -a
把它们全部打印出来。
以下是在我的计算机上应用stty sane
和QSerialPort
对串行端口所做的所有终端设置。 (那些只是一个秘密标签,可能在前面有一个破折号,是布尔标志;领先的破折号意思是“关”,没有领先的破折号意味着“开”)。
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
的后果;据推测, QSerialPort
的作者知道他们在做什么)。导致cat
立即退出是min = 0
,这(默认time = 0
)意味着“如果没有输入悬而未决,应该返回零字节”。 在正常情况下,从read()
返回的零字节表示文件结束 ,所以cat
立即退出,因为它认为已经传递了一个空文件。 (这种模式在O_NONBLOCK
之前可能已经发明了很多年O_NONBLOCK
。)
stty sane
是你要找的“恢复串口的方法”。 文档中没有QSerialPort::close()
,但是如果从你的SIGINT
处理程序调用的QSerialPort::close()
不能将终端恢复到原始状态,我会考虑Qt中的一个错误。 你也应该在收到SIGHUP
, SIGQUIT
, SIGABRT
, SIGTERM
,也可以说是SIGTSTP
, SIGTTIN
, SIGTTOU
(但是这更复杂,因为这不是致命的)。 确保恢复默认处理程序,然后重新提高信号,以便退出状态正确。