我有一个使用pthreads的Unix守护进程。一个线程在循环中运行,使用recvmsg
读取网络数据包。当守护进程收到一个信号时,会设置一个标志,告诉所有线程突破它们的循环并退出。但是,“侦听”线程永远不会检查该标志,直到下一个数据包到达并且recvmsg
返回,这可能需要一些时间。
使用SIGINT
向{listen}线程发送pthread_kill
使其突破recvmsg
,但它也会调用信号处理程序,该处理程序通过按Ctrl + C来处理发送的中断控制台。这是不需要的。强制recvmsg
提前返回的另一种方法是关闭它正在侦听的套接字,但我认为必须有更好的方法。你知道它是什么吗?
此守护程序仅在Linux上使用,如果它有所不同。
答案 0 :(得分:2)
三个想法:
想法#1:
为什么不是另一个信号信号SIGUSR1
和SIGUSR2
仅用于用户定义的信令。如果您已经设置了信号处理程序并且通常对其使用感到满意,这可能是最简单的方法。
创意#2: 像其他人建议的那样发送虚拟数据包。
创意#3:
如果您真的想避免发送数据包,请尝试使用非阻塞I / O,以便recvmsg根本不会阻塞(请参阅MSG_DONTWAIT
)。
相反,调用epoll | poll | select
来阻止套接字recv事件和另一个文件描述符。
这样的循环看起来像这样:
while(1){
recvmsg();
do_stuff();
}
变为:
while(1){
wait_for_events();
if( /* FD used for cancellation is readable */ )
break;
else
recvmsg();
do_stuff();
}
其中wait_for_events
为select
,poll
或epoll_wait
。设置等待列表以包括套接字和其他fd,例如管道,unix域套接字或POSIX消息队列(Linux中的文件)。
要取消套接字读取器线程,请在取消文件对象上执行write
。然后让读者检查从民意调查/ epoll / select中醒来时是否能够读取fd。
如果有多个事物处理recvmsg
的实例,你应该能够使用一个这样的文件来唤醒所有线程,如果使用epoll
,请确保使用等级触发而不是边缘触发
如果您不确定要使用哪三个来电,我建议您从poll
开始,如果您已经非常了解,请仅使用select
或epoll
你知道你需要它的可扩展性。
答案 1 :(得分:1)
如果这是UDP,我使用的常用方法是设置退出条件标志,然后只发送一个1字节的数据包以强制recvfrom / recvmsg调用返回。
或者,您可以在套接字SO_RCVTIMEO上设置超时值(例如1秒)。当套接字等待超时时,您可以检查退出条件。如果未设置退出条件,则再次调用recvmsg。
答案 2 :(得分:1)
pthread_cancel
可能是您正在寻找的内容。请仔细阅读手册页,不要过度使用。
答案 3 :(得分:1)
将空信号处理程序安装到未使用的POSIX实时信号SIGRTMIN+0
到SIGRTMAX-0
,然后使用pthread_kill()
将该信号发送到recvmsg()
中阻止的线程。实时信号排队(可靠),而标准信号则不排队。这意味着如果您使用实时信号,同时中断两个不同的线程,但如果使用正常信号(例如SIGUSR1
)可能会失败。
向信号处理程序传递信号,即使信号处理函数的主体为空,也会中断库函数或系统调用。 (当然,假设您使用sigaction()
安装信号处理程序而没有SA_RESTART
标志。请参阅man 7 signal
,"系统调用中断和库函数信号处理程序" 了解详情。)
这种方法可能遇到的一个问题(使用信号来中断阻塞I / O功能)是在目标线程实际阻塞I / O功能之前提前发出信号的时候。 那个窗口真的无法避免,你只需要处理它。
就个人而言,我喜欢使用专用线程来维护超时结构,比如
struct timeout {
pthread_t who;
struct timespec when; /* Using CLOCK_MONOTONIC */
volatile int state;
};
每个线程都可以获取和释放 - 我发现线程可以同时保存多个超时特别有用 - 以及一个简单的原子函数来检查状态(用于例如循环条件) )。
诀窍是,当超时最初触发时,由于上述有问题的时间窗口,它不会被删除。相反,我喜欢标记超时"经过",然后在几毫秒后重新安装毫秒。重复此过程,直到目标线程释放超时。 (每次触发超时时,我都会向目标线程发送POSIX实时信号,并安装空信号处理函数。)
这样,即使您的recv()
/ send()
循环偶尔会错过信号,也会很快收到通知。
如果你取消线程,你还需要安装一个线程清理处理程序来释放线程拥有的所有超时。否则你可能会泄漏"超时结构。
如果使用clock_gettime()
和pthread_cond_timedwait()
,等待新的超时条目(由其他线程添加)或现有的超时时间过去,超时管理线程将非常轻量级并且不会消耗太多内存或CPU时间。请注意,如果使用通过CLOCK_MONOTONIC
选择了该时钟的属性集创建添加了new-timeouts的条件变量,则可以使用pthread_condattr_setclock()
。