When using a serial port via POSIX, it's recommended to save the original attributes using tcgetattr()
before changing them with tcsetattr()
, and then restore them before closing the port. What about when a program is terminated by pressing control-C or when the program receives SIGINT
? I haven't seen this covered in any of the serial tutorials.
Apparently an atexit()
function wouldn't be sufficient, because it's not called by the default SIGINT
handler. So it seems installation of a signal handler would be necessary that restores the attributes to any serial ports still open. Is it even safe to call tcsetattr()
from a signal handler?
One might simply dismiss this issue a开发者_StackOverflow中文版s insignificant, but it's common to terminate a program with control-C, especially one that can take tens of seconds to complete operations. If it's OK not to preserve serial port settings in this case, then there seems little reason to preserve them at all. If anything, it might be better not to bother, rather than do it inconsistently.
I found some examples of source code doing the above, but nothing well-documented. I guess I'm interested in some discussion of whether this is a good idea. Thanks.
After further research I think I've answered this to my satisfaction.
First, in the man page for signal I noticed that a signal handler is specifically allowed to call tcsetattr()
, along with a few others:
The signal handler routine must be very careful, since processing elsewhere was interrupted at some arbitrary point. POSIX has the concept of "safe function". If a signal interrupts an unsafe function, and handler calls an unsafe function, then the behavior is undefined. Safe functions are listed explicitly in the various standards. The POSIX.1-2003 list is ... `raise()` ... `signal()` ... `tcsetattr()` [trimmed to relevant ones]
This strongly suggests that the POSIX committee had this exact kind of thing in mind, and leads to a straight forward approach where you change the SIGINT
handler once you've opened serial and saved its attributes, then in your handler, restore them and the old SIGINT
handler, then re-raise the signal:
static void (*prev_sigint)( int );
static termios saved_attr;
static int fd;
static void cleanup( int ignored )
{
tcsetattr( fd, TCSANOW, &saved_attr );
signal( SIGINT, prev_sigint );
raise( SIGINT );
}
int main( void )
{
open_serial_and_save_attrs();
prev_sigint = signal( SIGINT, cleanup );
...
}
精彩评论