一个基于 LD_PRELOAD 的线程调度管理系统,支持动态管理线程调度策略和标记,适用于实时系统、性能调优和资源管理场景。
- 自动线程追踪:通过 LD_PRELOAD hook 自动捕获线程创建事件,无需修改应用程序代码
- 标记继承机制:子线程自动继承父线程的所有标记,便于线程分类和管理
- 灵活的调度策略:支持 Linux 所有主流调度策略(SCHED_OTHER、SCHED_FIFO、SCHED_RR、SCHED_DEADLINE)
- 多语言支持:提供 C/C++、Python、Rust 三种语言的 API 绑定
- 标记格式验证:确保标记只包含字母和下划线,保证一致性和可读性
sched_client_set_policy()- 设置线程调度策略和优先级/nice 值sched_client_add_tag()- 为线程添加标记sched_client_remove_tag()- 删除指定标记sched_client_clear_tags()- 清空所有标记sched_client_get_tags()- 获取所有标记(逗号分隔)sched_client_gettid()- 获取当前线程 TID
项目采用客户端-服务器架构,包含三个主要组件:
┌─────────────┐ ┌──────────────────┐ ┌──────────────┐
│ Client │─────▶│ Schedule Server │◀─────│ Hook Library │
│ Application │ │ (Unix Socket) │ │ (LD_PRELOAD) │
└─────────────┘ └──────────────────┘ └──────────────┘
-
Hook 库 (
hook/)- 通过 LD_PRELOAD 拦截
pthread_create调用 - 自动向调度服务器发送线程创建事件
- 通过 LD_PRELOAD 拦截
-
调度服务器 (
schedule_server/)- 中央调度管理服务
- 处理调度策略设置和标记管理
- 实现标记继承机制
-
客户端 API (
api/)- 提供统一的跨语言接口
- 支持 C/C++、Python、Rust
- GCC 编译器
- Make 构建工具
- Git(用于管理 submodule)
- Python 3.x(可选,用于 Python 绑定)
- Rust 工具链(可选,用于 Rust 绑定)
# 克隆项目(包含 submodule)
git clone --recurse-submodules <repository-url>
cd ldpreload
# 如果已经克隆了项目,需要初始化 submodule
git submodule update --init --recursive
# 编译所有组件
make all
# 或者分别编译
make libsched # 编译客户端库
make dymso # 编译 hook 库
make sserver # 编译调度服务器编译完成后,所有产物位于 build/ 目录:
libsched_client.so- 客户端共享库libsched_client.a- 客户端静态库tsched_hook.so- Hook 库sserver- 调度服务器可执行文件
# 在终端中启动服务器
./build/sserver
# 或者使用 make 命令
make run_sserver注意:调度服务器需要 CAP_SYS_NICE 权限来设置线程调度策略。如果权限不足,可以使用:
sudo setcap cap_sys_nice+ep ./build/sserver# 使用 LD_PRELOAD 运行应用程序
LD_PRELOAD=./build/tsched_hook.so your_application
# 示例:运行 Python 测试
make run_ros#include "sched_client.h"
#include <pthread.h>
void* worker_thread(void* arg) {
// 获取当前线程 TID
pid_t tid = sched_client_gettid();
// 添加标记
sched_client_add_tag(0, "worker_thread");
sched_client_add_tag(0, "high_priority");
// 设置调度策略为 SCHED_FIFO,优先级 50
sched_client_set_policy(0, SCHED_FIFO, 50, 0);
// 获取所有标记
char tags[256];
sched_client_get_tags(0, tags, sizeof(tags));
printf("Tags: %s\n", tags); // 输出: "worker_thread,high_priority"
return NULL;
}编译:
gcc -o program program.c -I./api/inc -L./build -lsched_client -lpthread
LD_LIBRARY_PATH=./build ./programfrom sched_client import SchedClient, SCHED_FIFO
client = SchedClient()
# 添加标记
client.add_tag(0, "python_thread")
# 设置调度策略
client.set_policy(0, SCHED_FIFO, 50, 0)
# 获取标记
tags = client.get_tags()
print(f"Tags: {tags}")运行:
export LD_LIBRARY_PATH=./build:$LD_LIBRARY_PATH
python3 your_script.pyuse sched_client::{SchedClient, SCHED_FIFO};
// 添加标记
SchedClient::add_tag(0, "rust_thread").unwrap();
// 设置调度策略
SchedClient::set_policy(0, SCHED_FIFO, 50, 0).unwrap();
// 获取标记
let tags = SchedClient::get_tags(0, 1024).unwrap();
println!("Tags: {}", tags);int sched_client_set_policy(pid_t tid, int policy, int priority, int nice);- 参数:
tid: 线程 ID(0 表示当前线程)policy: 调度策略(SCHED_OTHER、SCHED_FIFO、SCHED_RR、SCHED_DEADLINE)priority: 优先级(用于 FIFO/RR/DEADLINE,范围通常 1-99)nice: nice 值(用于 SCHED_OTHER,范围 -20 到 19)
- 返回值:0 成功,-1 失败
// 添加标记(只允许字母和下划线)
int sched_client_add_tag(pid_t tid, const char *tag);
// 删除标记
int sched_client_remove_tag(pid_t tid, const char *tag);
// 清空所有标记
int sched_client_clear_tags(pid_t tid);
// 获取所有标记(逗号分隔)
int sched_client_get_tags(pid_t tid, char *buffer, size_t buffer_size);// 获取当前线程 TID
pid_t sched_client_gettid(void);项目提供了完整的示例程序,位于 demo/ 目录:
- C 示例 (
demo/c/) - 使用 pthread - C++ 示例 (
demo/cpp/) - 使用 std::thread - Python 示例 (
demo/python/) - 使用 threading - Rust 示例 (
demo/rust/) - 使用 std::thread
运行示例:
# 编译所有 demo
make demo-all
# 运行 C demo
LD_LIBRARY_PATH=./build ./build/demo_c
# 运行 Python demo
cd demo/python
export LD_LIBRARY_PATH=../../build:$LD_LIBRARY_PATH
python3 demo.py详细说明请参考 demo/README.md。
ldpreload/
├── api/ # 客户端 API
│ ├── inc/ # C 头文件
│ ├── src/ # C 实现
│ ├── python/ # Python 绑定
│ └── rust/ # Rust 绑定
├── hook/ # LD_PRELOAD hook 库
├── schedule_server/ # 调度服务器
├── share/ # 共享资源
│ ├── inc/ # 公共头文件
│ ├── src/ # 公共源文件
│ └── third_party/ # 第三方库(cJSON)
├── demo/ # 示例程序
│ ├── c/ # C 示例
│ ├── cpp/ # C++ 示例
│ ├── python/ # Python 示例
│ └── rust/ # Rust 示例
├── test/ # 测试代码
├── build/ # 构建输出目录
├── Makefile # 主 Makefile
└── README.md # 本文档
make all # 编译所有组件(默认)
make libsched # 编译客户端库
make dymso # 编译 hook 库
make sserver # 编译调度服务器
make demo-c # 编译 C demo
make demo-cpp # 编译 C++ demo
make demo-rust # 编译 Rust demo
make demo-all # 编译所有 demo
make clean # 清理所有构建产物
make help # 显示帮助信息- 应用程序通过
LD_PRELOAD加载tsched_hook.so - Hook 库拦截
pthread_create调用 - 创建新线程时,hook 库向调度服务器发送
parent_relation消息 - 调度服务器自动将父线程的标记继承给子线程
父线程 (TID: 1000)
├─ 标记: ["worker", "high_priority"]
└─ 创建子线程 (TID: 1001)
└─ 自动继承: ["worker", "high_priority"]
客户端通过 Unix Domain Socket 向调度服务器发送请求,服务器执行实际的调度策略设置操作。
- 权限要求:设置实时调度策略(SCHED_FIFO/RR)需要 root 权限或
CAP_SYS_NICE能力 - 标记格式:标记只能包含大小写英文字母和下划线(a-z, A-Z, _)
- 服务器运行:使用 API 前必须确保调度服务器正在运行
- 线程安全:API 函数是线程安全的,可以在多线程环境中使用
- 性能影响:Hook 库会略微增加线程创建的开销,但影响很小
# 确保库文件在 LD_LIBRARY_PATH 中
export LD_LIBRARY_PATH=./build:$LD_LIBRARY_PATH- 检查是否有足够的权限(需要 root 或 CAP_SYS_NICE)
- 确认调度服务器正在运行
- 检查线程 ID 是否正确
- 确保标记只包含字母和下划线
- 不能包含数字、空格或特殊字符