Unix-fork-monitor-child-progress
-
19-08-2019 - |
题
我有一个应用程序,其中一些并行处理会有所帮助。为了便于讨论,假设有一个目录,其中有 10 个文本文件,我想启动一个程序,该程序分叉 10 个进程,每个进程获取其中一个文件,并将文件内容大写。我承认父程序可以等待子程序使用其中之一完成 等待 函数,或使用 选择 功能。
我想做的是让父进程监视每个分叉进程的进度,并在进程运行时显示类似进度条的内容。
我的问题。
对于分叉进程来说,将这些信息传递回父进程有什么合理的选择?使用哪些 IPC 技术是合理的?
解决方案
在这种情况下,你只想要监控的进展,最简单的办法是使用共享内存。每过程更新它的共享存储器块的进展值(例如整数),并且主过程定期读取块。基本上,你不需要在这个方案中的任何锁定。此外,它是一种“投票”式的应用程序,因为主机可以读取,只要它想要的信息,所以你不需要任何事件处理处理进度数据。
其他提示
如果你需要的唯一的进步是“有多少工作已经完成了?”,然后进行简单
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/命名管道
- 子级或其他传递的句柄的 STDOUT
- 消息队列(如果适用)
如果你想要的是进度更新的,迄今为止最简单的方法可能是使用一个匿名管道。管道(2)调用给你两个文件描述符,一个用于管道的两端。只是你之前把它叉,并有家长听取第一FD和儿童写信给第二位。 (这样做是因为两个文件描述符和包含它们的二单元阵列的过程之间共享 - 本身不共享存储器,但所以除非你覆盖它们它们共享值是写入时复制)
就在今天早些时候有人告诉我,他们总是用一管,由孩子们可以发送通知,所有进展顺利父进程。这似乎是一个不错的解决方案,并在地方,你想打印错误特别有用,但不再有机会获得标准输出/标准错误,等等。
Boost.MPI 应该是在这种情况下是有用的。您可能会矫枉过正考虑它,但它绝对值得探讨:点击 www.boost.org/doc/html/mpi.html