即使在control-C之后也能恢复串行端口属性吗?

发布于 2024-09-28 16:29:15 字数 588 浏览 5 评论 0原文

通过 POSIX 使用串行端口时,建议先使用 tcgetattr() 保存原始属性,然后再使用 tcsetattr() 更改它们,然后在关闭端口之前恢复它们。当程序通过按 control-C 终止或程序收到 SIGINT 时会怎样?我还没有在任何系列教程中看到过这一点。

显然 atexit() 函数是不够的,因为它不是由默认的 SIGINT 处理程序调用的。因此,似乎有必要安装信号处理程序来恢复任何仍然打开的串行端口的属性。从信号处理程序调用 tcsetattr() 是否安全?

人们可能会认为这个问题无关紧要,但使用 control-C 终止程序是很常见的,尤其是那些可能需要数十秒才能完成操作的程序。如果在这种情况下不保留串行端口设置是可以的,那么似乎根本没有理由保留它们。如果有的话,最好不要打扰,而不是不一致地做。

我发现了一些执行上述操作的源代码示例,但没有任何详细记录。我想我有兴趣讨论这是否是一个好主意。谢谢。

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 as 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

在风中等你 2024-10-05 16:29:16

经过进一步的研究,我想我已经满意地回答了这个问题。

首先,在信号手册页中,我注意到信号处理程序专门允许调用tcsetattr() 以及其他一些:

信号处理程序例程必须非常小心,因为其他地方的处理在某个任意点被中断。 POSIX有“安全函数”的概念。如果信号中断不安全函数,并且处理程序调用不安全函数,则行为是未定义的。各种标准中都明确列出了安全功能。 POSIX.1-2003 列表是... `raise()` ... `signal()` ... `tcsetattr()` [修剪为相关的]

这强烈表明 POSIX 委员会有这种确切的类型记住这一点,并导致一种直接的方法,在打开串行并保存其属性后,您可以更改 SIGINT 处理程序,然后在处理程序中恢复它们和旧的 SIGINT< /code> handler,然后重新发出信号:

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 );
    ...
}

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 );
    ...
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文