3

waitpid()プロセスではなく、個々のスレッドを待機するために使用しようとしています。pthread_join()またはstd::thread::join()がスレッドを待つ典型的な方法であることを私は知っています。ただし、私の場合はexecv、プログラムをフォークして (経由で) 実行し、次にいくつかのスレッドを生成する監視アプリケーションを開発しています。そのため、監視アプリケーションからスレッドに参加することはできません。スレッドは別のプロセスに属しており、ソース コードにアクセスできないためです。それでも、これらの個々のスレッドが終了するのを待つことができるようにしたい.

私が達成しようとしていることを簡単に視覚化するために、より明確にすることを望んで、図を含めます。

ここに画像の説明を入力

プロセスを使用するとすべて正常に動作しますがwaitpid、スレッドを待機しません。基本的に、呼び出された直後にwaitpid戻ります(スレッドはその時点でさらに数秒間実行されています)。-1

waitpid州のドキュメント:

Linux カーネルでは、カーネル スケジュール スレッドは、プロセスとは別個の構成体ではありません。代わりに、スレッドは、Linux 固有の clone(2) システム コールを使用して作成される単なるプロセスです。移植可能な pthread_create(3) 呼び出しなどの他のルーチンは、clone(2) を使用して実装されます。Linux 2.4 より前では、スレッドはプロセスの特殊なケースに過ぎず、結果として、別のスレッドが同じスレッド グループに属している場合でも、あるスレッドは別のスレッドの子を待つことができませんでした。しかし、POSIX はそのような機能を規定しており、Linux 2.4 以降では、スレッドは同じスレッド グループ内の他のスレッドの子を待機することができ、デフォルトで待機します。

その説明は、スレッドから他のスレッドの子への待機のみを考慮しています (私の場合、別のプロセスのスレッドの子を待機したい)。しかし、少なくとも、それwaitpidはスレッド対応であることを示しています。

これは私がスレッドを待つために使用しているものです:

std::vector<pid_t> pids;

/* fill vector with thread IDs (LWP IDs) */

for (pid_t pid : pids) {
    int status;
    pid_t res = waitpid(pid, &status, __WALL);
    std::cout << "waitpid rc: " << res << std::endl;
}

このコードは、プロセスの待機には機能しますが、スレッドの待機には失敗します (__WALLフラグが使用されていても)。

実際に を使用してスレッドを待つことができるかどうか疑問に思っていますwaitpid。使用する必要がある他のフラグはありますか? 別のプロセスのスレッドを待つ方法が説明されているドキュメントを教えてください。

参考までに、スレッドの作成に使用しているコードは次のとおりです。

static void foo(int seconds) {
    int tid;
    {
        std::lock_guard<std::mutex> lock(mutex);
        tid = syscall(__NR_gettid);
        std::cout << "Thread " << tid << " is running\n";
        pids.push_back(tid);
        pids_ready.notify_all();
    }

    for (int i = 0; i < seconds; i++)
        std::this_thread::sleep_for(std::chrono::seconds(1));
}

static void create_thread(int seconds) {
    std::thread t(foo, seconds);
    threads.push_back(std::move(t));
}

std::vector<pid_t> create_threads(int num, int seconds) {
    for (int i = 0; i < num; i++)
        create_thread(seconds);

    std::unique_lock<std::mutex> lock(mutex);
    pids_ready.wait(lock, [num]() { return pids.size() == num; });

    return pids;
}

GCC 4.6 と Ubuntu 12.04 を使用しています。

更新:次を使用して機能させることができましたptrace

ptrace(PTRACE_ATTACH, tid, NULL, NULL);
waitpid(tid, &status, __WALL);
ptrace(PTRACE_CONT, tid, NULL, NULL);

while (true) {
    waitpid(tid, &status, __WALL);
    if (WIFEXITED(status)) // assume it will exit at some point
        break;
    ptrace(PTRACE_CONT, tid, NULL, NULL);
}

このコードは、T1、T2、...、Tn がプロセスである場合とスレッドである場合の両方で機能します。

ただし、問題があります。この監視ツールをマルチスレッド C++ アプリケーションで試すと、すべて正常に動作します。しかし、当初の目的は、複数のスレッドを生成する Java アプリケーションでこの監視ツールを使用することでした。マルチスレッド Java アプリケーションを使用するwaitpidと、ループ内で 1 秒間に何度も起動します (子スレッドは SIGSEGV シグナルによって停止されます)。これは、Java が独自の目的で SIGSEGV を使用しているという事実に関連しているようです (この質問この投稿を参照してください)。

これらのウェイクアップはすべて、アプリケーションの速度を大幅に低下させます。私のソリューションに何らかの欠陥があるかどうか、またそれを Java アプリケーションで動作させる方法があるかどうか疑問に思っています。

4

5 に答える 5

3

プロセスのすべてが「正常に機能する」というあなたの主張について、私は少し混乱しています。waitpid任意の他のプロセスではなく、自分の子プロセスのみを待つことができます。実際、それが自分の子プロセスである場合を除いて、プロセス ID を使用することはほぼ確実にバグです。

可能であることが意図されていない何かを行うための醜いハックを探すのではなく、適切なプロセス間通信メカニズムを使用するように設計を修正して、スレッドが完了したときに他のプロセスに通知できるようにしてみませんか? それとも、作業を複数のプロセスやスレッドに分割するのではなく、プログラム全体を単一のプロセス (複数のスレッド) に配置しますか?

于 2012-07-02T14:16:40.630 に答える
2

スレッドグループリーダー(メインスレッドとしても知られる)を除いて、Linuxの他のプロセスのスレッドを待つことはできません。

sys_waitpid最新のLinuxカーネルでは、ラッパーとして実装されており、その周りsys_wait4で.を呼び出しますdo_waitdo_waitプロセスを待つという重労働を行います(スレッドは単なる特別な種類のプロセスです)。現在のタスクの既知の子に対してのみ反復し、__WNOTHREAD指定されていない場合は、同じスレッドグループ内の他のスレッドの子に対して反復します。

ここで面白いのは、clonesyscallを使用してスレッドを作成すると、実際には新しく作成されたスレッドの親が複製されたプロセスの親に設定されますがこの親は新しい子を取得したことを通知されません(そのtask構造のリストに登録されています)。また、スレッドの終了信号が実際にプロセスをコピーする関数によってSIGCHLD設定されているため、クローンが存在する場合は受信しません。-1copy_process

この背後にある理論的根拠は非常に単純です。待機はシングルショット操作です。待機が実行されて完了すると、待機中のプロセスは存在しなくなります。別のプロセスが現在のプロセスのスレッドまたは子で待機することを許可する場合、現在のプロセスからその子で待機を実行する機能を取得します。また、競合状態が発生する可能性がpthread_join()あり、スレッドの1つで他のプロセスが待機しているため、失敗することは絶対にありませんね。

于 2012-07-02T17:45:09.577 に答える
1

わかりました、これは解決策ではありませんが、を使用した解決策があるとは思えない理由の説明ですwaitpid():

1.1 を使用して作成された Linux スレッドclone()は、それらを作成したプロセスの子です。

1.2 これに続いて、スレッドは、スレッドを作成したプロセス (B) を作成したプロセス (A) の孫です

2は、終了した孫のシグナルではトリガーしwaitpid()ません。SIGCHLD

これらすべてが一緒になって、あなたのアプローチがうまくいかない理由を説明しています。

于 2012-07-02T15:34:09.400 に答える
0

/proc/PID/task/Linux では、プロセス PID に属する各スレッドのディレクトリを含むディレクトリを監視できます。

残念ながら、inotify インターフェイスはここでは役に立たないようです。そのため、/proc/PID/task/ディレクトリを繰り返しスキャンしてスレッド ID を探す必要があります。幸いなことに、特にスキャンを 1 秒間に 10 回または多くても数十回しか実行しない場合は、最小のコストで済みます。スレッドがリープされたときではなく、スレッドが終了したときにディレクトリが消えることに注意してください。

TID==PID を持つ 1 つのスレッドは、Linux の元のプロセスです。他のスレッドは昇順で TID を取得します (ただし、最終的にはラップアラウンドします)。TID は pthreads スレッドとは関係がないことに注意してください。どの TID がどの pthread_t にマップされるかを調べるには、実行中のスレッドがgettid()(実際にはsyscall(SYS_gettid));を呼び出す必要があります。/proc/PID/task/TID/そうしないと、TID またはコンテンツだけに基づいて、どのスレッドがどれであるかを判断するのが非常に困難になります。スレッドのターンオーバーのみに関心がある場合 (作成および/または終了した場合)、このインターフェイスは ptrace などよりもはるかに効率的ですが、スレッドの終了検出には遅延があります (ディレクトリのスキャン間隔によって異なります)。 .

于 2012-07-02T20:18:21.147 に答える
0

私の知る限り、waitpid は指定された終了したサブプロを処理するためにのみ使用されます。また、一度に処理されるのを待っているサブプロがたくさんいる場合は、待つよりも安全です。

于 2015-03-23T11:53:04.143 に答える