【发布时间】:2020-12-28 11:42:11
【问题描述】:
我正在尝试使用alarm() 信号在发生超时后退出accept() 调用。信号注册为例外,但不会中断阻塞的accept() 进程。如何让信号中断循环或中断阻塞过程?
volatile sig_atomic_t time_out_flag = false;
void handleSig(int sig)
{
std::cout << "signal\n";
time_out_flag = true;
return;
}
void Server::start(ClientHandler &ch) // throw(const char *)
{
t = new std::thread([&] { // Main thread
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = INADDR_ANY;
sockaddr.sin_port = htons(_port);
if (bind(_socket, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) // Bind socket (socket, sockaddr, addrlen)
throw("Failed to bind to port.\n");
if (listen(_socket, 2) < 0) // Passivly listen on socket (socket, backlog aka max users queued)
throw("Failed to listen on socket.\n");
auto addrlen = sizeof(sockaddr);
signal(SIGALRM, handleSig); // What to do after alarm(k)
while (!time_out_flag)
{
alarm(3);
int connection = accept(_socket, (struct sockaddr *)&sockaddr, (socklen_t *)&addrlen);
if (connection < 0) {
throw("accept() error\n");
}
alarm(0);
ch.handle(connection);
}
std::cout << "after";
});
}
如果可能的话,我真的很想和accept() 合作,而不是select()。
更新
我已按照建议将signal() 更改为sigaction(),但accept() 继续阻塞线程。这是修改后的代码:
volatile sig_atomic_t alarmed = 0;
void handle_alarm(int)
{
std::cout << "alarm\n";
alarmed = 1;
return;
}
void Server::start(ClientHandler &ch) // throw(const char *)
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGALRM);
if (pthread_sigmask(SIG_BLOCK, &mask, nullptr) < 0)
throw("sigaction pthread failed\n");
#ifdef USE_SIGACTION
// Set up sigaction() with alarmer
struct sigaction sigbreak;
std::memset(&sigbreak, 0, sizeof sigbreak);
sigbreak.sa_handler = &handle_alarm;
if (sigaction(SIGALRM, &sigbreak, NULL) != 0)
throw("sigaction() failed\n");
#else
if (signal(SIGALRM, handle_alarm) == SIG_ERR)
throw("signal() failed\n");
#endif
t = new std::thread([&] { // Main thread
if (pthread_sigmask(SIG_UNBLOCK, &mask, nullptr) < 0)
std::cout << "alarmos\n";
struct sockaddr_in server_sockaddr;
struct sockaddr_in sockaddr = {0};
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = INADDR_ANY;
sockaddr.sin_port = htons(_port);
if (bind(_socket, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) // Bind socket (socket, sockaddr, addrlen)
throw("Failed to bind to port.\n");
if (listen(_socket, 2) < 0) // Passivly listen on socket (socket, backlog aka max users queued)
throw("Failed to listen on socket.\n");
auto addrlen = sizeof(sockaddr);
while (1)
{
if (alarmed)
return;
try
{
alarm(3);
int connection = accept(_socket, (struct sockaddr *)&sockaddr, (socklen_t *)&addrlen);
if (alarmed)
return;
if (connection == -1)
throw("accept() error\n");
alarm(0);
ch.handle(connection);
}
catch (const char *msg)
{
throw(msg);
}
}
});
alarm(3);
}
更新 + 解决方案
定义USE_SIGACTION 可以解决问题并按照答案中的建议中断accept()。
【问题讨论】:
-
这正是
select及其变体、poll、epoll 等所擅长的。为什么不使用其中之一? -
@TedLyngmo 讲师明确要求
alarm()作为超时解决方案的一部分。 -
请澄清
"...the interrupt isn't working"的确切含义。 -
您可以使用poll(2)。另见time(7)、signal(7)、signal-safety(7)、signalfd(2)。您的
SIGALRM信号处理程序将设置一个全局volatile sigatomic_t flag