Binder :ServiceManger服务(binder)启动

164 阅读6分钟

ServiceManager中的binder使用没有上层逻辑的影响,对分析binder比较直观

binder驱动注册和初始化


// 驱动函数映射
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,
};

// 注册驱动参数结构体
static struct miscdevice binder_miscdev = {
	.minor = MISC_DYNAMIC_MINOR,
    // 驱动名称
	.name = "binder",
	.fops = &binder_fops
};

static int binder_open(struct inode *nodp, struct file *filp){......}
static int binder_mmap(struct file *filp, struct vm_area_struct *vma){......}

static int __init binder_init(void)
{
    int ret;
    // 创建名为binder的单线程的工作队列
    binder_deferred_workqueue = create_singlethread_workqueue("binder");
    if (!binder_deferred_workqueue)
            return -ENOMEM;
    ......
    // 注册驱动,misc设备其实也就是特殊的字符设备
    ret = misc_register(&binder_miscdev);
    ......
    return ret;
}
// 驱动注册函数
device_initcall(binder_init);

1. binder驱动注册和初始化.png

主要流程:

  1. 通过驱动注册函数初始化binder
  2. 为binder创建单线程工作队列
  3. 注册驱动,misc设备其实也就是特殊的字符设备

驱动注册和初始化完成后,等待servicesmanager与他通信

ServiceManager启动

2. servermanager 6.0.png

主要完成三个任务:

  1. 打开驱动,创建和初始化servicemanager对应的Binder_proc进程对象,添加到全局链表中;并申请了128k字节大小的内存空间
  2. 通过iotcl-binder_iotcl将自己注册为Binder机制的管理者
  3. 启动循环,等待并处理Client端发来的请求

rc定义

所有的系统服务都是需要在ServiceManager中进行注册的,而ServiceManager作为一个起始的服务,是通过解析init.rc,/system/bin/servicemanager来启动的调用来启动的。

#/system/core/rootdir/init.rc
import /init.environ.rc
...
# Start essential services.
start servicemanager
#\frameworks\native\cmds\servicemanager\servicemanager.rc
service servicemanager /system/bin/servicemanager
    class core
    user system
    group system readproc
    onrestart restart healthd
   ...

init通过解析rc文件,fork出servicemanger进程,然后执行/system/bin/servicemanager,进入入口函数:

入口程序

Binder对应的目录是/dev/binder,注册驱动时将open release mmap等系统调用注册到Binder自己的函数,这样的话在用户空间就可以通过系统调用以访问文件的方式使用Binder。下面来粗略看一下相关代码。

#\frameworks\native\cmds\servicemanager\service_manager.c
struct binder_state
{
    int fd;
    void *mapped;
    size_t mapsize;
};

int main()
{
    struct binder_state *bs;
    #打开binder
    bs = binder_open(128*1024);
    ...
   //将自己注册为Binder机制的管理者
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }
    
    selinux_enabled = is_selinux_enabled();
    sehandle = selinux_android_service_context_handle();
    selinux_status_open(true);
   ....

    union selinux_callback cb;
    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
    cb.func_log = selinux_log_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);
    //启动循环,等待并处理client端发来的请求
    binder_loop(bs, svcmgr_handler);

    return 0;
}

binder_open

运行在servicemanager进程

  1. 调用系统调用打开binder设备驱动
  2. iotcl判断binder版本
  3. 内存共享将binder设备文件映射到进程的地址空间
//frameworks\native\cmds\servicemanager\binder.c
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;
    //申请对应的内存空间 
    bs = malloc(sizeof(*bs));
    //打开binder设备文件,这种属于设备驱动的操作方法
    bs->fd = open(driver, O_RDWR | O_CLOEXEC);
    //通过ioctl获取binder的版本号,会调用驱动的binder_ioctl方法
    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
        fprintf(stderr,
                "binder: kernel driver version (%d) differs from user space version (%d)\n",
                vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
        goto fail_open;
    }
    bs->mapsize = mapsize;
	//mmap进行内存映射,将Binder设备文件映射到进程的对应地址空间,地址空间大小为128k
	//映射之后,会将地址空间的起始地址和大小保存到结构体中,
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    return bs;
}

open(driver, O_RDWR | O_CLOEXEC)

最终会调用驱动binder.c中的binder_open方法。主要作用是是创建当前调用进程的binder_proc,赋值给打开/dev/binder获取的文件描述符的private_data

static int binder_open(struct inode *nodp, struct file *filp)
{																	
	struct binder_proc *proc;

	binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
		     current->group_leader->pid, current->pid);

	proc = kzalloc(sizeof(*proc), GFP_KERNEL);//1、创建并分配一个binder_proc空间来保存Binder数据。
	if (proc == NULL)
		return -ENOMEM;
	get_task_struct(current);
	proc->tsk = current;//2、增加当前线程/进程的引用计数,给binder_proc的tsk字段赋current值。
	//3、实现binder_proc队列的初始化。
	INIT_LIST_HEAD(&proc->todo);//使用INIT_LIST_HEAD初始化链表头todo
	init_waitqueue_head(&proc->wait);//初始化等待队列wait
	proc->default_priority = task_nice(current);//设置默认优先级(default_priority)为当前进程的nice值
	mutex_lock(&binder_lock);//加锁
	binder_stats_created(BINDER_STAT_PROC);//4、增加BINDER_STAT_PROC的对象计数,
	hlist_add_head(&proc->proc_node, &binder_procs);//通过hlist_add_head把创建的binder_proc对象添加到全局的binder_proc哈希表中,这样任何一个进程就都可以访问到其他进程的binder_proc对象了
	proc->pid = current->group_leader->pid;//5、把当前进程(或线程)的线程组的pid(pid指向线程id)赋值给proc的pid字段,。
	INIT_LIST_HEAD(&proc->delivered_death);
	filp->private_data = proc;//同时把创建的binder_proc对象指针赋值给filp的private_data对象并保存
	mutex_unlock(&binder_lock);//解锁

	if (binder_debugfs_dir_entry_proc) {
		char strbuf[11];
		snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
		proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,//6、在binder_proc 目录中创建只读文件/proc/binder/proc/$pid,其功能是输出当前binder proc对象的状态。
			binder_debugfs_dir_entry_proc, proc, &binder_proc_fops);//文件名以pid命名,但该pid字段并不是当前进程或线程的id,而是线程组的pid,表示是线程组中第一个线程的pid(current->group_leader->pid)
	}																//并且在创建该文件时也指定了操作该文件的函数接口为binder_read_proc_proc,此函数的参数表示创建的binder_proc对象proc

	return 0;
}

binder_become_context_manager

//frameworks\native\cmds\servicemanager\binder.c
int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

ioctl会调用Binder驱动的binder_ioctl函数,去注册成为管理者。

binder_loop

将servicemanger注册为Binder的上下文管理者后,它就是Binder机制的管理者了,它会在系统运行期间处理Client端的请求,因为请求的时间不确定性,这里采用了无限循环来实现。也就是binder_loop

//frameworks\native\cmds\servicemanager\binder.c
void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;
    uint32_t readbuf[32];

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;
    //当前线程注册为Binder的指令, 写入到binder的数据
    readbuf[0] = BC_ENTER_LOOPER;
    //将BC_ENTER_LOOPER指令写入到Binder驱动,
    //将当前的ServiceManager线程注册为了一个Binder线程(注意ServiceManager本身也是一个Binder线程)。
    //注册为Binder线程之后,就可以处理进程间的请求了
    binder_write(bs, readbuf, sizeof(uint32_t));
    //不断的循环遍历
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;
        //使用BINDER_WRITE_READ指令查询Binder驱动中是否有请求。
        //如果有请求,就走到下面的binder_parse部分处理,如果没有,当前的ServiceManager线程就会在Binder驱动中水命,等待新的进程间请求
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
        //走到这里说明有请求信息。将请求的信息用binder_parse来处理,处理方法是func
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
    }
}

binder_write

//frameworks\native\cmds\servicemanager\binder.c
int binder_write(struct binder_state *bs, void *data, size_t len)
{
    struct binder_write_read bwr;
    int res;

    bwr.write_size = len;
    bwr.write_consumed = 0;
    bwr.write_buffer = (uintptr_t) data;
    bwr.read_size = 0;
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    //BINDER_WRITE_READ既可以读也可以写。关键在于read_size和write_size。
    //如果write_size>0。则是写。如果read_size>0则是读。
    //如果都大于0,则先写,再读
    //这里很明显write_buffer>0, 而read_buffer==0
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    if (res < 0) {
        fprintf(stderr,"binder_write: ioctl failed (%s)\n",
                strerror(errno));
    }
    return res;
}

驱动侧:

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);
        //用户空间数据bwr的地址
	void __user *ubuf = (void __user *)arg;
	binder_selftest_alloc(&proc->alloc);

	trace_binder_ioctl(cmd, arg);

	ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
	thread = binder_get_thread(proc);
	switch (cmd) {
	case BINDER_WRITE_READ:
		ret = binder_ioctl_write_read(filp, cmd, arg, thread);
		if (ret)
			goto err;
		break;
   ...

}
static int binder_ioctl_write_read(struct file *filp,
				unsigned int cmd, unsigned long arg,
				struct binder_thread *thread)
{
	int ret = 0;
        //当前调用方进程的binder_proc信息
	struct binder_proc *proc = filp->private_data;
	unsigned int size = _IOC_SIZE(cmd);
         //(void __user *)arg 指的是arg值是一个用户空间的地址,
        //不能直接进行拷贝等,要使用例如copy_from_user,copy_to_user等函数
	void __user *ubuf = (void __user *)arg;
	struct binder_write_read bwr;

      ...
      //将用户空间的bwr的ubuf数据copy到内核空间bwr中
	if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
		ret = -EFAULT;
		goto out;
	}
      ...
        //表示往binder写数据
	if (bwr.write_size > 0) {
                //设置进入looper BC_ENTER_LOOPER
		ret = binder_thread_write(proc, thread,
					  bwr.write_buffer,
					  bwr.write_size,
					  &bwr.write_consumed);
		trace_binder_write_done(ret);
		if (ret < 0) {
			bwr.read_consumed = 0;
			if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
				ret = -EFAULT;
			goto out;
		}
	}
	if (bwr.read_size > 0) {
		ret = binder_thread_read(proc, thread, bwr.read_buffer,
					 bwr.read_size,
					 &bwr.read_consumed,
					 filp->f_flags & O_NONBLOCK);
		trace_binder_read_done(ret);
		binder_inner_proc_lock(proc);
		if (!binder_worklist_empty_ilocked(&proc->todo))
			binder_wakeup_proc_ilocked(proc);
		binder_inner_proc_unlock(proc);
		if (ret < 0) {
			if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
				ret = -EFAULT;
			goto out;
		}
	}
binder_thread_write
{
		case BC_ENTER_LOOPER:
	                ...
			thread->looper |= BINDER_LOOPER_STATE_ENTERED;
			break;
}

将BC_ENTER_LOOPER指令写入到Binder驱动,之后将当前的ServiceManager线程注册为了一个Binder线程(注意ServiceManager本身也是一个Binder线程)。 注册为Binder线程之后,ServiceManager就可以不断的往binder驱动请求数据,binder驱动通过binder_loop循环不断的查询是否有请求。

ServiceManager注册服务

引用:

juejin.cn/post/694496…