【问题标题】:Linux signal parent and child [c]Linux信号父子[c]
【发布时间】:2021-12-22 21:24:19
【问题描述】:

我是 Linux 新手,我仍在学习我的代码作业很简单,它从父母那里接收信号,孩子必须忽略信号并打印信号的编号,例如 [1,3,4,9,11] ,但我的问题是孩子在信号之后不打印任何内容,而且我希望孩子忽略信号,尤其是 [sigquit] 这是我的代码。

  // C program to implement sighup(), sigint()
    // and sigquit() signal functions
    #include <signal.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    // function declaration
    void sighup();
    void sigint();
    void sigquit();
    void sigsegv();
    
    // driver code
    void main()
    {
      int pid;
    
      /* get child process */
      if ((pid = fork()) < 0) {
        perror("fork");
        exit(1);
      }
    
      if (pid == 0) { /* child */
        signal(SIGHUP, sighup);
        signal(SIGINT, sigint);
        signal(SIGQUIT, sigquit);
                    signal(SIGSEGV, sigsegv);
        for (;;)
          ; /* loop for ever */
      }
    
      else /* parent */
      { /* pid hold id of child */
        printf("\nPARENT: sending SIGHUP\n\n");
        kill(pid, SIGHUP);
    
        sleep(3); /* pause for 3 secs */
        printf("\nPARENT: sending SIGINT\n\n");
        kill(pid, SIGINT);
    
        sleep(3); /* pause for 3 secs */
        printf("\nPARENT: sending SIGQUIT\n\n");
        kill(pid, SIGQUIT);
        sleep(3);
      }
    }
    
    // sighup() function definition
    void sighup()
    
    {
      signal(SIGHUP, sighup); /* reset signal */
      printf("CHILD: 1 [sighub]\n");
    }
    
    // sigint() function definition
    void sigint()
    
    {
      signal(SIGINT, sigint); /* reset signal */
      printf("CHILD: 2 [sigint]\n");
    }
    // sigsegv() function definition
    void sigsegv()
    
    {
      signal(SIGSEGV, sigsegv); /* reset signal */
      printf("CHILD: 11 [sigsegv]\n");
    }
    
    // sigquit() function definition
    void sigquit()
    {
    signal(SIGINT, sigquit); /* reset signal */
      printf("3 [sigquit]\n");
      
    }

【问题讨论】:

  • 请注意printf 不是signal safe
  • 您可以使用sigwait() 完成此作业吗?还是signalfd()
  • (至少你应该使用sigaction()而不是signal()
  • 信号被发送到进程,而不是线程。
  • @Shawn 是的,我可以使用任何东西你能帮我吗?

标签: c linux signals


【解决方案1】:

检查 /usr/bin/include 中的 signal.h,信号处理程序

/* Type of a signal handler.  */
typedef void (*__sighandler_t) (int);

所以需要同时改变前向声明和函数定义来匹配这个原型


 // C program to implement sighup(), sigint()
    // and sigquit() signal functions
    #include <signal.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>

    
    // function declaration
    void sighup(int);
    void sigint(int);
    void sigquit(int );
    void sigsegv(int );
    
    // driver code
    int main()
    {
      int pid;
    
      /* get child process */
      if ((pid = fork()) < 0) {
        perror("fork");
        exit(1);
      }
    
      if (pid == 0) { /* child */
        signal(SIGHUP, sighup);
        signal(SIGINT, sigint);
        signal(SIGQUIT, sigquit);
                    signal(SIGSEGV, sigsegv);
        for (;;)
          ; /* loop for ever */
      }
    
      else /* parent */
      { /* pid hold id of child */
        printf("\nPARENT: sending SIGHUP\n\n");
        kill(pid, SIGHUP);
    
        sleep(3); /* pause for 3 secs */
        printf("\nPARENT: sending SIGINT\n\n");
        kill(pid, SIGINT);
    
        sleep(3); /* pause for 3 secs */
        printf("\nPARENT: sending SIGQUIT\n\n");
        kill(pid, SIGQUIT);
        sleep(3);
      }
return  0 ;
    }
    
    // sighup() function definition
    void sighup(int signo)
    
    {
      signal(SIGHUP, sighup); /* reset signal */
      printf("CHILD: 1 [sighub]\n");
    }
    
    // sigint() function definition
    void sigint(int signo)
    
    {
      signal(SIGINT, sigint); /* reset signal */
      printf("CHILD: 2 [sigint]\n");
    }
    // sigsegv() function definition
    void sigsegv(int signo)
    
    {
      signal(SIGSEGV, sigsegv); /* reset signal */
      printf("CHILD: 11 [sigsegv]\n");
    }
    
    // sigquit() function definition
    void sigquit(int signo)
    {
    signal(SIGINT, sigquit); /* reset signal */
      printf("3 [sigquit]\n");
      
    }

【讨论】:

  • 你能解释一下第二部分(int signo)吗?我不太明白你的意思
  • 请看我上面修改过的答案,也有更正的代码。希望澄清
【解决方案2】:

如 cmets 中所述,printf() 之类的 stdio 函数在信号处理程序中使用并不安全。在 Linux 上,您还应该使用 sigaction() 而不是 signal() 来安装信号处理程序,因为这样可以避免在后一个函数中处理程序如何工作的不精确定义的一些问题(仅应在针对裸骨标准 C 时使用,而不是 POSIX,信号处理程序可以做的事情比 POSIX 更受限制)。

但是,当面向 Linux 或 Unix 平台时,您根本不需要信号处理程序来完成此任务!每个进程都有一个信号掩码,用于控制阻止哪些信号正常执行处理程序或关闭默认操作。如果一个进程阻塞信号 X 得到了那个信号,它就被认为是pending,并且还有其他的方式来接收它。 Linux 中的一种方法是使用signalfd,这是一种特殊的文件描述符,可以从中读取以获取有关挂起信号的信息。一个使用它的例子:

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/signalfd.h>
#include <sys/types.h>
#include <sys/wait.h>

int child_main(const sigset_t *, int);
void send_signals(pid_t, int *);

int main(void) {
  // The signals we want to catch
  int signals[] = {SIGHUP, SIGINT, SIGQUIT, SIGSEGV, -1};

  // Set up the signal mask
  sigset_t sigs, oldmask;
  sigemptyset(&sigs);
  for (int i = 0; signals[i] >= 0; i++) {
    sigaddset(&sigs, signals[i]);
  }

  // To avoid a race condition where the parent starts sending signals
  // before the child is ready for them, block the signals before
  // forking the child
  if (sigprocmask(SIG_BLOCK, &sigs, &oldmask) < 0) {
    perror("sigprocmask");
    return EXIT_FAILURE;
  }

  pid_t child = fork();
  if (child < 0) {
    perror("fork");
    return EXIT_FAILURE;
  } else if (child == 0) {
    // In the child process
    return child_main(&sigs, (sizeof signals / sizeof signals[0]) - 1);
  } else {
    // Parent process. Restore the original signal mask and send child signals
    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
      perror("parent sigprocmask");
      kill(child, SIGKILL);
      return EXIT_FAILURE;
    }
    send_signals(child, signals);
    // Wait for the child to finish
    if (waitpid(child, NULL, 0) < 0) {
      perror("parent waitpid");
      return EXIT_FAILURE;
    }
  }

  return 0;
}

void send_signals(pid_t proc, int *signals) {
  for (int i = 0; signals[i] >= 0; i++) {
    printf("Sending process %d signal %s (%d)\n", (int)proc,
           strsignal(signals[i]), signals[i]);
    if (kill(proc, signals[i]) < 0) {
      printf("Failed: %s\n", strerror(errno));
    }
  }
}

int child_main(const sigset_t *sigs, int nsigs) {
  // Create a signalfd that monitors the given signals
  int fd = signalfd(-1, sigs, 0);
  if (fd < 0) {
    perror("child signalfd");
    return EXIT_FAILURE;
  }

  struct signalfd_siginfo s;
  // Loop up to nsigs times reading from the signal fd
  int count = 0;
  while (++count <= nsigs && read(fd, &s, sizeof s) == sizeof s) {
    printf("Child received signal %s (%d)\n", strsignal(s.ssi_signo),
           s.ssi_signo);
  }
  if (count <= nsigs && errno != EINTR) {
    perror("child read");
    close(fd);
    return EXIT_FAILURE;
  }
  close(fd);
  return 0;
}

示例输出:

Sending process 17248 signal Hangup (1)
Sending process 17248 signal Interrupt (2)
Sending process 17248 signal Quit (3)
Sending process 17248 signal Segmentation fault (11)
Child received signal Hangup (1)
Child received signal Segmentation fault (11)
Child received signal Interrupt (2)
Child received signal Quit (3)

【讨论】:

    【解决方案3】:

    你的程序在我的机器上运行得很完美,所以我不明白你为什么说孩子不打印任何东西。

    无论如何,你必须小心,因为孩子正在打印接收到的信号的消息,但永远运行,这意味着当父母完成并且exit()s 你让孩子运行......永远? (并且您正在运行子进程而没有停止或阻塞,因此消耗了所有可用的 cpu)

    您必须设计一种方法来杀死这样的进程,因为如果您不这样做,您将以许多进程的名义运行而结束,除了忽略您对它们所做的kill()s 之外什么都不做。

    为此,有些信号是不可忽略的,SIGKILL 就是其中之一,出于安全原因,您不能为其安装信号处理程序... :)

    此外,当使用signal(2) 系统调用时,处理程序在接收到第一个信号时执行,并在信号处理程序完成时切换到默认行为(取决于信号)。

    如果您希望它是永久的,您需要重新安装信号处理程序(这有一个竞争条件,以防您在执行信号处理程序时收到第二个信号并且没有时间再次安装它)或改为调用sigaction(2),这允许您永久(在进程运行时,或直到您对sigaction 进行的下一次调用)安装不需要更新的信号处理程序。查看 sigaction 的手册页,你需要学习如何使用它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-11
      • 1970-01-01
      • 2021-05-19
      • 1970-01-01
      • 2021-06-20
      相关资源
      最近更新 更多