Время продолжительность программы, вызываемой execv - PullRequest
0 голосов
/ 29 сентября 2018

Я делаю программу на C, которая использует fork и execv для параллельного запуска других программ.

Кажется, я не могу рассчитать время выполнения программы, вызванной execv, так какновый процесс умирает сразу после завершения работы программы.Другая сложность заключается в невозможности использования родительского процесса для ожидания завершения дочернего процесса (я использую waitpid), потому что мне нужно, чтобы родительский процесс выполнял какую-то другую работу, а не ожидал завершения дочернего процесса.

Итак, мой вопрос: есть ли способ измерить продолжительность вызова execv без использования вспомогательного разветвления, pthread или текстового файла?

Заранее спасибо

1 Ответ

0 голосов
/ 30 сентября 2018

Ваш родительский процесс знает, когда он выполнил системный вызов fork ().Это не совсем тот момент, когда процесс execv'd запускается, поскольку системный вызов 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, слава.)

...