进程结束后QSerialPort对`/ dev/ttyS*`的影响?

Kyl*_*and 5 linux qt file-descriptor filehandle qtserialport

当Qt app使用QSerialPort经历非干净关闭(例如由于接收和不处理SIGINT)时,串口的文件描述符如何受到影响?

在运行打开QSerialPorton 的应用程序/dev/ttyS0,然后退出之后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)                           = ?
Run Code Online (Sandbox Code Playgroud)

以下是输出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
Run Code Online (Sandbox Code Playgroud)

zwo*_*wol 3

Under POSIX, terminal devices (that is, serial ports and pseudoterminals) have a whole bunch of settings which enable the computer to speak the multitude of variations on the basic RS-232 protocol that exist or have existed. A great deal of this API was designed back in the days when dinosaurs and teletypewriters (hence "tty") ruled the earth, and we wouldn't do it over again the same way, but we're stuck with it now.

The terminal settings are persistent; once one program sets them, they stay that way until another program changes them. The command-line utility stty can print or change these settings; stty sane resets them all to "reasonable" defaults; stty -a prints them all out.

Here are all the terminal settings that differ between what stty sane applies on my computer, and what QSerialPort did to your serial port. (The ones that are just a cryptic label, possibly with a dash in front, are boolean flags; the leading dash means "off", no leading dash means "on".)

  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            
Run Code Online (Sandbox Code Playgroud)

Many of the QSerialPort settings are abnormal in the sense that a line- or file-oriented program hooked up to a serial port in this state will misbehave. (However, they are perfectly appropriate for a program that knows it is talking to a serial port and is prepared to deal with the consequences of having turned these particular knobs; presumably the authors of QSerialPort knew what they were doing.) The one that is causing cat to quit immediately is min = 0, which (together with the default time = 0) means "read() should return zero bytes if there is no input pending." Under normal circumstances, zero bytes returned from read() means end of file, so cat quits immediately because it thinks it's been handed an empty file. (This mode may well have been invented years before O_NONBLOCK.)

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