Binder驱动之设备控制----binder_ioctl -- 一

概述

ioctl是Linux中常见的系统调用,它用于对底层设备的一些特性进行控制的用户态接口,应用程序在调用ioctl进行设备控制时,最后会调用到设备注册struct file_operations结构体对象时的unlocked_ioctl或者compat_ioctl两个钩子上,具体是调用哪个钩子判断标准如下:

  • compat_ioctl : 32位的应用运行在64位的内核上,这个钩子被调用。
  • unlocked_ioctl: 64位的应用运行在64位的内核或者32位的应用运行在32位的内核上,则调用这个钩子。
    Binder做为Android中进程间高效通信的核心组件,其底层是以misc设备驱动的形式实现的,但它本身并没有实现read,write操作,所有的控制都是通过ioctl操作来实现。在Binder驱动的struct file_operations定义中可见,它的compat_ioctlunlocked_ioctl两个钩子的的实现都是对应到binder_ioctl上的。
static const struct file_operations binder_fops = {
  .owner = THIS_MODULE,
  .poll = binder_poll,
  .unlocked_ioctl = binder_ioctl,
  .compat_ioctl = binder_ioctl,
  .mmap = binder_mmap,
  .open = binder_open,
  .flush = binder_flush,
  .release = binder_release,
};

接下来,我们来先看看binder_ioctl的实现有关的结构体。

相关数据结构

2.1 struct binder_thread

struct binder_thread {
  struct binder_proc *proc;         /*该thread相关联的binder_proc*/
  struct rb_node rb_node;         /*用于链入proc的threads红黑树*/
  int pid;
  int looper;           /* 状态标识位,用于表示当前线程所处的状态,具体包括以下几种状态:
                             * enum {
                             *      BINDER_LOOPER_STATE_REGISTERED  = 0x01, /*进程的非主线程进入Binder循环状态*/
                             *      BINDER_LOOPER_STATE_ENTERED     = 0x02, /*进程的主线程进入Binder循环状态*/
                             *      BINDER_LOOPER_STATE_EXITED      = 0x04, /*线程退出Binder循环状态*/
                             *      BINDER_LOOPER_STATE_INVALID     = 0x08, /*线程处在一个无效的状态,表示出错*/
                             *      BINDER_LOOPER_STATE_WAITING     = 0x10, /*线程的todo队列为空,进入等待请求的状态*/
                             *      BINDER_LOOPER_STATE_NEED_RETURN = 0x20  /*线程是否需要返回数据给进程的用户态*/
                             *  };
                             */
  struct binder_transaction *transaction_stack;     /*该线程的事务栈。通过struct binder_transaction的
                                                    * from_parent和to_parent分别链入客户端和服务端线程的
                                                    * transaction_stack事务栈中(即本字段)。详见2.7 */
  struct list_head todo;                            /*binder_work队列,管理本线程所有待处理的binder_work*/
  uint32_t return_error;                         /* Write failed, return error code in read buf */
  uint32_t return_error2; /* Write failed, return error code in read 
                           * buffer. Used when **sending a reply to a dead process** that */
                           * we are also waiting on 。发送reply时发生错误,该错误码用于返回给发送进程 */
  wait_queue_head_t wait;  /*binder线程空闲时,用于等待队列相关的结构,是Linux内核的一个数据结构*/
  struct binder_stats stats; /*统计有关的结构*/
};

2.2 struct binder_proc中的相关成员

struct binder_proc{
  ...
  struct rb_root threads; /*管理thread的红黑树根节点,以线程的id为序*/
  struct rb_root nodes;            /*管理binder_node的红黑树根节点,以binder_node中的ptr大小为序*/
  struct rb_root refs_by_desc;  /*管理binder_ref的红黑树根节点,以binder_ref中的desc大小为序*/
  struct rb_root refs_by_node;  /*管理binder_ref的红黑树根节点,以binder_ref对应的binder_node的地址为序*/
  ...
  int max_threads; /*最大线程数*/
  ...
};

2.3 用于用户态向内核态传输数据的结构体 —— struct binder_write_read

struct binder_write_read {
  /* process ----> kernel */
  binder_size_t        write_size;    /* bytes to write, 进程用户态地址空间传递到内核数据的大小*/
  binder_size_t        write_consumed;    /* bytes consumed by driver 进程用户态地址空间传递到内核数据中已经被内核态处理的大小*/
  binder_uintptr_t    write_buffer;       /*进程用户态地址空间传递到内核数据的起始地址*/
/*  kernel ----> process */
  binder_size_t        read_size;    /* bytes to read, 总共可供给驱动写入的字节数,read_buffer可供内核使用的大小*/
  binder_size_t        read_consumed;    /* bytes consumed by driver, 内核Binder驱动发送给用户态进程的字节数*/
  binder_uintptr_t    read_buffer;  /*内核驱动发送给进程数据buffer的起始地址*/
};

2.4 Binder C/S通信架构中,C端的驱动层表示 —— struct binder_ref

struct binder_ref {
  /* Lookups needed: */
  /*   node + proc => ref (transaction) */
  /*   desc + proc => ref (transaction, inc/dec ref) */
  /*   node => refs + procs (proc exit) */
  int debug_id;                /*每个binder_ref的唯一标识符,主要用于debug*/
  struct rb_node rb_node_desc; /*用来链入proc->refs_by_desc红黑树中,该红黑树以desc域为序组织的*/
  struct rb_node rb_node_node; /*用来链入proc->refs_by_node红黑树中, 该红黑树以该binder_ref所对应的binder_node的地址为序组织的*/
  struct hlist_node node_entry; /*用来链入binder_node的refs哈希链表中。*/
  struct binder_proc *proc;     /*指向该binder_ref中所属的binder_proc*/
  struct binder_node *node;     /*指向该binder_ref所对应(引用)的binder_node*/
  uint32_t desc;                /*binder_ref的描述符。用来返回给进程用户态地址空间,标识所对应的binder_ref*/
  int strong;                   /*强引用计数*/
  int weak;                     /*弱引用计数*/
  struct binder_ref_death *death; /*Binder“死亡讣告”相关的一个结构体,详见:2.8*/ 
};

2.5 Binder C/S通信架构中,S端的驱动层表示 —— struct binder_node

struct binder_node {
  int debug_id;
  struct binder_work work;
  union {
    struct rb_node rb_node;   /*用来链入proc的nodes红黑树,该红黑树以binder_node的ptr的大小为序*/
    struct hlist_node dead_node;
  };
  struct binder_proc *proc;
  struct hlist_head refs; /*所有引用这个binder_node的binder_ref通过它的node_entry加入这个哈希链表中,
                        * 这样binder_node通过查看这个哈希链表就知道有哪些binder_ref在引用它*/
  int internal_strong_refs;  /*binder_ref的强引用计数,即有多少个binder_ref强引用这个binder_node*/
  int local_weak_refs;       /*BBinder弱引用计数*/
  int local_strong_refs;    /*binder_buffer.target_node及BBinder的强引用计数*/
  binder_uintptr_t ptr;      /*对应BBinder基类RefBase中mRef成员的的地址,它是一个引用计数器,类型为weakref_impl*/
  binder_uintptr_t cookie;   /*对应BBinder的地址*/**
  unsigned has_strong_ref:1;  /*标识是否已经增加了用户态对应binder service(BBinder)对象的强引用计数*/
  unsigned pending_strong_ref:1;  /*标识是否有未处理的BR_ACQUIRE命令,在执行BR_ACQUIRE请求命令前设为1,在BC_ACQUIRE_DONE中设为0*/
  unsigned has_weak_ref:1;   /*标识是否已经增加了用户态对应binder service(BBinder)对象的弱引用计数*/
  unsigned pending_weak_ref:1; /*标识是否有未处理的BR_INCREFS命令,在执行BR_INCREFS请求命令前设为1,在BC_INCREFS_DONE中设为0*/
  unsigned has_async_transaction:1; /*标识是否有异步事务要处理。异步事务的含义是:客户端发送了带有TF_ONE_WAY标识的请求。*/
  unsigned accept_fds:1;          /*是否接受文件描述符*/
  unsigned min_priority:8;
  struct list_head async_todo;  /*异步事务待处理链表*/
};

2.6 用于Binder用户态向内核驱动传输事务数据 —— struct 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;                      /*标志位,如:TF_ONE_WAY*/
  pid_t        sender_pid;                /*发送者进程id*/
  uid_t        sender_euid;             /*发送者有效用户id*/
  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 {     /*存放事务的数据部分,如果是inline,则数据直接放在buf数组中;
               * 如果不是,则放在ptr结构体中的buffer和offsets的指针中。一般情况都是通过ptr结构体*/
    struct {
        /* transaction data */
        binder_uintptr_t    buffer;
        /* offsets from buffer to flat_binder_object structs */
        binder_uintptr_t    offsets;
      } ptr;
    __u8    buf[8]; /*inline数据直接放在这个buf中, 在4.0.9的内核中,这个字段没有看见使用的地方*/
  } data;
};

2.7 用于Binder内核态驱动表示Binder通信事务数据结构 —— struct binder_transaction

struct binder_transaction {
  int debug_id;
  struct binder_work work;                   /*用于链入线程/进程todo队列的成员*/
  struct binder_thread *from;               /*事务发起线程thread的地址,如果是binder server回复给client,该域为NULL*/
  struct binder_transaction ***from_parent**;  /* 用于链入事务发起线程的事务栈中,
                                                * 加入的时机是binder_transaction_data传入驱动并被binder_transaction处理的时候*/
  struct binder_proc *to_proc;        /*目标线程的proc地址*/
  struct binder_thread *to_thread;    /*事务目标线程thread的地址*/
  struct binder_transaction *to_parent; /* 用于链入目标线程的事务栈中,
                                         * 加入的时机是目标线程在调用binder_thread_read处理thread->todo队列
                                         * 类型为BINDER_WORK_TRANACTION的binder_work时*/
  unsigned need_reply:1;
  /* unsigned is_dead:1; */    /* not used at the moment */
  struct binder_buffer *buffer;    /*存储数据的地方*/
  unsigned int    code;                /*一个binder调用所对应的代号*/
  unsigned int    flags;
  long    priority;                /*请求/回复 线程的优先级*/
  long    saved_priority;     /*存储线程优先级备份,当需要修改一个线程的优先级时,先将它当前值放在该变量中,以便于稍后恢复。*/
  kuid_t    sender_euid;
};

进程用户态传输进来的struct binder_transaction_data到内核态后,会转化成相应的struct binder_transaction。该数据结构主要用于承载Binder请求和回复通信中的数据。

2.8 用于注册Binder service死亡通知的数据结构 —— binder_ref_death

struct binder_ref_death {
  struct binder_work work;  /*binder service死亡时,通过该work的entry域链入thread或者proc的todo队列*/
  binder_uintptr_t cookie; /*binder service死亡时,要通知的BpBinder对象的地址*/
};

3. 设备驱动控制 --- binder_ioctl

Binder驱动没有提供read/write操作,所有数据传输、控制都是通过binder_ioctl进行,因此该部分是Binder驱动的核心内容,承载了Binder数据传输部分的主要业务。

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int ret;
    struct binder_proc *proc = filp->private_data;
    struct binder_thread *thread;
    /*读取命令的大小*/
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;
    /*pr_info("binder_ioctl: %d:%d %x %lx\n",
            proc->pid, current->pid, cmd, arg);*/
    trace_binder_ioctl(cmd, arg);
    /* 如果binder_stop_on_user_error < 2 则直接返回0;
    * 否则,调用_wait_event_interruptible进入可中断的挂起状态,接着让出处理器,
    * 直到被wake_up且条件(binder_stop_on_user_error < 2)为真时才返回
    */
    ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
    if (ret)
        goto err_unlocked;
    /*获取binder_main_lock锁*/
    binder_lock(__func__);
    /*在proc->threads红黑树中查找thread,该红黑树以pid为序,具体详见3.1*/
    thread = **binder_get_thread**(proc);
    if (thread == NULL) {
        ret = -ENOMEM;
        goto err;
    }
    /*根据不同的命令,调用不同的处理函数进行处理*/
    switch (cmd) {
    case BINDER_WRITE_READ:
        /*读写命令,数据传输,binder IPC通信的核心逻辑,详见3.2*/
        ret = **binder_ioctl_write_read**(filp, cmd, arg, thread);
        if (ret)
            goto err;
        break;
    case BINDER_SET_MAX_THREADS:
        /*设置最大线程数,直接将值设置到proc结构的max_threads域中。*/
        if (copy_from_user(&**proc->max_threads**, ubuf, sizeof(proc->max_threads))) {
            ret = -EINVAL;
            goto err;
        }
        break;
    case BINDER_SET_CONTEXT_MGR:
        /*设置Context manager,即将自己设置为ServiceManager,详见3.3*/
        ret = binder_ioctl_set_ctx_mgr(filp);
        if (ret)
            goto err;
        break;
    case BINDER_THREAD_EXIT:
        /*binder线程退出命令,释放相关资源,详见3.4*/
        binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n",
                proc->pid, thread->pid);
        binder_free_thread(proc, thread);
        thread = NULL;
        break;
    case BINDER_VERSION: {
        /*获取binder驱动版本号,在kernel4.4版本中,32位该值为7,64位版本该值为8*/
        struct binder_version __user *ver = ubuf;
        if (size != sizeof(struct binder_version)) {
            ret = -EINVAL;
            goto err;
        }
        /*将版本号信息写入用户态地址空间struct binder_version的protocol_version中*/
        if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
                &ver->protocol_version)) {
            ret = -EINVAL;
            goto err;
        }
        break;
    }
    default:
        ret = -EINVAL;
        goto err;
    }
    ret = 0;
    err:
        if (thread)
            thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
        binder_unlock(__func__); /*释放binder_main_lock锁*/
        wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
        if (ret && ret != -ERESTARTSYS)
            pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
    err_unlocked:
        trace_binder_ioctl_done(ret);
        return ret;
}
  • _IOC_SIZE读取参数命令的大小。在32位的体系架构中,参数cmd由四个域组成:
    • 读写属性域(direction: read/write), 区分是读取命令,还是写入命令。bit30~bit31,占2bit。可用宏_IOC_DIR读取。
    • 数据大小域(size : size of argument), 表示ioctlarg变量所指内存区域占用内存大小。bit16~bit29,占14bit。可用宏_IOC_SIZE读取 。
    • 魔数域 (type:usually related to the major number),又叫“幻数”区,用来区分不同的设备驱动,当传入的值与自身的值不一致时则不进行进一步处理,是用于防止误使用的一种状态标识位。一般用字母A~Z或者a~z表示。bit8~bit15,占8bit。可用宏_IOC_TYPE读取。
    • 序号数或者基数(command):用于区分各种命令。bit0~bit7,占8bit。可用宏_IOC_NR读取。
  • binder_stop_on_user_error, 该变量是一个全局静态变量, 它的值通过模块参数stop_on_user_error控制,当系统出现问题时,可以通过将该值设置为一个大于或等于2的值,来暂停binder,来进行debug。模块参数的设置可以在模块插入时以参数传递的形式设定,如 insmod xxx.ko arg=xxxx形式;如果权限设置允许的话,也可以通过sysfs来动态设置(如echo 3 > /sys/module/binder/parameters/stop_no_user_error)。相关代码如下:
static int binder_stop_on_user_error;
  /*定义模块参数`stop_on_user_error`的设置函数*/
static int binder_set_stop_on_user_error(const char *val,
                    struct kernel_param *kp)
{
    int ret;
    ret = param_set_int(val, kp);/*将sysfs中/sys/module/binder/parameters/stop_on_user_error读入binder_stop_on_user_error*/
    if (binder_stop_on_user_error < 2)
        wake_up(&binder_user_error_wait);
    return ret;
}
module_param_call(stop_on_user_error/*模块参数名字,所在路径为:/sys/module/binder/parameters/stop_on_user_error*/, 
    binder_set_stop_on_user_error /*模块参数`stop_on_user_error`的set函数*/,
    param_get_int/*模块参数`stop_on_user_error`的读取函数*/, 
    &binder_stop_on_user_error/*模块参数对应的变量地址*/,
    S_IWUSR | S_IRUGO /*在sysfs中的权限设置*/);
  • module_param_call该宏用于定义一个内核模块参数,它的定义为

     module_param_call(name, set, get, arg, perm),其中:

  1. name:内核模块参数的名字,也是在sysfs中显示的名字;
  2. set:是该内核模块参数设定的回调函数,当在插入模式时传递参数或者通过sysfs设定内核模块参数时,该函数会被调用;
  3. get: 是该内核模块参数读取的回调函数;
  4. arg:内核模块参数的地址;
  5. perm:该内核模块参数的权限设置,可以在sysfs中看到。

对于基本的数据类型的读取和设定回调函数,内核已经预先做了定义,一般形式为:param_get_xxxparam_set_xxxxxxint, short等。可以参考一下这篇博客,Linux内核模块的编写方法和技巧

  • 额外提一下sysfs,它是Linux2.6开始提供的一种虚拟文件系统,设计该文件系统的目的是把原本在procfs关于设备的部分独立出来,以“设备层次结构架构(device tree)”的形式呈现。它可以把设备和驱动程序的信息从内核输出到用户空间,也可以对设备和驱动程序做设置。具体详见sysfs简介

3.1 查找thread --- binder_get_thread

static struct binder_thread *binder_get_thread(struct binder_proc *proc)
{
    struct binder_thread *thread = NULL;
    struct rb_node *parent = NULL;
    struct rb_node **p = &proc->threads.rb_node; /*获取红黑树根节点*/
/*查找pid等于当前线程id的thread,该红黑树以pid大小为序组织*/
    while (*p) {
        parent = *p;
        thread = rb_entry(parent, struct binder_thread, rb_node);
    /*current->pid 是当前运行线程的id,不是进程的id*/
        if (current->pid < thread->pid)
            p = &(*p)->rb_left;
        else if (current->pid > thread->pid)
            p = &(*p)->rb_right;
        else
            break;
    }
    if (*p == NULL) {
        /*如果没有找到,则新创建一个*/
        thread = kzalloc(sizeof(*thread), GFP_KERNEL);
        if (thread == NULL)
            return NULL;
        /*更新thread创建统计计数*/
        binder_stats_created(BINDER_STAT_THREAD);
        /*初始化相关数据成员*/
        thread->proc = proc;
        thread->pid = current->pid; /*获取线程id*/
        init_waitqueue_head(&thread->wait);    /*初始化等待队列*/
        INIT_LIST_HEAD(&thread->todo);       /*初始化待处理队列*/
        rb_link_node(&thread->rb_node, parent, p);  /*加入到proc的threads红黑树中*/
        rb_insert_color(&thread->rb_node, &proc->threads);
        thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
        thread->return_error = BR_OK;
        thread->return_error2 = BR_OK;
    }
    return thread;
}

 

下一文:Binder驱动之设备控制----binder_ioctl -- 二

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值