【问题标题】:Why sleep() in a thread cannot be interrupted by signal in my code?为什么线程中的 sleep() 不能被代码中的信号中断?
【发布时间】:2020-05-07 06:52:48
【问题描述】:
#include <iostream>
#include <thread>
#include <signal.h>
#include <unistd.h>

void handler(int sig){
  std::cout << "handler" << std::endl;
} 

void func() {
  sleep(100);
  perror("sleep err:");
}


int main(void) {
  signal(SIGINT, handler);
  std::thread t(func);
  pthread_kill(t.native_handle(), SIGINT);
  perror("kill err:");
  t.join();
  return 0;
}

如果我将 sleep() 放在 main 函数中,并通过按 ctrl+c 发送信号,sleep 将被中断并立即返回 perror() 表示它已中断。

但是使用上面的代码,处理函数中的“处理程序”将被打印,但睡眠不会返回,程序继续运行。这个程序的输出是:

kill err:: Success
handler

如果我用recvfrom()替换sleep(),recvfrom()即使在主线程内也不会被中断。

#include <vector>
#include <string.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>

void SigHandler(int sig){
  std::cout << "handler" << std::endl;
}

int main(void) {
 signal(SIGINT, SigHandler);
 int bind_fd_;
 if ((bind_fd_ = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    std::cout << "socket creation failed " << strerror(errno) << std::endl;
  }

  struct sockaddr_in servaddr;
  memset(&servaddr, 0, sizeof(servaddr));

  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  servaddr.sin_port = htons(12345);

  if (bind(bind_fd_, reinterpret_cast<const struct sockaddr *>(&servaddr),
           sizeof(servaddr)) < 0) {
    std::cout << "socket bind failed " << strerror(errno) << std::endl;
  }


  struct sockaddr_in cliaddr;    
  socklen_t cliaddr_len = sizeof(cliaddr);    
  std::vector<char> buffer(10*1024*1024,0);
  std::cout << "Wait for new request"<< std::endl;    
  int n = 0;    
  while (n == 0) {    
    std::cout << "before recvfrom" << std::endl;
    n = recvfrom(bind_fd_, buffer.data(), buffer.size(), 0,    
                 reinterpret_cast<struct sockaddr *>(&cliaddr), &cliaddr_len);    
    // sleep(100);
    perror("recvfrom err: ");
    std::cout << "recv " << n << " bytes from " << cliaddr.sin_port<< std::endl;    
  }    
}

我不知道我的代码有什么问题,希望您的帮助,谢谢

【问题讨论】:

    标签: linux signals system-calls


    【解决方案1】:

    在您将信号定向到线程时,该线程尚未进行到足以在 sleep() 中阻塞的程度。很有可能它甚至没有第一次被安排。将代码更改为类似

    std::thread t(func);
    sleep(5); // give t enough time to arrive in sleep()
    pthread_kill(t.native_handle(), SIGINT);
    

    你会看到你所期望的。

    请注意,在多线程程序中使用信号通常不是一个好主意,因为某些方面未定义/未明确定义。

    还要注意,在信号处理程序中使用 iostream 是不正确的。信号处理程序运行在几乎没有什么安全可做的环境中,就像裸机上的中断服务例程一样。请参阅here 了解有关该问题的详细说明。

    【讨论】:

    • 在 pthread_kill 之前添加 sleep() 将使 func() 内的睡眠被中断,我已经在我的代码中检查了这一点,非常感谢!!此外,我想知道如何在另一个线程中中断阻塞系统调用的最佳实践。会是 pthread_cancel 吗?但是我发现 pthread_cancel 似乎立即取消了整个线程,并且系统调用之后的代码不会被执行。另一个问题是,recv 来自一个特殊的系统调用,因为在向它的线程发送信号后我无法中断它(并用睡眠替换它会导致中断)。
    • 如果你 pthread_cancel() 一个线程,而它在一个关键部分的中间(持有一个互斥锁),该线程将没有机会释放互斥锁。您可以为此目的安装取消处理程序,但我怀疑这在任何规模上都是可行的,但最小的规模除外。 pthread_kill(),嗯,信号处理程序,嗯,都不是很令人满意。使用 pthread_kill(),我建议你携带一个特定于线程的 终止标志,当阻塞调用被 EINTR'd 时,你检查它。
    猜你喜欢
    • 2015-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多