首页 > 解决方案 > 时间 execv 调用的程序的持续时间

问题描述

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

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

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

先感谢您

标签: ctimeexecv

解决方案


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

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

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

现在您有了每个子进程的开始时间和结束时间。

不过有一个小问题。在 fork() 调用返回到父进程之前,您不能将开始时间附加到子进程的 PID。但是完全有可能 fork() 调用将返回给子级,并且子级将调用 execv() 并且 execv() 的进程在 fork() 调用返回给父级之前全部终止。(诚​​实。它发生了。)

因此,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.

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

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

  • 等待进程()。特别注意用于提取状态信息的宏。

  • 签名()。如何将处理函数分配给信号。如果这对您来说仍然是希腊语,请从signal(7)或 Unix 编程教科书中的相关章节开始。

  • 比赛条件(来自维基百科)

  • 比较和交换(在维基百科上)。(不要使用他们的示例代码;它不起作用。GCC 有一个内置扩展,它在任何支持它的架构上实现原子比较和交换。我知道该部分被标记为遗留,你应该使用下一节中更复杂的功能__atomic,但在这种情况下,默认值很好。但如果你使用__atomic_compare_exchange_n,荣誉。)


推荐阅读