繁体   English   中英

Linux信号父子[c]

[英]Linux signal parent and child [c]

我是 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");
      
    }

检查 /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");
      
    }

正如评论中提到的,像printf()这样的 stdio 函数在信号处理程序中使用是不安全的。 在 Linux 上,您还应该使用sigaction()而不是signal()来安装信号处理程序,因为这避免了处理程序如何在后一个函数中工作的不精确定义的一些问题(仅应在针对裸机标准 C 时使用,不是 POSIX,信号处理程序可以做什么比在 POSIX 中更受限制)。

但是,当针对 Linux 或 Unix 平台时,您根本不需要信号处理程序来完成这项任务! 每个进程都有一个信号掩码,它控制哪些信号被阻止以阻止处理程序或默认操作的正常执行。 如果进程阻塞信号 X 收到该信号,则认为它是挂起的,并且还有其他方法可以接收它。 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)

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

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

你必须设计一种方法来终止这样的进程,因为如果你不这样做,你将以代表你运行的许多进程结束,除了忽略你对它们所做的kill()什么都不做。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM