计算 execv 调用的程序的持续时间

Time the duration of a program called by execv

我正在制作一个 C 程序,它使用 fork 和 execv 来并行 运行 其他程序。

我似乎无法计算 execv 调用的程序的执行持续时间,因为新进程在该程序完成后立即死亡 运行ning。另一个问题是无法使用父进程等待子进程完成(我正在使用 waitpid),因为我需要父进程做一些其他工作而不是等待子进程完成。

所以我的问题是:有没有一种方法可以在不使用辅助 fork、pthread 或文本文件的情况下测量 execv 调用的持续时间?

提前致谢

您的 parent 进程知道它何时发出 fork() 系统调用。那不是 execv 进程启动的那一刻 运行,因为 execv() 系统调用需要一些时间,但将这段时间包括在计数中并非完全不合理。如果您接受该限制,您可以将开始时间记录为您调用 fork() 的时间。

当child 终止时,parent 将收到SIGCHLD 信号。 SIGCHLD 的默认操作是忽略它,但您可能仍想更改它。如果您将信号处理程序附加到 SIGCHLD,那么在该信号处理程序中您可以调用 waitpid(使用 WNOHANG 选项),直到您收到所有 child 终止通知。对于每个通知,您将通知时间记录为流程的结束时间。 (同样,如果系统负载过重,信号可能会滞后于终端,导致您的时间测量不准确。但大多数时候,它是准确的。)

显然,parent 需要跟踪多个 child 进程。因此,您需要使用 child 的 PID 来索引这些值。

现在每个 child 进程都有开始时间和结束时间。

不过有一个小问题。在 fork() 调用 return 到 parent 之前,您不能将开始时间附加到 child 进程的 PID。但是完全有可能 fork() 调用 return 到 child,并且 child 将调用 execv() 并且 execv() 的进程在fork() 调用 returned 到 parent。 (老实说。它发生了。)

因此,SIGCHLD 处理程序可能会收到尚未记录开始时间的进程终止通知。

这很容易解决,但是当您这样做时,您需要考虑信号处理程序无法分配内存。因此,如果您在动态分配的存储中记录开始和结束时间信息,则需要在信号处理程序运行之前分配存储。

所以代码看起来像这样:

1. Allocate storage for a new process times table entry
   (PID / start time / end time / status result). Set all
   fields to 0 to indicate that the entry is available.
2. Recall the current time as start_time (a local variable,
   not the table entry).
3. Fork()
4. (Still in the parent). Using an atomic compare-and-swap
   (or equivalent), set the PID of the table entry created
   in step 1 to the child's PID. If the entry was 0 (and is
   now the PID) or if the entry was already the PID, then
   continue to step 6.
5. If the entry has some other non-zero PID, find an empty entry
   in the table and return to step 4.
6. Now record the start time in the table entry. If the table entry
   already has an end time recorded, then the signal handler already
   ran and you know how long it took and what its return status is.
   (This is the case where the child terminated before you got to
   step 4.) You can now report this information.

在 SIGCHLD 信号处理程序中,您需要执行如下操作:

For each successful call to waitpid():
1. Find the entry in the child process information table whose PID
   corresponds to the PID returned by waitpid(). If you find one,
   skip to step 4.
2. Find an empty entry in the child process information table.
   Note that the signal handler cannot be interrupted by the main
   program, so locking is not required here.
3. Claim that entry by setting its PID field to the PID returned by
   waitpid() above.
4. Now that you have an entry, record the end time and return status
   information in the table entry. If the table entry existed
   previously, you need to put the entry on a notification queue
   so that the main process can notify the user. (You cannot call
   printf in a signal handler either.) If the table entry didn't
   exist before, then the main process will notice by itself.

你可能需要画一些图来说服自己上面的算法是正确的并且没有竞争条件。祝你好运。

此外,如果您以前没有做过任何这些事情,您将需要阅读一些书:-)

  • waitpid()。请特别注意用于提取状态信息的宏。

  • sigaction(). How to assign a handler function to a signal. If that's still greek to you, start with signal(7) 或您的 Unix 编程教科书中的相关章节。

  • Race conditions (from Wikipedia)

  • Compare and Swap (on Wikipedia). (Don't use their sample code; it doesn't work. GCC has a built-in extension 在任何支持它的架构上实现原子比较和交换。我知道该部分已标记为旧版,您 应该 在下一节 __atomic 中使用更复杂的函数,但在这种情况下,默认值就可以了。但是如果你使用__atomic_compare_exchange_n,荣誉。)