You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于LD_PRELOAD的多线程定期暂停与检测工具开发问题

基于LD_PRELOAD的多线程定期暂停检测工具解决方案

一、获取所有线程ID(无需Hook pthread_create)

Linux下可以直接通过/proc/self/task目录获取所有活跃线程的ID——这个目录下的每个子目录名就是线程的TID,而Linux的pthread_t本质就是TID的整数类型,直接转换即可。这种方式完全不需要拦截pthread_create,开销极低:

#include <dirent.h>
#include <vector>
#include <cstdlib>
#include <pthread.h>
#include <cstring>

std::vector<pthread_t> get_all_threads() {
    std::vector<pthread_t> threads;
    DIR* dir = opendir("/proc/self/task");
    if (!dir) return threads;

    struct dirent* entry;
    while ((entry = readdir(dir)) != nullptr) {
        if (entry->d_type == DT_DIR && strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
            pthread_t tid = static_cast<pthread_t>(strtoul(entry->d_name, nullptr, 10));
            threads.push_back(tid);
        }
    }
    closedir(dir);
    return threads;
}

二、实现全线程信号投递

原来的ITIMER_REAL定时器只会把信号发给进程内随机一个线程,无法满足需求。改用timer_create创建线程触发式定时器,每次定时器到期时,遍历所有线程并通过pthread_kill发送自定义信号:

#include <signal.h>
#include <time.h>

extern "C" void thread_signal_handler(int sig) {
    // 这里写每个线程的暂停检测逻辑
}

void timer_callback(union sigval arg) {
    auto threads = get_all_threads();
    for (pthread_t tid : threads) {
        // 用SIGUSR1避免和系统默认信号逻辑冲突
        pthread_kill(tid, SIGUSR1);
    }
}

struct Initialization {
    Initialization() {
        // 注册信号处理函数
        struct sigaction sa;
        sa.sa_handler = thread_signal_handler;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_RESTART; // 防止信号中断系统调用
        sigaction(SIGUSR1, &sa, nullptr);

        // 创建定时器
        timer_t timerid;
        struct sigevent sev;
        sev.sigev_notify = SIGEV_THREAD; // 用独立线程执行回调
        sev.sigev_value.sival_ptr = &timerid;
        sev.sigev_notify_function = timer_callback;
        sev.sigev_notify_attributes = nullptr;

        timer_create(CLOCK_REALTIME, &sev, &timerid);

        // 设置1秒周期触发
        struct itimerspec its;
        its.it_value.tv_sec = 1;
        its.it_value.tv_nsec = 0;
        its.it_interval.tv_sec = 1;
        its.it_interval.tv_nsec = 0;

        timer_settime(timerid, 0, &its, nullptr);
    }
};

static Initialization _;

三、修复thread_local初始化失效问题

LD_PRELOAD场景下thread_local变量的初始化可能受加载时机影响失效,改用pthread_key_create实现线程本地存储是更可靠的方案:

#include <pthread.h>

// 定义线程本地数据的全局key
static pthread_key_t thread_local_key;

// 线程退出时自动清理资源的销毁函数
static void thread_local_destructor(void* data) {
    delete static_cast<YourDataType*>(data);
}

// 初始化key的全局对象
struct ThreadLocalKeyInit {
    ThreadLocalKeyInit() {
        pthread_key_create(&thread_local_key, thread_local_destructor);
    }
};

static ThreadLocalKeyInit thread_local_key_init;

// 获取线程本地数据(首次访问自动初始化)
YourDataType* get_thread_local_data() {
    YourDataType* data = static_cast<YourDataType*>(pthread_getspecific(thread_local_key));
    if (!data) {
        data = new YourDataType();
        pthread_setspecific(thread_local_key, data);
    }
    return data;
}

注意事项

  • /proc/self/task的方式仅适用于Linux,若需兼容其他Unix系统需另行调整,但Linux是LD_PRELOAD工具的主要目标环境。
  • 优先使用SIGUSR1/SIGUSR2这类自定义信号,避免干扰应用自身的信号逻辑。
  • SIGEV_THREAD模式的定时器回调会在独立线程执行,不会阻塞原有业务线程,开销可控。

内容的提问来源于stack exchange,提问作者user2345

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 01:57:38