unix-fork-monitor-child-progress
-
19-08-2019 - |
質問
少しの並列処理が役立つアプリケーションがあります。説明の便宜上、10 個のテキスト ファイルを含むディレクトリがあり、10 個のプロセスをフォークしてそれぞれのファイルから 1 つを取得し、ファイルの内容を大文字にするプログラムを起動したいとします。親プログラムは、次のいずれかを使用して子プログラムが完了するまで待機できることを認めます。 待って 関数を使用するか、 選択する 関数。
私がやりたいのは、親プロセスにフォークされた各プロセスの進行状況を監視させ、プロセスの実行中に進行状況バーのようなものを表示することです。
私の質問。
フォークされたプロセスがこの情報を親に返すための合理的な代替手段は何でしょうか?どのような IPC テクニックを使用するのが合理的でしょうか?
解決
進行状況のみを監視したいこのような状況では、最も簡単な代替手段は共有メモリを使用することです。すべてのプロセスは、共有メモリブロックの進行値(整数など)を更新し、マスタープロセスは定期的にブロックを読み取ります。基本的に、このスキームではロックは必要ありません。また、<!> quot; polling <!> quot;です。マスターは必要なときにいつでも情報を読み取ることができるため、進行状況データを処理するためのイベント処理は必要ありません。
他のヒント
必要な唯一の進捗が<!> quot;いくつのジョブが完了したか?<!> 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;
}
エラー処理などは明確にするため省略しました。
いくつかのオプション (「ファイルを大文字にする」の例えとは対照的に、実際に何をしているかによって大きく異なります)。
- 信号
- FIFO / 名前付きパイプ
- 子または他の渡されたハンドルの STDOUT
- メッセージキュー (該当する場合)
進行状況の更新のみが必要な場合は、おそらく最も簡単な方法は匿名パイプを使用することです。 pipe(2)呼び出しは、パイプの両端に1つずつ、2つのファイル記述子を提供します。フォークする直前に呼び出して、親に最初のfdを聞かせ、子に2番目のfdを書かせます。 (これは、ファイル記述子とそれらを含む2要素配列の両方がプロセス間で共有されるため機能します。共有メモリ自体ではありませんが、コピーオンライトなので、上書きしない限り値を共有します。)
今日、先日、誰かが常にパイプを使用していることを教えてくれました。これにより、子はすべてが順調に進んでいるという通知を親プロセスに送信できます。これはまともな解決策のようで、エラーを出力したいがstdout / stderrなどにアクセスできない場所では特に有用です。
Boost.MPI は、このシナリオで役立つはずです。あなたはそれを過剰と考えるかもしれませんが、それは間違いなく調査する価値があります:
www.boost.org/doc/html/mpi.html