我正在尝试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
in 循环每秒唤醒多次(子线程被 SIGSEGV 信号停止)。这似乎与 Java 出于自己的目的使用 SIGSEGV 的事实有关(请参阅此问题和此帖子)。
所有这些唤醒最终都会大大降低应用程序的速度。所以我想知道我的解决方案是否存在一些缺陷,以及是否有办法让它与 Java 应用程序一起使用。