3. Binder线程管理

任务说明

本案例实现Binder驱动中用于处理进程间调用的事务线程池管理模块,核心目标包括:维护线程池空闲列表与活跃线程计数、按需创建新线程、在线程完成任务后回收并允许退出。该模块是Binder IPC中binder_thread生命周期管理的基础设施,聚焦于线程的创建与退出策略,不涉及具体事务路由逻辑。

核心数据结构


/* 线程状态 */
#define BINDER_THREAD_IDLE      0
#define BINDER_THREAD_ACTIVE    1
#define BINDER_THREAD_EXITING   2

struct binder_thread_node {
    int tid;
    int state;
    struct binder_thread_node *next;
};

struct binder_thread_pool {
    struct binder_thread_node *idle_list;    /* 空闲线程链表 */
    int total_threads;                       /* 当前线程总数 */
    int active_threads;                      /* 活跃线程数 */
    int max_threads;                         /* 线程上限 */
    int min_idle_threads;                    /* 保持的最小空闲数 */
};

关键完整代码


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BINDER_THREAD_IDLE      0
#define BINDER_THREAD_ACTIVE    1
#define BINDER_THREAD_EXITING   2

struct binder_thread_node {
    int tid;
    int state;
    struct binder_thread_node *next;
};

struct binder_thread_pool {
    struct binder_thread_node *idle_list;
    int total_threads;
    int active_threads;
    int max_threads;
    int min_idle_threads;
};

/* 初始化线程池 */
void binder_pool_init(struct binder_thread_pool *pool, int max, int min_idle)
{
    pool->idle_list = NULL;
    pool->total_threads = 0;
    pool->active_threads = 0;
    pool->max_threads = max;
    pool->min_idle_threads = min_idle;
}

/* 创建新线程节点 */
static struct binder_thread_node *binder_thread_create(struct binder_thread_pool *pool, int tid)
{
    struct binder_thread_node *node;

    if (pool->total_threads >= pool->max_threads)
        return NULL;

    node = (struct binder_thread_node *)malloc(sizeof(*node));
    if (!node)
        return NULL;

    node->tid = tid;
    node->state = BINDER_THREAD_IDLE;
    node->next = NULL;
    pool->total_threads++;
    return node;
}

/* 将线程加入空闲列表 */
static void binder_thread_add_idle(struct binder_thread_pool *pool,
                                   struct binder_thread_node *node)
{
    node->state = BINDER_THREAD_IDLE;
    node->next = pool->idle_list;
    pool->idle_list = node;
}

/* 从空闲列表摘除一个线程 */
static struct binder_thread_node *binder_thread_remove_idle(struct binder_thread_pool *pool)
{
    struct binder_thread_node *node = pool->idle_list;

    if (!node)
        return NULL;

    pool->idle_list = node->next;
    node->next = NULL;
    return node;
}

/* 获取一个可用线程:优先复用空闲线程,否则创建新线程 */
struct binder_thread_node *binder_thread_get(struct binder_thread_pool *pool)
{
    struct binder_thread_node *node;

    node = binder_thread_remove_idle(pool);
    if (!node) {
        /* 尝试创建新线程,tid用当前总数+1模拟内核分配 */
        node = binder_thread_create(pool, pool->total_threads + 1);
        if (!node)
            return NULL;
    }

    node->state = BINDER_THREAD_ACTIVE;
    pool->active_threads++;
    return node;
}

/* 线程完成工作后归还:若空闲线程过多则标记退出并释放 */
void binder_thread_put(struct binder_thread_pool *pool, struct binder_thread_node *node)
{
    int idle_count = 0;
    struct binder_thread_node *cur;

    /* 统计当前空闲数 */
    for (cur = pool->idle_list; cur; cur = cur->next)
        idle_count++;

    if (idle_count >= pool->min_idle_threads) {
        /* 空闲线程已足够,允许该线程退出 */
        node->state = BINDER_THREAD_EXITING;
        pool->total_threads--;
        free(node);
    } else {
        /* 需要保留该线程作为空闲线程 */
        binder_thread_add_idle(pool, node);
    }
    pool->active_threads--;
}

/* 打印线程池状态 */
void binder_pool_dump(struct binder_thread_pool *pool)
{
    struct binder_thread_node *cur;

    printf("total=%d active=%d idle_list: ", pool->total_threads, pool->active_threads);
    for (cur = pool->idle_list; cur; cur = cur->next)
        printf("[tid:%d] ", cur->tid);
    printf("\n");
}

/* 模拟工作负载:获取线程、执行、归还 */
void binder_pool_simulate_work(struct binder_thread_pool *pool, int work_items)
{
    int i;
    struct binder_thread_node *workers[10];
    int worker_cnt = 0;

    for (i = 0; i < work_items && i < 10; i++) {
        workers[worker_cnt] = binder_thread_get(pool);
        if (!workers[worker_cnt]) {
            printf("work item %d: failed to get thread (pool full)\n", i);
            break;
        }
        printf("work item %d: got thread tid=%d\n", i, workers[worker_cnt]->tid);
        worker_cnt++;
    }

    /* 模拟执行完毕,归还线程 */
    for (i = 0; i < worker_cnt; i++)
        binder_thread_put(pool, workers[i]);
}

/* 验证入口 */
int main(void)
{
    struct binder_thread_pool pool;
    struct binder_thread_node *t1, *t2;

    binder_pool_init(&pool, 4, 1);  /* 最大4线程,保持至少1个空闲 */

    printf("=== 初始状态 ===\n");
    binder_pool_dump(&pool);

    printf("\n=== 获取2个线程 ===\n");
    t1 = binder_thread_get(&pool);
    t2 = binder_thread_get(&pool);
    printf("t1 tid=%d, t2 tid=%d\n", t1->tid, t2->tid);
    binder_pool_dump(&pool);

    printf("\n=== 归还t1(空闲数0<1,应保留为空闲) ===\n");
    binder_thread_put(&pool, t1);
    binder_pool_dump(&pool);

    printf("\n=== 归还t2(空闲数1>=1,应退出释放) ===\n");
    binder_thread_put(&pool, t2);
    binder_pool_dump(&pool);

    printf("\n=== 模拟5个并发工作项 ===\n");
    binder_pool_simulate_work(&pool, 5);
    binder_pool_dump(&pool);

    printf("\n=== 模拟超过上限的工作项 ===\n");
    binder_pool_simulate_work(&pool, 6);
    binder_pool_dump(&pool);

    return 0;
}

编译与运行


gcc -o binder_thread_pool binder_thread_pool.c
./binder_thread_pool

验证方法与判定标准

程序输出应满足以下条件即判定通过:

  • 初始状态total=0 active=0,空闲列表为空。
  • 获取2个线程后total=2 active=2,两个线程tid分别为1和2。
  • 归还t1后,由于空闲数0小于min_idle_threads=1,t1进入空闲列表,total=2 active=1。
  • 归还t2后,空闲数1不小于1,t2被释放退出,total=1 active=0,空闲列表仅剩t1。
  • 模拟5个工作项时,前4个成功获取线程(tid 2,3,4,5),第5个因达到max_threads=4获取失败,输出failed to get thread。
  • 所有工作项归还后,空闲线程数维持在min_idle_threads=1,多余线程被回收,total最终为1。

线程状态流转

空闲 (IDLE) 在空闲列表中等待 活跃 (ACTIVE) 正在处理事务 退出 (EXITING) 释放并销毁 binder_thread_create() binder_thread_get() binder_thread_put() 空闲数 < min_idle binder_thread_put() 空闲数 ≥ min_idle free(node) 线程池策略:优先复用空闲线程;空闲不足且未达上限时创建新线程;空闲过剩时允许线程退出

关键机制说明

  • 线程获取:binder_thread_get()优先从空闲列表摘取线程,若空闲列表为空且未达max_threads上限则新建线程;两者均失败时返回NULL,调用方需自行处理(如阻塞等待或返回错误)。
  • 线程归还:binder_thread_put()根据当前空闲线程数决定线程去留——空闲数不足min_idle_threads时保留为空闲线程,否则标记退出并释放内存,实现动态伸缩。
  • 模拟验证:main()中的工作负载模拟展示了线程从创建、复用、保留到回收的完整生命周期,可直接观察线程池容量变化。