【问题标题】:Keeping track of child processes in Unix在 Unix 中跟踪子进程
【发布时间】:2014-02-01 10:53:48
【问题描述】:

我已经完成了一个 C 程序,它产生了许多进程,然后在短时间内杀死它们。我对此并不陌生,并试图弄清楚为什么我试图跟踪我的进程以杀死工作的方式。我的指针 pid_t* id 指向创建时 cmd 行参数要求的进程 ID 的数量。

现在这是我的难题。 fork() 为子级和父级返回一个值,但我找不到关于它如何工作的固定顺序。它是先返回子值还是父值,还是未定义?

id[] 数组对于每个生成的进程都是相同的,对吧(因为没有生成 n_child 数组)?

由于程序 100% 的时间都在工作,因此似乎总是最后返回父级,因为这是在终止过程中存储在数组中的内容。这是否是跟踪流程的“安全”方式(请记住,我不是在寻找最好的方式或任何东西,因为我确信有很多更好的方式)?似乎答案应该是否定的,并且我应该只在确定它是父数组时才设置数组。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>

#define MAX_N_CHILD 10

int main (int argc, char **argv) {
  int n_child, i;
  pid_t* id; 
  /* note: on a 32 bit machine, pid_t is defined as the __S32_TYPE,
     which is an int rather than a long */

  if (argc == 2) { 
    n_child = atoi (argv[1]); /* captures the command line argument */
    /* **NOTE** argv[0] is always the file name of this program */

    if (n_child > MAX_N_CHILD) {
      printf ("Too many children wanted!\n");
      return 0;
    }

  }
  else {
    printf ("Invalid number of arguments!\n");
    return 0;
  }

  id = malloc( sizeof(pid_t) * n_child );
  if(id == NULL)
  {
    return 0;
  }

  printf ("********  HELLO!  *********\n");
  printf ("parent %d(CPU#%d)\n", getpid(), sched_getcpu());


  /* create new process(es) */
  for(i = 0; i < n_child; ++i)
  {
    id[i] = fork(); 

    if (id[i] == -1) 
    {
      printf("Error: Process not created");
      return 0;
    }
    else if (id[i] == 0) { /* I'm the child */
      //execlp ("./dummy", "dummy", NULL); /* replace myself with a new program */
      sleep(2);
    }
    else
    { 
      continue; //Continue loop
    }

  } //End for

      /* Only the parent process should get here. */

      /* wait a little to let the child processes run before killing them */
      usleep(50000); /* sleep for 50000 microseconds */


      /* kill */
      for(i = 0; i < n_child; ++i)
      {
        printf ("killing %d\n", id[i]);
        kill (id[i], SIGKILL); /* SIGKILL is defined in signal.h */
      }

      //pkill -TERM -P id  //Only kills immediate children of parent

      printf ("All %d child processes killed!\n", n_child);
}

【问题讨论】:

    标签: c unix fork


    【解决方案1】:

    现在这是我的困惑。 fork() 为子项和父项返回一个值,但我找不到关于它如何工作的固定顺序。它是先返回子值还是父值,还是未定义?

    嗯……不……不完全是。

    fork 中发生的情况如下(有效):

    • 您的单个​​进程被克隆到一个相同的子进程中。几乎所有的属性都被复制了。
    • 但是,子进程将拥有一个新的进程 ID (pid)
    • 在子进程中,fork 返回一个0
    • 在父进程中,fork返回子进程的新pid。

    来自fork 的两个返回同时发生在进程的两个不同副本中。实际上,它们之间的唯一区别是它们从fork 获得的结果。

    没有顺序:这些几乎肯定会同时出现在两个 CPU 内核上,即使在单核系统上,它们也会出现在 任意顺序并且可能一次执行单个机器代码指令,一个与另一个交错。

    换个角度来看,顺序被明确定义为同时发生。

    fork 的那一刻,一切都变成了两个副本。所以,一方面,是的,现在存在 n 个数组 id[] 的副本;然而,每个孩子的副本将只有在它之前产生的孩子的 id(甚至没有它自己的)。

    sleep 替换exec(在这种情况下为execlp)会导致程序徘徊在……非常奇怪的领域。你写了/* Only the parent process should get here */,但sleep正确的。

    使用exec* 函数,您可以销毁进程的内容并用新程序替换它(保留其 pid 和一些其他属性)。假设 exec 没有失败,那么确实,您的程序将不再是子进程中的程序,并且不会到达该行;

    但是,正如所写,您有一个奇怪的竞争条件。 sleepusleep 可以为程序花费的时间设置一个下限,但是如果由于某种原因父进程没有足够快地杀死它的子进程,那么子进程将开始尝试 (edit 忘了这一点)完成for 循环并产生更多的孩子,然后杀死彼此和他们的新孩子。由于每个孩子的id[] 被复制到forked 之前,因此它会尝试杀死所有“年长的兄弟姐妹”,然后为自己和它产生的其他孩子杀死pid=0。

    您还在问题中提到了生成 threads,但您的问题代码中没有任何内容与线程有关。

    【讨论】:

    • 我不想将太多代码复制到窗口中,因为这是一个小问题。所以我只是随机放置 sleep(2) 而不是另一个代码块,以防有人运行它。我知道这是不可取的。谢谢你的回答。我的意思是流程,哎呀..正在改变。
    【解决方案2】:

    我在这里看到一个问题。行:

    execlp ("./dummy", "dummy", NULL); /* replace myself with a new program */
    

    在您的原始程序中非常重要。如果它被注释掉,孩子将继续循环并在循环中产生它自己的子孩子。因此,您将创建 (n*(n-1))/2 个进程,然后这些进程会以不可预知的方式退出或被杀死。

    你的时间也很紧。尝试使用数十秒的延迟,以便您可以手动查看系统中发生的情况。

    否则程序对我来说似乎没问题。

    关于新进程创建后的执行顺序,它会以与其父进程相同的方式竞争 CPU。所以基本上无法预测哪个会先运行以及运行多长时间。如果您的 CPU 中有多个内核,它们实际上可以同时运行。

    【讨论】:

    • 不不,您可以看到父级在开始终止之前只等待 50,000 微秒(0.05 秒)。 sleep(2) 将为每个孩子等待 2 秒。这不是问题,但我谢谢你。链接到的原始程序是该 cpu 的浪费时间的程序。我只是询问 fork(),它是如何工作的,以及跟踪子进程。不过我确实理解你的心情。
    • 我特别想知道为什么在某些地方 id[i] 总是孩子的 id(由父母设置)而不是 0(意味着由孩子设置)。我想我要么走运,要么对流程的运作方式有明显的误解。
    • 你可以想象fork创建了整个过程的一个新副本,包括整个数组id。所以 id[i] 在子数组的副本中设置为 0。它在父母副本中设置为孩子的pid。因此,父进程的 id 仅包含子进程的 pid。
    • @user2079828 这取决于fork 的工作方式。 Fork 创建整个过程的副本,包括堆的副本(以及您的数组id)。如果您使用线程,情况会有所不同,因为新线程将共享数组。
    【解决方案3】:

    当你调用fork时,父进程和子进程各自拥有自己所有变量的副本;它有效地制作了父进程的快照并将其复制到子进程。两个进程各自独立运行,fork函数在both进程中同时返回;在父进程中返回子进程的PID,在子进程中返回0。

    所以顺序无关紧要。每个进程都有自己的id 数组,它们分配给自己的id[i] 私有副本。

    【讨论】:

    • 好的,这回答了我的问题。我读到它制作了源代码的“副本”,包括PC和其他东西。所以基本上,id* 指针的 malloc() 被复制了 n 次。
    猜你喜欢
    • 1970-01-01
    • 2010-12-24
    • 1970-01-01
    • 1970-01-01
    • 2011-01-23
    • 2016-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多