unix-форк-монитор-дочерний элемент-прогресс
-
19-08-2019 - |
Вопрос
У меня есть приложение, в котором было бы полезно немного параллельной обработки.Для целей обсуждения предположим, что есть каталог с 10 текстовыми файлами в нем, и я хочу запустить программу, которая разветвляет 10 процессов, каждый из которых берет один из файлов и вводит содержимое файла в верхний регистр.Я признаю, что родительская программа может дождаться завершения дочерних программ, используя один из подожди функций, или с использованием выберите функция.
Что я хотел бы сделать, так это заставить родительский процесс отслеживать ход выполнения каждого разветвленного процесса и отображать что-то вроде индикатора выполнения по мере выполнения процессов.
Мой вопрос.
Какие у меня были бы разумные альтернативы для разветвленных процессов для передачи этой информации обратно родительскому?Какие методы IPC было бы разумно использовать?
Решение
В такой ситуации, когда вы хотите отслеживать только прогресс, самая простая альтернатива - использовать разделяемую память. Каждый процесс обновляет свое значение прогресса (например, целое число) в блоке общей памяти, и мастер-процесс регулярно читает блок. По сути, вам не нужна блокировка в этой схеме. Кроме того, это & Quot; polling & Quot; стиль приложения, потому что мастер может читать информацию в любое время, поэтому вам не нужно обрабатывать события для обработки данных о ходе выполнения.
Другие советы
Если единственный прогресс, который вам нужен, - это "сколько заданий выполнено?", то простой
while (jobs_running) {
pid = wait(&status);
for (i = 0; i < num_jobs; i++)
if (pid == jobs[i]) {
jobs_running--;
break;
}
printf("%i/%i\n", num_jobs - jobs_running, num_jobs);
}
сделаю.Чтобы сообщить о прогрессе, который, ну, в общем, в процессе выполнения, вот глупые реализации некоторых других предложений.
Трубы:
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
int child(int fd) {
int i;
struct timespec ts;
for (i = 0; i < 100; i++) {
write(fd, &i, sizeof(i));
ts.tv_sec = 0;
ts.tv_nsec = rand() % 512 * 1000000;
nanosleep(&ts, NULL);
}
write(fd, &i, sizeof(i));
exit(0);
}
int main() {
int fds[10][2];
int i, j, total, status[10] = {0};
for (i = 0; i < 10; i++) {
pipe(fds[i]);
if (!fork())
child(fds[i][1]);
}
for (total = 0; total < 1000; sleep(1)) {
for (i = 0; i < 10; i++) {
struct pollfd pfds = {fds[i][0], POLLIN};
for (poll(&pfds, 1, 0); pfds.revents & POLLIN; poll(&pfds, 1, 0)) {
read(fds[i][0], &status[i], sizeof(status[i]));
for (total = j = 0; j < 10; j++)
total += status[j];
}
}
printf("%i/1000\n", total);
}
return 0;
}
Общая память:
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
int child(int *o, sem_t *sem) {
int i;
struct timespec ts;
for (i = 0; i < 100; i++) {
sem_wait(sem);
*o = i;
sem_post(sem);
ts.tv_sec = 0;
ts.tv_nsec = rand() % 512 * 1000000;
nanosleep(&ts, NULL);
}
sem_wait(sem);
*o = i;
sem_post(sem);
exit(0);
}
int main() {
int i, j, size, total;
void *page;
int *status;
sem_t *sems;
size = sysconf(_SC_PAGESIZE);
size = (10 * sizeof(*status) + 10 * sizeof(*sems) + size - 1) & size;
page = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
status = page;
sems = (void *)&status[10];
for (i = 0; i < 10; i++) {
status[i] = 0;
sem_init(&sems[i], 1, 1);
if (!fork())
child(&status[i], &sems[i]);
}
for (total = 0; total < 1000; sleep(1)) {
for (total = i = 0; i < 10; i++) {
sem_wait(&sems[i]);
total += status[i];
sem_post(&sems[i]);
}
printf("%i/1000\n", total);
}
return 0;
}
Обработка ошибок и т.д.исключено для ясности.
Несколько вариантов (не знаю, какой из них, если таковой имеется, вам подойдет - многое зависит от того, что вы на самом деле делаете, в соответствии с аналогией с "файлами в верхнем регистре"):
- сигналы
- fifos / именованные каналы
- стандартный вывод дочерних элементов или других переданных дескрипторов
- очереди сообщений (при необходимости)
Если все, что вам нужно, это обновление прогресса, то, безусловно, самый простой способ - это использовать анонимный канал. Вызов pipe (2) даст вам два файловых дескриптора, по одному для каждого конца канала. Вызовите его перед тем, как разветвляться, и пусть родитель слушает первый fd, а ребенок пишет второму. (Это работает, потому что как файловые дескрипторы, так и двухэлементный массив, содержащий их, совместно используются процессами - не разделяемая память как таковая, но она копируется при записи, поэтому они разделяют значения, если вы их не перезаписываете.)
Просто сегодня утром кто-то сказал мне, что они всегда используют канал, по которому дети могут отправлять уведомление родительскому процессу, что все идет хорошо. Это кажется приемлемым решением и особенно полезно в тех местах, где вы хотите напечатать ошибку, но больше не имеете доступа к stdout / stderr и т. Д.
Boost.MPI должен быть полезен в этом сценарии. Вы можете считать это излишним, но это определенно стоит исследовать:
www.boost.org/doc/html/mpi.html р>