我正在学习如何使用pselect。我拿了一个示例代码,该代码工作正常并修改它以从一个从main生成的线程调用相同的代码,但它不起作用(pselect仍然永远被阻止)
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
/* Flag that tells the daemon to exit. */
static volatile int exit_request = 0;
/* Signal handler. */
static void hdl (int sig)
{
exit_request = 1;
printf("sig=%d\n", sig);
}
/* Accept client on listening socket lfd and close the connection
* immediatelly. */
static void handle_client (int lfd)
{
int sock = accept (lfd, NULL, 0);
if (sock < 0) {
perror ("accept");
exit (1);
}
puts ("accepted client");
close (sock);
}
void *mythread(void *arg __attribute__ ((unused)))
{
int lfd;
struct sockaddr_in myaddr;
int yes = 1;
sigset_t mask;
sigset_t orig_mask;
struct sigaction act;
memset (&act, 0, sizeof(act));
act.sa_handler = hdl;
/* This server should shut down on SIGUSR1. */
if (sigaction(SIGUSR1, &act, 0)) {
perror ("sigaction");
return NULL;
}
sigemptyset (&mask);
sigaddset (&mask, SIGUSR1);
if (pthread_sigmask(SIG_BLOCK, &mask, &orig_mask) < 0) {
perror ("pthread_sigmask");
return NULL;
}
lfd = socket (AF_INET, SOCK_STREAM, 0);
if (lfd < 0) {
perror ("socket");
return NULL;
}
if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR,
&yes, sizeof(int)) == -1) {
perror ("setsockopt");
return NULL;
}
memset (&myaddr, 0, sizeof(myaddr));
myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = INADDR_ANY;
myaddr.sin_port = htons (10000);
if (bind(lfd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) {
perror ("bind");
return NULL;
}
if (listen(lfd, 5) < 0) {
perror ("listen");
return NULL;
}
while (!exit_request) {
fd_set fds;
int res;
/* BANG! we can get SIGUSR1 at this point, but it will be
* delivered while we are in pselect(), because now
* we block SIGUSR1.
*/
FD_ZERO (&fds);
FD_SET (lfd, &fds);
res = pselect (lfd + 1, &fds, NULL, NULL, NULL, &orig_mask);
if (res < 0 && errno != EINTR) {
perror ("select");
return NULL;
}
else if (exit_request) {
puts ("exited");
break;
}
else if (res == 0)
continue;
if (FD_ISSET(lfd, &fds)) {
handle_client (lfd);
}
}
return NULL;
}
int main (int argc, char *argv[])
{
void * res;
pthread_t mythr_h;
pthread_create(&mythr_h, (pthread_attr_t *)NULL, mythread, NULL);
pthread_join(mythr_h, &res);
return 0;
}
强文
将SIGUSR1发送到此程序后,我发现它在pselect调用中仍然被阻止。当mythread函数中的代码移回main而不是从main生成任何线程时,它完美地工作。
答案 0 :(得分:1)
将SIGUSR1发送到此程序后,我发现它仍然被阻止 pselect电话。当mythread函数中的代码被移回时 进入main而不是从main生成任何线程,它运行得很好。
这是预期的 - 不能保证信号将被传递到“正确”的线程,因为没有明确定义的“正确”线程的概念。
信号和多线程不能很好地混合,但如果你想这样做,我建议摆脱exit_request标志(注意:无论如何都是volatile关键字isn't sufficient to work reliably in multithreaded scenarios),而是创建一对连接的文件描述符(通过调用pipe()函数或socketpair()函数)。你的所有信号处理函数(hdl())需要做的是将一个字节写入两个文件描述符之一。让你的线程在其read-socket-set(fds)中包含另一个文件描述符,这样当写入的字节将导致pselect()返回,然后你对FD_ISSET的后续调用(theSecondFileDescriptorOfThePair,&amp; fds)将返回true ,这就是你的线程将如何知道它现在退出的时间。
答案 1 :(得分:1)
除了pselect()
调用上的线程阻塞之外,信号被传递到主线程。如果有多个线程使信号解除阻塞,则可以将信号传递给任何一个线程。
由于您没有指定您的平台,首先我引用POSIX standard(系统接口卷,2.4.1信号生成和传送)。
为流程生成的信号应该在流程中正好传递给其中一个,在调用sigwait()函数时选择该信号或< strong>没有阻止信号传递。
您还可以在Linux联机帮助页signal(7)
中看到类似的陈述。
可以将过程导向的信号传递给任何人 其中一个当前没有阻塞信号的线程。 如果多个线程的信号未被阻塞,那么 内核选择一个任意线程来传递信号。
和FreeBSD手册页sigaction(2)
。
对于针对该过程的信号,如果是 信号当前未被所有线程阻止,然后传递到 一个没有阻塞它的线程(其选择是 未指定)。
所以你可以做的是阻止SIGUSR1
进程中的所有线程,除了调用pselect()
的线程。幸运的是,当创建一个新线程时,它会从其创建者继承信号掩码。
从上面的相同POSIX部分
线程的信号掩码应该从其父节点或创建线程初始化....
Linux pthread_sigmask(3)
,
新线程继承其创建者信号掩码的副本。
FreeBSD sigaction(2)
,
线程的信号掩码初始化为其父级的信号掩码(通常为空)。
您可以对代码进行以下更改。在main()
中,在创建任何线程之前,阻止SIGUSR1
。在该主题中,将SIGUSR1
未阻止的信号掩码传递给pselect()
。
--- old.c Mon Mar 21 22:48:52 2016
+++ new.c Mon Mar 21 22:53:54 2016
@@ -56,14 +56,14 @@
return NULL;
}
- sigemptyset (&mask);
- sigaddset (&mask, SIGUSR1);
-
- if (pthread_sigmask(SIG_BLOCK, &mask, &orig_mask) < 0) {
+ sigemptyset(&orig_mask);
+ if (pthread_sigmask(SIG_BLOCK, NULL, &orig_mask) < 0) {
perror ("pthread_sigmask");
return NULL;
}
+ sigdelset(&orig_mask, SIGUSR1);
+
lfd = socket (AF_INET, SOCK_STREAM, 0);
if (lfd < 0) {
perror ("socket");
@@ -126,6 +126,15 @@
{
void * res;
pthread_t mythr_h;
+ sigset_t mask;
+
+ sigemptyset (&mask);
+ sigaddset (&mask, SIGUSR1);
+
+ if (pthread_sigmask(SIG_BLOCK, &mask, NULL) != 0) {
+ return 1;
+ }
+
pthread_create(&mythr_h, (pthread_attr_t *)NULL, mythread, NULL);
pthread_join(mythr_h, &res);
return 0;
最后一件事不在话题。 printf()
不是async-signal-safe
函数,因此不应在信号处理程序中调用。