unix-fork-monitor de criança-progress
-
19-08-2019 - |
Pergunta
Eu tenho uma aplicação onde um pouco de processamento paralelo seria de benefício. Para efeitos da discussão, digamos que há um diretório com 10 arquivos de texto na mesma, e quero iniciar um programa, que garfos Off 10 processos, cada uma tomada dos arquivos, e maiúscula o conteúdo do arquivo. Eu reconheço que o programa pai pode esperar que as crianças a completar usando um dos espera funções, ou usando o selecione função .
O que eu gostaria de fazer é ter monitorar o processo pai o andamento de cada processo de mentiroso, e exibir algo como uma barra de progresso como os processos são executados.
minha pergunta.
O que seria uma alternativas razoáveis ??que eu tenho para os processos bifurcados para comunicar isso de volta informações para o pai? Que técnicas IPC seria razoável uso?
Solução
Neste tipo de situação onde você só deseja monitorar o progresso, a alternativa mais fácil é usar memória compartilhada. Todo processo de atualiza-lo progredir valor (por exemplo, um inteiro) em um bloco de memória compartilhada, eo processo mestre lê o bloco regularmente. Basicamente, você não precisa de qualquer bloqueio neste esquema. Além disso, é um "polling" aplicação estilo, porque o mestre pode ler as informações sempre que quiser, para que você não precisa de qualquer processamento de eventos para lidar com os dados de progresso.
Outras dicas
Se o único progresso que você precisa é "quantos postos de trabalho ter concluído?", Então um simples
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);
}
vai fazer. Para relatar o progresso enquanto, bem, em andamento, aqui está implementações mudos de algumas das outras sugestões.
Pipes:
#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;
}
memória compartilhada:
#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;
}
Tratamento de erros etc. elidido para maior clareza.
Algumas opções (nenhuma idéia que, se houver, vai servi-lo - muito depende do que você está realmente fazendo, como op à analogia "maiúscula arquivos"):
- sinais
- fifos / pipes
- o STDOUT dos filhos ou outras alças passadas
- filas de mensagens (se apropriado)
Se tudo que você quer é uma atualização de progresso, de longe a maneira mais fácil é provavelmente usar um pipe anônimo. O (2) chamada tubo irá dar-lhe dois descritores de arquivos, um para cada extremidade do tubo. Chamá-lo um pouco antes de garfo, e ter o pai ouvir a primeira fd ea gravação criança para o segundo. (Isso funciona porque ambos os descritores de arquivo e a matriz de dois elementos que os contenham são compartilhados entre os processos -. Não memória compartilhada per se, mas é copy-on-write para que eles compartilham os valores a menos que você substituí-los)
Apenas mais cedo hoje alguém me disse que eles sempre usam um tubo, através do qual as crianças podem enviar uma notificação para o processo pai que tudo está indo bem. Esta parece ser uma solução decente, e é especialmente útil em lugares onde você gostaria de imprimir um erro, mas já não têm acesso a stdout / stderr, etc.
Boost.MPI deve ser útil neste cenário. Você pode considerar um exagero, mas é definitivamente vale a pena investigar:
www.boost.org/doc/html/mpi.html