【问题标题】:Reinstalling set signals in C在 C 中重新安装设置信号
【发布时间】:2021-03-24 00:31:37
【问题描述】:

我正在尝试使用一个信号处理程序处理多个信号,预期结果是 ctrlc 退出子进程并退出父进程,而 ctrlz 每次按下 ctrlz 时都会打印一个随机数,但它似乎没有在处理第一个信号后工作。代码的另一部分是一个子进程,它循环直到调用 ctrl-c。 这是代码。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <time.h>
#include <string.h>

int sig_ctrlc = 0;
int sig_ctrlz = 0;

//main signal handler to handle all signals
void SIGhandler(int sig) {
  switch (sig) {
    case SIGINT:
      sig_ctrlc = SIGINT;
      break;

    case SIGTSTP:
      sig_ctrlz = SIGTSTP;
      break;

    case SIGCHLD:
    default: break;
  }
}


int main() {
  int fd[2]; //to store the two ends of the pipe
  char get_inode[] = "ls -il STATUS.TXT";
  FILE *fp;
  FILE *log;
  FILE *command;
  time_t t;
  time(&t);
  int rv = 0;
  log = fopen("file_log.txt", "w");
  struct sigaction act;
  memset (&act, 0, sizeof(struct sigaction));
  act.sa_handler = SIGhandler;

  //if pipe can't be created
  if (pipe(fd) < 0) {
    fprintf(log, "Pipe error");
  }
    int pid = fork();
  switch(pid) {

      case -1:
          fprintf(stderr, "fork failed\n");
          exit(1);
      case 0:
        /*child process */

//          maps STDOUT to the writing end of the pipe
//           if  (dup2(fd[1], STDOUT_FILENO) == -1) {
//             fprintf(log, "error in mapping stdout to the writing pipe\n");
//           }
          act.sa_flags = SA_RESTART;
          sigemptyset(&act.sa_mask);
          sigaction(SIGINT, &act, NULL);
          sigaction(SIGTSTP, &act, NULL);

          for (size_t i = 1;; i++) {
              /* code */
              printf(" running in child\n");
              sleep(1);
              if (sig_ctrlc != 0) {
                printf("ctrlc handled\n");
                printf("exiting...\n");
                sig_ctrlc = 0;
                break;
              }

              if (sig_ctrlz != 0) {
                printf("ctlrz handled.\n");
                /* random generator, the problem with this is it runs with time if ctrlz
                    is handled within a second it returns the same number
                */
                srand(time(0));
                int rand_num;
                rand_num = rand() % (50 - 10 + 1) + 10;
                printf("random number: %d\n", rand_num);
                sig_ctrlz = 0;
                sigaction(SIGINT, &act, NULL);
                sigaction(SIGTSTP, &act, NULL);
              }
          }

      default:
          /* parent process */
          close(fd[1]);
          //maps STDIN to the reading end of the pipe
          // if (dup2(fd[0], STDIN_FILENO) < 0) {
          //   fprintf(log, "can't redirect");
          //   exit(1);
          // }

//          //checks for fopen not working and writes to STATUS.TXT with a redirect
//          if ((fp = freopen("STATUS.TXT", "w", stdout)) != NULL) {
//            printf("start time of program: %s\n",  ctime(&t));
//            printf("Parent process ID: %d\n", getppid());
//            printf("Child process ID: %d\n", getpid());
//
//            //gets inode information sends the command to and receives the info from the terminal
//            command = popen(get_inode, "w");
//            fprintf(command, "STATUS.TXT");
//            fclose(command);
//
////            map STDOUT to the status file
//            if(freopen("STATUS.TXT", "a+ ", stdout) == NULL) {
//                fp = fopen("file_log.txt","w");
//                fprintf(log, "can't map STATUS.TXT to stdout\n");
//                exit(1);
//            }
//
          printf("parent has started\n");

          wait(NULL);
          time(&t);
          printf("PARENT: My child's termination status is: %d at: %s\n", WEXITSTATUS(rv), ctime(&t));
//            fprintf(fp, "PARENT: My child's termination status is: %d at: %s\n", WEXITSTATUS(rv), ctime(&t));
//          fclose(fp);
//          fclose(log);

          sigaction(SIGINT, &act, NULL);

          for (size_t i = 1;; i++) {
              /* code */
              printf("PARENT: in parent function\n");
              sleep(1);
              if (sig_ctrlc != 0)
                  exit(0);
          }

  }
    return 0;
}

【问题讨论】:

  • 我认为您在case 0: 的末尾缺少break;exit()。就目前而言,在处理信号并跳出循环后,子进程陷入default: 案例并运行为父进程准备的代码。
  • 如果这不是问题,请更清楚地解释您的问题。运行程序后你输入了什么,你看到了什么输出,你期待什么输出?
  • 您应该检查来自wait() 的返回值,以确保您获得了正确尸体的详细信息。看起来您应该调用int corpse = wait(&amp;rv); 以便获得孩子的退出状态,而不是无条件地看到0
  • 不起作用”是什么意思?它适用于第一个Ctrl-Z,但不适用于后面的Ctrl-Z? -- 你可能想阅读更多关于信号的信息。尤其是触发handler时,是否恢复默认为implementation-defined
  • @thebusybee 处理完任何信号后,包括处理过的信号在内的所有信号都无法再次处理(如果按下 ctrlc 或 ctrlz 则不会执行任何操作)

标签: c signals signal-handling


【解决方案1】:

原始帖子中有一些很好的 cmets 可以帮助进行小修复。我认为还有一个静态变量sig_ctrlcsig_ctrlz 可能不是异步信号安全的问题。除此之外,我认为您的信号处理设置应该在您反复发送SIGTSTP 然后SIGINT 之后工作。我认为您将如何测试您的程序可能是问题所在。

根据您提供的一些线索:

  • “ctrlz 被按下,但在处理第一个信号后似乎不起作用”
  • “在第一个 ctrlz 之后不能同时处理 ctrlc 和 ctrlz”

这让我相信你所经历的实际上是终端的工作控制阻碍了你。这一系列事件可以解释它:

  • 父(进程A)在%1组的终端前台启动
  • 子(进程 B)被分叉,并且在组 %1 中的终端前台中
  • 信号处理程序在子进程中设置
  • 为了给孩子发信号,请按ctrl-z 发送SIGTSTP
  • 拥有终端(本例中为子(进程 B)的祖父母)接收请求
  • 拥有终端将信号广播到前台组中的所有进程
  • 拥有终端从前台删除组%1
  • 父进程(进程 A)收到 SIGTSTP 并被挂起(默认操作)
  • 子(进程 B)接收到SIGTSTP 并调用信号处理程序
  • 在子循环的下一次迭代中生成并打印随机数
  • 随后通过ctrl-zctrl-c 向孩子发送信号的尝试不会被终端转发给孩子(或父母),因为终端前台没有任何内容

如果情况确实如此,那么此时,您应该能够通过手动输入fg 并点击enter 将进程带回前台。然后,您可以尝试再次发出信号。但是,测试此类程序的更好方法是在一个终端中运行它,然后使用来自另一个终端的 pid 通过kill(...) 发送信号。

额外注意:与signal(...) 不同,sigaction(...) 不需要在每次配置后“重新安装”。乔纳森在这里的一个很好的解释https://stackoverflow.com/a/232711/7148416

【讨论】:

  • 谢谢,我明白你输入 fg 和回车的意思了,你能详细解释一下通过 kill 发送信号的意思吗
  • 是的,一旦您确定了正在运行的子进程的进程 ID(通过 ps、pgrep 等),您就可以从另一个终端(假设是 bash)运行 kill -TSTP 12345,其中“12345”被替换为实际的进程 ID。
猜你喜欢
  • 1970-01-01
  • 2014-02-06
  • 1970-01-01
  • 2015-12-16
  • 2020-03-04
  • 1970-01-01
  • 1970-01-01
  • 2018-12-27
  • 1970-01-01
相关资源
最近更新 更多