我正在制作一个使用 fork 和 execv 并行运行其他程序的 C 程序。
我似乎无法计算 execv 调用的程序的执行持续时间,因为新进程在该程序运行完毕后立即终止。另一个复杂之处是无法使用父进程等待子进程完成(我正在使用 waitpid),因为我需要父进程做一些其他工作而不是等待子进程完成。
所以我的问题是:有没有一种方法可以在不使用辅助 fork、pthread 或文本文件的情况下测量 execv 调用的持续时间?
提前谢谢
最佳答案
您的父进程知道它何时发出 fork() 系统调用。这并不是 execv'd 进程开始运行的时刻,因为 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.
您可能需要画一些图表来说服自己上述算法是正确的并且没有竞争条件。祝你好运。
此外,如果您以前没有做过任何这些事情,您将需要阅读一些内容:-)
waitpid() 。请特别注意用于提取状态信息的宏。
sigaction() 。如何将处理函数分配给信号。如果这对您来说仍然是希腊语,请从 signal(7) 开始或 Unix 编程教科书中的相关章节。
Compare and Swap (on Wikipedia) 。 (不要使用他们的示例代码;它不起作用。GCC 有一个 built-in extension,它在任何支持它的体系结构上实现原子比较和交换。我知道该部分被标记为遗留,并且您 应该在下一节
__atomic
中使用更复杂的函数,但在这种情况下,默认值就可以了。但如果您使用__atomic_compare_exchange_n
,那就太好了。)
关于c - 计算 execv 调用的程序的持续时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52572005/