在同一个 Linux 线程中运行 select() 套接字和计时器

问题描述 投票:0回答:2

我正在ucLinux上编写用于套接字通信的代码。我使用

select()
来读取套接字上的数据。我还有一个 20 毫秒计时器(使用
setitimer
创建)在同一线程中运行,用于执行并行操作。我的 select 函数每次都会被阻止,说“被系统调用中断”,因为它每 20 毫秒收到定时器溢出时发出的 SIGALRM 信号。我尝试在发出 EINTR 时重新启动系统,然后再次运行
select()
。但这不会有帮助,因为我总是每 20 毫秒就会收到计时器发出的 SIGALRM 信号。我不想忽略这个信号,因为它用于执行系统中的其他任务,但我想使用 select 而不受此信号的影响。有什么办法可以处理这个问题吗?我无法使用像
timer_create()
这样的功能,因为我正在使用的平台不支持这些功能。所以,我坚持使用
setitimer
来创建计时器。有什么方法可以在我的代码中独立运行两者吗?

c linux sockets timer posix-select
2个回答
4
投票

你所做的事情很奇怪。让我们面对现实吧:计时器是一种古老且几乎已经过时的工作机制。如今,几乎每个人都在回避像瘟疫这样的信号。在信号回调中基本上没有什么用处(例如,您当然不能调用像

malloc
这样复杂的东西),因此您必须有某种方法将计时器通知从
SIGALRM
处理程序返回到主线程已经 - 你实际上并没有在信号处理程序中完成工作,是吗?

所以你有两种策略:使用标准的自管道技巧将信号转换为 fd 上的事件,这是处理

SIGTERM
SIGINT
等事情的“正常”方式。您调用
socketpair
pipe
来创建一个管道,然后从信号处理程序将一个字节写入管道。您从
select
循环中读回字节。您通常将信号的值写为数据,但您实际上可以写任何东西。

另一种策略(更加理智)是完全避免信号和

setitimer
的混乱。
setitimer
是严重的遗留问题,会导致各种问题(例如,它可能导致像
getaddrinfo
这样的函数挂起,这是 glibc 中尚未修复的错误(http://www.cygwin.org) /frysk/bugzilla/show_bug.cgi?id=15819)。因此,“正常”策略是使用
select
的计时器和对象的链接列表。管理代码中的周期性事件。当您调用
select
时,您可以使用剩余计时器中最短的一个。当
select
调用返回时,您可以检查是否有任何计时器已过期并调用计时器处理程序。这是一个标准的应用程序事件循环,这样您就可以监听计时器驱动的事件以及 fd 驱动的事件。


1
投票

您可以选择做这样的事情吗?

While(1) {
   int rc = select(nfds, &readfds, &writefds, &exceptfds, &timeout);
   if ((rc < 0) && (errno == EINTR) )
      continue;
   else {
      // some instructions
   }
}

如果这不适合您,您可以使用 pselect,它在末尾添加一个参数(sigmask),指定在 pselect() 期间应阻止的一组信号,请参阅此处

© www.soinside.com 2019 - 2024. All rights reserved.