【问题标题】:waitpid does not give me consistent resultswaitpid 没有给我一致的结果
【发布时间】:2020-02-25 16:10:04
【问题描述】:

编辑:我首先要澄清的是,当 waitpid 不起作用时,它不适用于所有进程。 按照建议,我打印了 waitpid 的返回值并收到了有趣的结果。首先,在不成功的运行过程中,即使WIFEXITED(stats)返回1,waitpid()也会返回0。子进程怎么可能没有状态变化但返回完成?

其次,我使用了一个虚拟程序,它每 1 秒打印出一个字符串参数并持续指定的次数。 (这就是我跟踪程序是否完成的方式)。我注意到在成功运行期间,在上下文切换期间并没有打印出 waitpid 值,而是在所有进程完成运行之后。

像这样:(这里假设每个 prog 需要 2 个配额才能完成) “prog1 运行” “prog2 运行” “prog3 运行” “prog1 运行” “prog2 运行” “prog3 运行” 等待PID:0 等待PID:0 等待PID:0 ...

另一方面,一次不成功的运行给了我这个: “prog1 运行” 等待PID:0 检测到程序终止 “prog2 运行” 等待PID:0 检测到程序终止 “prog3 运行” 等待PID:0 检测到程序终止

TLDR:waitpid(child_PID, stat, WNOHANG) 是否可以在同一程序的不同运行中给出不同的 WIFEXITED(stat)?

我正在编写循环调度程序。父进程派生出 n 个子进程,每个子进程在调度程序中运行一个进程。使用信号 SIGCONT 和 SIGSTOP 以及 usleep() 函数,父进程能够为每个子进程分配指定的时间配额,以便在一个循环中按顺序运行。 在每个配额结束时,父进程会检查是否有任何进程已完成。它使用 waitpid(child_PID, stat, WNOHANG);然后是 WIFEXITED(stat)。如果进程已完成,则父进程在后续循环中不会再为该进程分配任何时间配额。

但是,我注意到,在我运行代码的每隔一段时间,WIFEXITED(stat) 在第一个配额周期后给我一个 1,即使我认为我已经确保每个进程的运行时间都应该比所述配额长得多。我知道程序不应该完成,因为我的测试程序涉及在退出之前打印指定数量的行。最奇怪的是,WIFEXITED 每次运行都给我错误的结果,而且在第一个周期。

我已经包含了代码,以防有人有足够的耐心阅读它。希望不需要阅读代码来理解问题。对于那些愿意阅读它的人,谢谢,这意味着很多,也许您可​​能知道为什么我的程序没有终止?当 t 正确运行时,它会正确调度所有进程并运行它们直到所有进程都终止,但不会自行终止。

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <stdbool.h>

int main(int argc, char *argv[]) {
  int tick_interval = 10000;
  char opt;
  while ((opt = getopt(argc, argv, "t:")) != -1) {
    switch (opt) {
    case 't':
      tick_interval = atoi(optarg);
      break;
    default:
      goto usage;
    }
  }

  if (optind >= argc) {
    goto usage;
  }

  char *filepath = argv[optind];//filepath to textfile containing name of programs and arguments.
  int n;
  FILE * fp;
  char * line = NULL;
  size_t len = 0;
  ssize_t read;

  printf("parent PID: %d\n", getpid());
  fp = fopen(filepath, "r");
  if (fp == NULL)
      exit(EXIT_FAILURE);
  int PID;
  int *prog_tracker = malloc(0);
  int line_counter = 0;
  int word_counter;
  int word_length;
  char ***lines = malloc(0);
  while ((read = getline(&line, &len, fp)) != -1) {
      //printf("round %d\n", line_counter);
      word_counter = 0;
      word_length = 0;
      lines = realloc(lines, (++line_counter) * sizeof(char**));
      lines[line_counter - 1] = malloc(0);
      int char_counter;
      bool is_new = 1;
      for (char_counter = 0; char_counter < read; char_counter ++) {
          if (is_new) {
              is_new = 0;
              lines[line_counter - 1] = realloc(lines[line_counter - 1], ++word_counter * sizeof(char*));
              lines[line_counter - 1][word_counter - 1] = malloc(0);
          }
          lines[line_counter - 1][word_counter - 1] = realloc(lines[line_counter - 1][word_counter - 1], ++word_length * sizeof(char));
          if (line[char_counter] == ' '||line[char_counter] == '\0' || line[char_counter] == '\n' || line[char_counter] == EOF) {
              is_new = 1;
              lines[line_counter - 1][word_counter - 1][word_length - 1] = '\0';
              word_length = 0;
          } else {
              lines[line_counter - 1][word_counter - 1][word_length - 1] = line[char_counter];
          }
      }
      //first line states number of cores to be used. To be implemented. Ignored for now.
      if (line_counter != 1) {
          PID = fork();
          if (PID != 0) {
              printf("PID: %d created at: %d\n", PID, line_counter);
              kill(PID, SIGSTOP);
              prog_tracker = realloc(prog_tracker, (line_counter - 1)  * sizeof(int));
              prog_tracker[line_counter - 2] = PID;
          } else {
              char *arguments[word_counter + 1];
              int counter;
              for (counter = 0; counter < word_counter; counter ++) {
                  arguments[counter] = lines[line_counter - 1][counter];
              }
              arguments[word_counter] = NULL;
              execv(arguments[0], arguments);//child processes implement processes in file.
              break;
          }
      }
  }
  free(lines);
  fclose(fp);
  if (line)
      free(line);

  if (PID != 0) {
      printf("parent running %d\n", getpid());
      int proc_num = 0;
      int prog_num = line_counter - 1;
      printf("prog_num: %d\n", prog_num);
      while (prog_num != 0) { //The while loop should break when all programs have finished, but it does not.
          kill(prog_tracker[proc_num], SIGCONT);
          usleep(tick_interval * 1000);
          kill(prog_tracker[proc_num], SIGSTOP);
          int stat;
           printf("status: %d", waitpid(prog_tracker[proc_num], &stat, WNOHANG)); //I now print out the return of waitpid.
          printf("%d\n", WIFEXITED(stat));
          if (WIFEXITED(stat)) {
              //printf("%d\n", WIFEXITED(stat));
              printf("program termination detected\n");
              prog_tracker[proc_num] = 0;
              prog_num -= 1;
              printf("processes left %d\n", prog_num);
          }
          proc_num = (++proc_num) % (line_counter - 1);
          while(prog_tracker[proc_num] == 0) {
              proc_num = (++proc_num) % (line_counter - 1);
          }
       }
       printf("All programs ended.");//This never gets printed!
  }
  return 0;

【问题讨论】:

  • 在尝试使用stat之前,您应该检查waitpid()的返回值以确保它成功。
  • 对于相对较大的程序,很难发现可能的错误。您应该简化代码以创建minimal reproducible example。我建议用固定值替换命令行参数和输入文件的处理。添加调试输出以查看waitpid 的结果属于哪个PID。检查所有函数的返回值,可能某个函数报错。
  • 贴出的代码无法编译!除其他外,它缺少最后一个 }
  • 发布的代码导致编译输出大量警告,包括这个关键警告:untitled1.c:108:20: warning: 'proc_num' 上的操作可能未定义 [-Wsequence-点] 并再次在第 110 行
  • OT:建议在编译时始终启用警告,然后修复这些警告。 (对于gcc,至少使用:-Wall -Wextra -Wconversion -pedantic -std=gnu11)注意:其他编译器使用不同的选项来产生相同的结果

标签: c waitpid


【解决方案1】:

这种代码:

        PID = fork();
        if (PID != 0) 
        {   << then running parent process or error occurred
            printf("PID: %d created at: %d\n", PID, line_counter);
            kill(PID, SIGSTOP);
            prog_tracker = realloc(prog_tracker, (line_counter - 1)  * sizeof(int));
            prog_tracker[line_counter - 2] = PID;
        }

如果对fork() 的调用成功,则会杀死子进程。可能不是你想要的。

【讨论】:

  • 嗨!谢谢回复!我可以知道为什么它是子进程吗?父母以后不能用 SIGCONT 继续子进程吗?
  • fork() 返回时,有三种可能的结果。 1) 0 是子进程的PID(父进程正在执行)
【解决方案2】:

waitpid() 可以并且很可能会在示例中给出不一致的结果。 如果您像几乎总是应该的那样正确初始化它的状态变量(“int stat = 0;”),您会立即找出原因,因为代码根本无法按预期工作。这只是一个例子,说明 C 程序员必须具备至少某种程度的抗错误编码风格!

这种行为的原因是,如果请求的进程仍在运行,那么 waitpid() 只会返回 0 的值,甚至不会触及您的“stat”变量,因此它保持未初始化状态。您可能可以做一些肮脏的黑客攻击来解决这个问题,例如,每次使用 0xFF 之类的东西初始化“stat”,但正确的方法是检查 waitpid(... WNOHANG) 的返回值并只处理其余的如果返回值不为 0。在大多数情况下,这实际上是您唯一需要检查的内容,甚至不需要“WIFEXITED(stat)”,除非您真的对子进程终止的准确程度和返回值感兴趣.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-24
    • 2011-10-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多