簡體   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