1. Binder驱动初探

任务目标

编写一个C程序,读取/proc/binder/proc文件,解析其中记录的Binder进程信息,并输出每个使用Binder的进程PID、进程名(若可获取)以及该进程的Binder线程数量。该文件由内核Binder驱动在启用CONFIG_ANDROID_BINDER_IPC且挂载debugfs时生成,是观察Binder驱动运行时状态的最直接入口。

前置条件

  • Linux内核已启用Binder驱动(Android环境或加载了binder_linux模块的自编译内核)
  • debugfs已挂载到/sys/kernel/debug,且当前用户有读取权限(通常需要root)
  • 系统中有至少一个进程正在使用Binder(如servicemanager、surfaceflinger等),否则文件内容为空

实现思路

/proc/binder/proc的文本格式为:每个使用Binder的进程以proc开头,后跟PID;随后若干行以thread开头,后跟线程ID,表示该进程创建的Binder线程。解析时逐行扫描,遇到proc行记录新进程,遇到thread行累加计数。进程名通过读取/proc/[pid]/comm获得。

完整代码

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

#define MAX_LINE 256
#define PROC_PATH "/proc/binder/proc"

/* 从 /proc/[pid]/comm 读取进程名 */
static void get_process_name(int pid, char *buf, size_t size) {
    char path[64];
    FILE *fp;
    snprintf(path, sizeof(path), "/proc/%d/comm", pid);
    fp = fopen(path, "r");
    if (fp) {
        if (fgets(buf, size, fp)) {
            buf[strcspn(buf, "\n")] = '\0';  /* 去掉换行 */
        }
        fclose(fp);
    } else {
        snprintf(buf, size, "<unknown>");
    }
}

int main(void) {
    FILE *fp = fopen(PROC_PATH, "r");
    if (!fp) {
        perror("fopen");
        fprintf(stderr, "无法打开 %s(需要root权限且内核已启用Binder驱动)\n", PROC_PATH);
        return 1;
    }

    char line[MAX_LINE];
    int current_pid = -1;
    int thread_count = 0;
    int first = 1;

    while (fgets(line, sizeof(line), fp)) {
        /* 去掉行尾空白 */
        line[strcspn(line, "\r\n")] = '\0';

        if (strncmp(line, "proc ", 5) == 0) {
            /* 先输出上一个进程的统计 */
            if (current_pid != -1) {
                char name[64];
                get_process_name(current_pid, name, sizeof(name));
                printf("PID %d (%s): %d Binder线程\n",
                       current_pid, name, thread_count);
            }
            /* 解析新进程PID */
            current_pid = atoi(line + 5);
            thread_count = 0;
            first = 0;
        } else if (strncmp(line, "thread ", 7) == 0 && current_pid != -1) {
            thread_count++;
        }
    }

    /* 输出最后一个进程 */
    if (current_pid != -1) {
        char name[64];
        get_process_name(current_pid, name, sizeof(name));
        printf("PID %d (%s): %d Binder线程\n",
               current_pid, name, thread_count);
    }

    if (first) {
        printf("当前没有进程使用Binder,或文件为空。\n");
    }

    fclose(fp);
    return 0;
}

编译与运行

gcc -o binder_proc binder_proc.c
sudo ./binder_proc

验证方法

运行程序后,观察输出是否列出系统中使用Binder的进程及其线程数。可通过以下方式交叉验证:

  • 直接执行cat /proc/binder/proc,对比程序输出的PID和线程数量是否与原始文件一致
  • 使用ps -ef | grep servicemanager确认servicemanager进程存在,且其PID出现在程序输出中
  • 若程序输出为空,检查/proc/binder/proc是否存在且非空;若文件不存在,说明内核未启用Binder驱动或debugfs未挂载

预期输出示例

以下为基于文件内容的确定性解析结果(非实测数据),用于说明输出格式:

PID 1234 (servicemanager): 1 Binder线程
PID 5678 (surfaceflinger): 4 Binder线程

常见问题

  • 无法打开文件:确认以root运行,且内核已加载Binder驱动(ls /dev/binder应存在)
  • 输出为空:当前系统没有进程创建Binder上下文,或文件权限不足导致读到空内容
  • 进程名显示unknown:进程可能在读取瞬间已退出,属正常现象