Ваш родительский процесс знает, когда он выполнил системный вызов fork(). Это не совсем тот момент, когда запускается процесс execv, так как системный вызов execv() занимает некоторое время, но включать это время в подсчет вполне разумно. Если вы принимаете это ограничение, вы можете просто записать время начала как время, когда вы вызвали fork().
Когда дочерний процесс завершится, родитель получит сигнал SIGCHLD. Действие по умолчанию для SIGCHLD — игнорировать его, но вы, вероятно, все равно захотите это изменить. Если вы прикрепите обработчик сигнала к SIGCHLD, то в этом обработчике сигнала вы можете вызывать waitpid (с опцией WNOHANG) до тех пор, пока не получите все уведомления о завершении дочернего процесса. Для каждого уведомления вы записываете время уведомления как время окончания процесса. (Опять же, если система находится под большой нагрузкой, сигнал может отставать от терминации, что приведет к неточности измерения времени. Но в большинстве случаев оно будет точным.)
Очевидно, что родитель должен отслеживать более одного дочернего процесса. Поэтому вам нужно будет использовать дочерний PID для индексации этих значений.
Теперь у вас есть время начала и время окончания для каждого дочернего процесса.
Однако есть небольшая проблема. Вы не можете привязать время начала к PID дочернего процесса, пока вызов fork() не вернется к родительскому процессу. Но вполне возможно, что вызов 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.
Условия гонки (из Википедии)
Сравнить и поменять местами (в Википедии). (Не используйте их пример кода, он не работает. В GCC есть встроенное расширение, реализующее атомарное сравнение и переключитесь на любую архитектуру, которая поддерживает ее. Я знаю, что этот раздел помечен как устаревший, и вам следует использовать более сложные функции в следующем разделе __atomic
, но в данном случае значения по умолчанию подходят. Но если вы используете __atomic_compare_exchange_n
, слава.)
person
rici
schedule
29.09.2018
main
и зарегистрировать обработчик сatexit
. Обработчик может взять вторую метку времени и вычислить разницу. Конечно, если код всегда доходит до концаmain
, то можно просто взять вторую отметку времени там. - person user3386109   schedule 29.09.2018waitpid
, может использоваться для сообщения о разнице во времени. Конечно, вам нужно быть осторожным с кодировкой, так как у вас есть только 8 бит для игры. - person user3386109   schedule 30.09.2018/usr/bin/time your-command arg1 arg2 …
, но командаtime
сама разветвится и выполнит команду для вас. Вы можете написать свой собственный код для выполнения этой работы, возможно, сообщая время в микросекундах или даже наносекундах (хотя точность таких времен может быть немного спорной). Но вы правы, иногда нет положительного ответа на вопросы, заданные на SO; иногда люди просят о невозможном, и тогда лучшее, что можно сказать, это сожаление; вы не можете сделать это, как вы хотели бы. - person Jonathan Leffler   schedule 30.09.2018exit()
с кодом возврата или простоreturn
изmain
с кодом возврата, это значение становится доступным для родителя через аргументstat_loc
дляwaitpid
. Таким образом, начать следует с справочной страницы дляwaitpid
, особенно с той части, где обсуждаетсяWEXITSTATUS
. - person user3386109   schedule 30.09.2018