【问题标题】:Error using sigaction multiple times多次使用 sigaction 时出错
【发布时间】:2018-03-15 13:37:01
【问题描述】:

每次我的子进程死亡时,我都会尝试调用一个函数。这是通过捕获 execve() 发送的 SIGCHLD 来完成的。 我的问题是我的处理函数并不总是被第二次调用。有时是这样,所以我有点困惑。

我的代码是这个:

  #include <stdio.h>
  #include <stdlib.h>
  #include <signal.h> // sigaction()
  #include <time.h>
  #include <errno.h>
  #include <unistd.h>
  #define MAX_LENGTH 1000
  #define BILLION 1E9

  struct timespec start,stop;
  float totalTime = 0;

  void runCommand(int signo)
  {
    printf("Run command called\n");

    float processTime = 0;

    //Get the time when the process ended
    if(clock_gettime(CLOCK_MONOTONIC,&stop) == -1){
      perror(NULL);
    }

    processTime = stop.tv_sec - start.tv_sec + ( stop.tv_nsec - start.tv_nsec ) / BILLION;
    totalTime += processTime;
  }


  int main(int argc, char const *argv[]) {

    int counter = 0;
    int pid = 0;
    int status;
    char *args[4];
    char line[MAX_LENGTH];

    //Setup behaviour for SIGCHLD
    struct sigaction psa;
    psa.sa_handler = runCommand;
    sigaction(SIGCHLD, &psa, NULL);

    FILE *fp = fopen("script_file.txt","r");
    if(!fp)
    {
      return 0;
    }

    //Setup argsgcc
    args[0] = "/bin/sh";
    args[1] = "-c";
    args[3] = NULL;

    while(fgets(line, MAX_LENGTH, fp))
    {
      printf("The command number %d is %s \n", counter, line);

      //Get the time when the process started
      if(clock_gettime(CLOCK_MONOTONIC,&start) == -1){
        perror(NULL);
      }

      //Create a new process
      pid = fork();

      //Child process
      if(pid == 0)
      {
        args[2] = line;
        execve("/bin/sh", args, NULL);
      }
      //Parent process
      else
      {
        wait(&status);
      }

      counter++;
    }

    printf("The overall time was %f seconds.\n", totalTime);

    fclose(fp);


    return 0;
  }

当我尝试运行它时,有时我的结果是:

The command number 0 is mkdir test/

Run command called
The command number 1 is sleep 0.5

Run command called
The command number 2 is sleep 1

Run command called
The command number 3 is rmdir test/

Run command called
The overall time was 1.544909 seconds.

这似乎是正确的。 但在其他情况下,我的结果是:

The command number 0 is mkdir test/

Run command called
The command number 1 is sleep 0.5

The command number 2 is sleep 1

The command number 3 is rmdir test/

The overall time was 0.009810 seconds.

这让我相信 sigaction 并不总是收到我的 SIGCHLD 信号。 当我运行 valgrind 时,出现以下错误:

    ==5407== All heap blocks were freed -- no leaks are possible
    ==5407== 
    ==5407== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
    ==5407== 
    ==5407== 1 errors in context 1 of 2:
    ==5407== Syscall param rt_sigaction(act->sa_flags) points to uninitialised byte(s)
    ==5407==    at 0x4E6F5AE: __libc_sigaction (sigaction.c:62)
    ==5407==    by 0x40098B: main (iv.c:40)
    ==5407==  Address 0xffefff498 is on thread 1's stack
    ==5407==  Uninitialised value was created by a stack allocation
    ==5407==    at 0x400931: main (iv.c:29)
    ==5407== 
    ==5407== 
    ==5407== 1 errors in context 2 of 2:
    ==5407== Syscall param rt_sigaction(act->sa_mask) points to uninitialised byte(s)
    ==5407==    at 0x4E6F5AE: __libc_sigaction (sigaction.c:62)
    ==5407==    by 0x40098B: main (iv.c:40)
    ==5407==  Address 0xffefff4a8 is on thread 1's stack
    ==5407==  Uninitialised value was created by a stack allocation
    ==5407==    at 0x400931: main (iv.c:29)
    ==5407== 
    ==5407== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

但我不确定这是什么意思。

【问题讨论】:

  • 你看到 valgrind 说的是未初始化字节,你觉得是什么意思?
  • 在声明之后和初始化sa_handler 字段之前放置一个memset(&amp;psa, 0, sizeof(psa));。 valgrind 不会再告诉你这些了
  • "Uninitialised value was created by a stack allocation [...] at 0x400931: main (iv.c:29)" 看看iv.c 第29行。
  • 谢谢!好像只是需要初始化的psa
  • printf() 不是异步信号安全函数,不能安全地用于信号处理程序。

标签: c linux signals fork sigaction


【解决方案1】:

最简单的方法是初始化psa是零初始化它:

struct sigaction psa = {0};

但是,也有一些微妙之处。您不能从信号处理程序调用异步信号不安全函数,例如 printf。有关允许的功能列表,请参阅 signal safty。您可以将 printf 替换为 write(2) 调用以避免这种情况:

write(STDOUT_FILENO, "Run command called\n", sizeof "Run command called\n" - 1);

此外,在创建另一个子进程之前,您正在对每个子进程执行wait。因此,任何时候总是只有一个孩子在奔跑。 如果您修改您的程序以允许多个子进程同时运行并且子进程几乎相同死亡,那么并非所有SIGCHLD 信号都可以被捕获。例如,如果信号处理程序正在处理的 SIGCHLD 到达之前是先前的 SIGCHLD,那么它将丢失。因为挂起的信号将被丢弃,除非它们是不同的信号,实时信号除外。例如,如果SIGUSR1SIGCHLD 正在处理(通过信号处理程序)时到达,它将被阻塞;但另一个SIGCHLD 将被忽略。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-26
    • 2020-08-20
    • 1970-01-01
    • 2021-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多