【问题标题】:pthreads and signal handling C ending earlypthreads 和信号处理 C 提前结束
【发布时间】:2023-03-22 23:29:01
【问题描述】:

这个程序应该是

父母只是无限期地等待任何孩子返回(提示,waitpid)。 湾。孩子设置了两个信号处理程序(提示、信号)并进入睡眠状态 5 分钟。 一世。第一个信号处理程序侦听 USR1 信号,并在接收到它时: 1. 创建一个线程(提示,pthread_create)。 一种。基本上,线程需要做的就是“打个招呼”并休眠 60 秒。 ii.第二个信号处理程序监听 USR2 信号,并在接收到它时: 1. 销毁线程(提示,pthread_cancel)。

当这个程序接收到第一个创建线程的信号时,它输出 “[线程] 休眠 1 m[线程] 休眠 1 分钟” 然后结束,它从不等待第二个信号,我做错了什么?

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>

pthread_t thread;

void* temp()
{
    printf("[thread] hello professor\n");
    printf("[thread] sleeping for 1 minute\n");
    sleep(60);
}
void handle_USR1(int x)
{
    int s;
    printf("[signal] creating the thread\n");
    s = pthread_create(&thread, NULL, &temp, NULL);
}

void handle_USR2(int x)
{
    int s;
    printf("[signal] destroying the thread\n");
    s = pthread_cancel(thread);
}

int main(void)
{
    int status = 0;

    if(fork() != 0)
    {
     printf("[parent] waiting.....\n");
     waitpid(-1, &status, 0);
    }
    else
    {
     printf("[child] to create the thread: kill -USR1 %d\n", getpid());
     printf("[child] to end the thread: kill -USR2 %d\n", getpid());
     printf("[child] setting up signal handlers\n");

     signal(SIGUSR1, handle_USR1);
     signal(SIGUSR2, handle_USR2);

     printf("[child] waiting for signals\n");
     sleep(300);
    }
    return (0);
}

【问题讨论】:

  • OP 的问题是向子进程发送 kill SIGUSR1 会调用处理程序,然后杀死子进程,以便父进程中的 waitpid() 返回。问题是在将 SIGUSR1 发送给孩子之后,OP 如何防止 waitpid() 返回。
  • @charlie 谢谢,我不确定到底发生了什么,仍然不知道如何解决它,哈哈
  • pthread_create 不是从信号处理程序调用的安全函数,所以这看起来从一开始就招来了麻烦。
  • @duck 我们的教授就是这么说的,他的工作很好:/

标签: c linux pthreads signals


【解决方案1】:

正如 Charlie Burns 指出的那样,两个进程最终都会因信号而退出,但原因不同。

孩子

在休眠期间,子进程被系统调用阻塞(实际系统调用为nanosleep,用于实现sleep()函数)。当进程在系统调用中接收到信号时,会执行相应的信号处理程序,系统调用返回错误EINTR,表示它已被中断,无法履行职责。然后,您可以决定是否要重新启动系统调用。收到 SIGUSR1 后,子进程执行的 nanosleep 系统调用被中断,处理程序被执行,并且 sleep() 立即返回。注意man 3 sleep 所说的关于 sleep() 的返回值:

Zero if the requested time has elapsed, or the number of seconds left to sleep, if the call was interrupted by a signal handler.

正确的方法是让孩子检查 sleep 的返回值(剩余的睡眠秒数),然后在这段时间内再次睡眠。

家长

与 Charlie Burns 所指出的不同,父级中的 waitpid() 不会因为子级接收到信号而返回。由于孩子退出,它返回。如果孩子没有处理信号,它会因为孩子而返回,因此被它杀死(未处理的信号导致进程死亡)。您可以(并且应该)使用man 2 waitpid 中描述的 WIFEXITED 宏及其同伴进行检查。这个手册页底部的例子非常好:

do {
   w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
   if (w == -1) {
       perror("waitpid");
       exit(EXIT_FAILURE);
   }

   if (WIFEXITED(status)) {
       printf("exited, status=%d\n", WEXITSTATUS(status));
   } else if (WIFSIGNALED(status)) {
       printf("killed by signal %d\n", WTERMSIG(status));
   } else if (WIFSTOPPED(status)) {
       printf("stopped by signal %d\n", WSTOPSIG(status));
   } else if (WIFCONTINUED(status)) {
       printf("continued\n");
   }
} while (!WIFEXITED(status) && !WIFSIGNALED(status));

基本上,这段代码所做的是等待子进程,直到它正常退出或由于未处理的信号而退出。在您的情况下,父母最好检查状态变量以确保 waitpid 由于它预期的事件(孩子退出)而不是其他原因而返回。

【讨论】:

  • 这只是一个简短的任务,第一次搞乱信号处理。我们的教授特别指出不要担心错误处理,因为这将是我们的下一个任务。但是,您对睡眠的检查很有效,谢谢!
【解决方案2】:

在您的pthread_create 之后放置一个pthread_join

【讨论】:

  • 是的,但在信号处理程序中这太疯狂了。
  • 根据我的教授的说法,我们应该能够只通过创建和取消来做到这一点:/加入如何解决它?
  • 它不一定必须放在信号处理函数中,对吧?它可能在 main()...
  • 那又怎样,加在sleep 300之后?
【解决方案3】:

好的,我知道发生了什么。

当您发送信号时,如果没有通过掩码将其定向到特定线程,则进程中的任何线程都可以获取它。当 SIGUSR1 被传递 main 时,子进程中的 sleep 被吹出,并且主线程终止杀死在处理程序中创建的线程。

这里有很多问题涉及如何将信号定向到单个线程和/或使用sigaction 重新启动系统调用(如果这也是您想要解决的方向)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-07
    相关资源
    最近更新 更多