binder 驱动分析

一 binder线程池

hidl service启动时要设置binder的线程池:
configureRpcThreadpool(10, true);
具体到驱动的调用

    case BINDER_SET_MAX_THREADS: {
        int max_threads;

        if (copy_from_user(&max_threads, ubuf,
                   sizeof(max_threads))) {
            ret = -EINVAL;
            goto err;
        }
        binder_inner_proc_lock(proc);
        proc->max_threads = max_threads;
        binder_inner_proc_unlock(proc);
        break;
    }

proc->max_threads 设置的时候有的是1 有的是10不等,具体这个值要设置多少呢? 这个值有什么意义呢?
在驱动的binder_read读取完成后有如下:


done:

    *consumed = ptr - buffer;
    binder_inner_proc_lock(proc);
    if (proc->requested_threads == 0 &&
        list_empty(&thread->proc->waiting_threads) &&
        proc->requested_threads_started < proc->max_threads &&
        (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
         BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
         /*spawn a new thread if we leave this out */) {
        proc->requested_threads++;
        binder_inner_proc_unlock(proc);
        binder_debug(BINDER_DEBUG_THREADS,
                 "%d:%d BR_SPAWN_LOOPER\n",
                 proc->pid, thread->pid);
        if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
            return -EFAULT;
        binder_stat_br(proc, thread, BR_SPAWN_LOOPER);
    } else
        binder_inner_proc_unlock(proc);
    return 0;

我们看到条件是
if (proc->requested_threads == 0 &&
list_empty(&thread->proc->waiting_threads) &&
proc->requested_threads_started < proc->max_threads &&
(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED))
proc->requested_threads 要么是0要么是1,默认是0。&thread->proc->waiting_threads也就是当前waiting_threads为空,没有在等待状态,都在忙碌,proc->requested_threads_started小于我们设定的值。从判断条件看,如果线程都在忙的话,说明线程忙不过来,那么我们就要起一个其他线程来帮我们处理了。也就是下面的BR_SPAWN_LOOPER 命令给用户层,调用下面的函数,起线程代码如下:

void ProcessState::spawnPooledThread(bool isMain)
{
    if (mThreadPoolStarted) {
        String8 name = makeBinderThreadName();
        ALOGV("Spawning new pooled thread, name=%s\n", name.string());
        sp<Thread> t = new PoolThread(isMain);
        t->run(name.string());
    }
}

我们在stats看到的

proc 2941
context hwbinder
  threads: 4
  requested threads: 0+3/9
  ready threads 4
  free async space 520192
  nodes: 1
  refs: 1 s 1 w 1
  buffers: 0
  pending transactions: 0
  BC_FREE_BUFFER: 682
  BC_INCREFS: 1
  BC_ACQUIRE: 1
  BC_INCREFS_DONE: 1
  BC_ACQUIRE_DONE: 1
  BC_REGISTER_LOOPER: 3
  BC_ENTER_LOOPER: 1
  BC_TRANSACTION_SG: 1
  BC_REPLY_SG: 25
  BR_TRANSACTION: 681
  BR_REPLY: 1
  BR_TRANSACTION_COMPLETE: 26
  BR_INCREFS: 1
  BR_ACQUIRE: 1
  BR_SPAWN_LOOPER: 3

requested threads: 0+3/9 说明我们当前启动了3个线程用来处理,最大可以启动9个线程

二 todo列表

在binder_thread和binder_proc中都有todo列表,这两个todo列表有什么区别和联系呢?

static bool binder_proc_transaction(struct binder_transaction *t,
                    struct binder_proc *proc,
                    struct binder_thread *thread)
    if (!thread && !target_list)
        thread = binder_select_thread_ilocked(proc);

    if (thread) {
        target_list = &thread->todo;
        binder_transaction_priority(thread->task, t, node_prio,
                        node->inherit_rt);
    } else if (!target_list) {
        target_list = &proc->todo;

如果有空闲thread就挂到空闲thread的todo中,如果没有的话就挂在process的todo中。

三 binder transact 驱动流程

binder_transaction.png

首先client BC_TRANSACTION给驱动的binder_thread_write

binder_thread_write将client的todo添加BINDER_WORK_TRANSACTION_COMPLETE
将server的todo添加BINDER_WORK_TRANSACTION.client阻塞在binder_thread_read

server被唤醒,通过binder_thread_read 读取BINDER_WORK_TRANSACTION,返回BR_TRANSACTION 给server

server 处理完成后,发送sendReply BC_REPLY 给drvier驱动,也是先写后读

binder_thread_write将server的todo添加BINDER_WORK_TRANSACTION_COMPLETE,然后将client的todo添加BINDER_WORK_TRANSACTION唤醒client,然后进入binder_thread_read

client被唤醒,执行BINDER_WORK_TRANSACTION_COMPLETE,返回BR_TRANSACTION_COMPLETE。执行BINDER_WORK_TRANSACTION,返回BR_REPLY

四 binder数据传输

binder_cmd.png

binder_transaction中cmd后面跟的是binder_transaction_data,他的结构体如下

struct binder_transaction_data {
    /* The first two are only used for bcTRANSACTION and brTRANSACTION,
     * identifying the target and contents of the transaction.
     */
    union {
        /* target descriptor of command transaction */
        __u32   handle;
        /* target descriptor of return transaction */
        binder_uintptr_t ptr;
    } target;
    binder_uintptr_t    cookie; /* target object cookie */
    __u32       code;       /* transaction command */

    /* General information about the transaction. */
    __u32           flags;
    pid_t       sender_pid;
    uid_t       sender_euid;
    binder_size_t   data_size;  /* number of bytes of data */
    binder_size_t   offsets_size;   /* number of bytes of offsets */

    /* If this transaction is inline, the data immediately
     * follows here; otherwise, it ends with a pointer to
     * the data buffer.
     */
    union {
        struct {
            /* transaction data */
            binder_uintptr_t    buffer;
            /* offsets from buffer to flat_binder_object structs */
            binder_uintptr_t    offsets;
        } ptr;
        __u8    buf[8];
    } data;
};

在传输过程中有两次copy,一次是将binder_transaction_data后面的buffercopy到binder_buffer下,还有


binder_transaction_data.png

offsets保存的是

struct flat_binder_object {
    struct binder_object_header hdr;
    __u32               flags;

    /* 8 bytes of data. */
    union {
        binder_uintptr_t    binder; /* local object */
        __u32           handle; /* remote object */
    };

    /* extra data associated with local object */
    binder_uintptr_t    cookie;
};
struct binder_object_header {
    __u32        type;
};

type的种类有如下:

enum {
    BINDER_TYPE_BINDER  = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),
    BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),
    BINDER_TYPE_HANDLE  = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
    BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
    BINDER_TYPE_FD      = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
    BINDER_TYPE_FDA     = B_PACK_CHARS('f', 'd', 'a', B_TYPE_LARGE),
    BINDER_TYPE_PTR     = B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE),
};

我们在writeStrongBinder中写入的就是flat_binder_object
在binder 被对方read的时候,直接指针赋值,没有copy,只是将binder_transaction_data copy到用户空间。

tr.data.ptr.buffer = (binder_uintptr_t)
            ((uintptr_t)t->buffer->data +
            binder_alloc_get_user_buffer_offset(&proc->alloc));

tr.data.ptr.offsets = tr.data.ptr.buffer +
            ALIGN(t->buffer->data_size,
                sizeof(void *));
ptr += sizeof(uint32_t);
if (copy_to_user(ptr, &tr, sizeof(tr))) {
    if (t_from)
        binder_thread_dec_tmpref(t_from);

    binder_cleanup_transaction(t, "copy_to_user failed",
                   BR_FAILED_REPLY);

    return -EFAULT;
}

也就是buffer在传输过程中,只进行了一次copy操作。
在buffer处理完成后执行 IPCThreadState::freeBuffer释放掉

void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data,
                                size_t /*dataSize*/,
                                const binder_size_t* /*objects*/,
                                size_t /*objectsSize*/, void* /*cookie*/)
{
    //ALOGI("Freeing parcel %p", &parcel);
    IF_LOG_COMMANDS() {
        alog << "Writing BC_FREE_BUFFER for " << data << endl;
    }
    ALOG_ASSERT(data != NULL, "Called with NULL data");
    if (parcel != NULL) parcel->closeFileDescriptors();
    IPCThreadState* state = self();
    state->mOut.writeInt32(BC_FREE_BUFFER);
    state->mOut.writePointer((uintptr_t)data);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容