写点什么

Android 基础之 Binder 分析,flutter 环境变量配置

用户头像
Android架构
关注
发布于: 刚刚

// 4186static int __init init_binder_device(const char *name) {


int ret;


struct binder_device *binder_device;


// 4191 1.为 binder 设备分配内存 binder_device = kzalloc(sizeof(*binder_device), GFP_KERNEL);


// 4195 2.初始化设备 binder_device->miscdev.fops = &binder_fops; // 设备的文件操作结构,这是 file_operations 结构


binder_device->miscdev.minor = MISC_DYNAMIC_MINOR; // 次设备号 动态分配 binder_device->miscdev.name = name; // 设备名,"binder"


binder_device->context.binder_context_mgr_uid = INVALID_UID;


binder_device->context.name = name;


// 4202 misc 驱动注册 ret = misc_register(&binder_device->miscdev);


// 4208 3.将 hlist 节点添加到 binder_devices 为表头的设备链表 hlist_add_head(&binder_device->hlist, &binder_devices);return ret;}


misc 设备:是没有硬件的一段内存,优点是注册简单

2.binder_open

主要工作:


  1. 创建 binder_proc 对象

  2. 当前进程信息 proc

  3. filp->private_data = proc

  4. 添加到 binder_procs 链表中


kernel/drivers/staging/android/binder.c


// 3454static int binder_open(struct inode *nodp, struct file *filp)


// 3462 为 binder_proc 结构体在 kernel 分配内存空间 proc = kzalloc(sizeof(*proc), GFP_KERNEL);


// 3465 将当前线程的 task 保存到 binder 进程的 tskget_task_struct(current); proc->tsk = current;INIT_LIST_HEAD(&proc->todo); // 初始化 todo 列表 init_waitqueue_head(&proc->wait); // 初始化 wait 队列 proc->default_priority = task_nice(current); // 将当前进程的 nice 值转换为进程优先级


// 3474 同步锁,因为 binder 支持多线程访问 binder_lock(func);binder_stats_created(BINDER_STAT_PROC); // binder_proc 对象创建数加 1hlist_add_head(&proc->proc_node, &binder_procs); // 将 proc_node 节点添加到 binder_procs 的队列头部 proc->pid = current->group_leader->pid; // 进程 pidINIT_LIST_HEAD(&proc->delivered_death); // 初始化已分发的死亡通知列表 filp->private_data = proc; // 将这个 binder_proc 与 filp 关联起来,这样下次通过 filp 就能找 到这个 proc 了 binder_unlock(func); // 释放同步锁

3.binder_mmap

主要工作:


  1. 通过用户空间的虚拟内存大小,分配一块内核的虚拟内存

  2. 分配一块物理内存(4KB)

  3. 把这块物理内存分别映射到用户空间的虚拟内存和内核的虚拟内存。


kernel/drivers/staging/android/binder.c


// 3355 vma:进程的虚拟内存 static int binder_mmap(struct file *filp, struct vm_area_struct *vma)// 3366 保证映射内存大小不超过 4Mif ((vma->vm_end - vma->vm_start) > SZ_4M)


vma->vm_end = vma->vm_start + SZ_4M;


// 3382 同步锁,保证一次只有一个进程分配内存,保证多进程间的并发访问 mutex_lock(&binder_mmap_lock);// 是否已经做过映射,执行过则进入 if,goto 跳转,释放同步锁后结束 binder_mmap 方法 if (proc->buffer) {


goto err_already_mapped;}// 采用 VM_IOREMAP 方式,分配一个连续的内核虚拟内存,与进程虚拟内存大小一致 area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);// 内存分配不成功直接报错 if (area == NULL) {


ret = -ENOMEM;


failure_string = "get_vm_area";


goto err_get_vm_area_failed;}// 将 proc 中的 buffer 指针指向这块内核的虚拟内存 proc->buffer = area->addr;// 计算出用户空间和内核空间的地址偏移量。地址偏移量 = 用户虚拟内存地址 - 内核虚拟内存地址 proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;mutex_unlock(&binder_mmap_lock); // 释放锁


// 3407 分配物理页的指针数组,数组大小为 vma 的等效 page 个数 ,4kb 的物理内存 proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);


// 3418 分配物理页面,同时映射到内核空间和进程空间,先分配 1 个物理页。if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma))


kernel/drivers/staging/android/binder.c


// 576static int binder_update_page_range(struct binder_proc *proc, int allocate,void *start, void *end, ?


struct vm_area_struct *vma)


// 609 allocate 为 1,代表分配内存过程。如果为 0 则代表释放内存过程 if (allocate == 0)


goto free_range;


// 624 分配一个 page 的物理内存*page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);


// 630 物理空间映射到虚拟内核空间 ret = map_kernel_range_noflush((unsigned long)page_addr,


PAGE_SIZE, PAGE_KERNEL, page);


// 641 物理空间映射到虚拟进程空间 ret = vm_insert_page(vma, user_page_addr, page[0]);


kernel/drivers/staging/android/binder.c


// 3355static int binder_mmap(struct file *filp, struct vm_area_struct *vma)


// 3425list_add(&buffer->entry, &proc->buffers);// 将 buffer 连入 buffers 链表中 buffer->free = 1; // 此内存可用 binder_insert_free_buffer(proc, buffer);// 将 buffer 插入 proc->free_buffers 链表中 proc->free_async_space = proc->buffer_size / 2; // 异步的可用空闲空间大小 barrier();proc->files = get_files_struct(current);proc->vma = vma;proc->vma_vm_mm = vma->vm_mm;

3-1.binder_insert_free_buffer

kernel/drivers/staging/android/binder.c


// 494static void binder_insert_free_buffer(struct binder_proc *proc,struct binder_buffer *new_buffer)


// 511while (*p) {


parent = *p;


buffer = rb_entry(parent, struct binder_buffer, rb_node);


// 计算得出空闲内存的大小 buffer_size = binder_buffer_size(proc, buffer);if (new_buffer_size < buffer_size)


p = &parent->rb_left;


else


p = &parent->rb_right;}rb_link_node(&new_buffer->rb_node, parent, p); // 将 buffer 插入 proc->free_buffers 链表中 rb_insert_color(&new_buffer->rb_node, &proc->free_buffers)

4.binder_ioctl

只要做读写操作


kernel/drivers/staging/android/binder.c


// 3241static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)


// 3254 进入休眠状态,直到中断唤醒 ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);


// 3259 根据当前进程的 pid,从 binder_proc 中查找 binder_thread,// ? ? 如果当前线程已经加入到 proc 的线程队列则直接返回,// ? ? 如果不存在则创建 binder_thread,并将当前线程添加到当前的 procthread = binder_get_thread(proc);


// 3265 进行 binder 的读写操作 switch (cmd) {


case BINDER_WRITE_READ:


ret = binder_ioctl_write_read(filp, cmd, arg, thread);


if (ret)

4-1.binder_ioctl_write_read

kernel/drivers/staging/android/binder.c


// 3136static int binder_ioctl_write_read(struct file *filp,


unsigned int cmd, unsigned long arg,


struct binder_thread *thread)


// 3150 把用户空间数据 ubuf 拷贝到 bwr ,copy 的数据头,不是有效数据 if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {


// 3160if (bwr.write_size > 0) { // 当写缓存中有数据,则执行 binder 写操作 ret = binder_thread_write(proc, thread,


bwr.write_buffer,


bwr.write_size,


&bwr.write_consumed);}if (bwr.read_size > 0) { // 当读缓存中有数据,则执行 binder 读操作 ret = binder_thread_read(proc, thread, bwr.read_buffer,


bwr.read_size,


&bwr.read_consumed,


filp->f_flags & O_NONBLOCK); ?


// 进程 todo 队列不为空,则唤醒该队列中的线程 if (!list_empty(&proc->todo))


wake_up_interruptible(&proc->wait);}


// 3192 把内核空间数据 bwr 拷贝到 ubufif (copy_to_user(ubuf, &bwr, sizeof(bwr))){

数据结构

file_operations

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_proc

每个进程调用 open()打开 binder 驱动都会创建该结构体,用于管理 IPC 所需的各种信息。


struct binder_proc {


struct hlist_node proc_node; // 进程节点 struct rb_root threads; // binder_thread 红黑树的根节点 struct rb_root nodes; // binder_node 红黑树的根节点 struct rb_root refs_by_desc; // binder_ref 红黑树的根节点(以 handle 为 key)struct rb_root refs_by_node; // binder_ref 红黑树的根节点(以 ptr 为 key)int pid; // 相应进程 idstruct vm_area_struct *vma; // 指向进程虚拟地址空间的指针 struct mm_struct *vma_vm_mm; // 相应进程的内存结构体 struct task_struct *tsk; // 相应进程的 task 结构体 struct files_struct *files; // 相应进程的文件结构体 struct hlist_node deferred_work_node;


int deferred_work;


void *buffer; // 内核空间的起始地址 ptrdiff_t user_buffer_offset; // 内核空间与用户空间的地址偏移量 struct list_head buffers; // 所有的 bufferstruct rb_root free_buffers; // 空闲的 bufferstruct rb_root allocated_buffers; // 已分配的 buffersize_t free_async_space; // 异步的可用空闲空间大小 struct page **pages; // 指向物理内存页指针的指针 size_t buffer_size; // 映射的内核空间大小 uint32_t buffer_free; /


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


/ 可用内存总大小 struct list_head todo; // 进程将要做的事 wait_queue_head_t wait; // 等待队列 struct binder_stats stats; // binder 统计信息 struct list_head delivered_death; // 已分发的死亡通知 int max_threads; // 最大线程数 int requested_threads; // 请求的线程数 int requested_threads_started; // 已启动的请求线程数 int ready_threads; // 准备就绪的线程个数 long default_priority; // 默认优先级 struct dentry *debugfs_entry;


struct binder_context *context;};

binder_node

struct binder_node {


int debug_id; // 节点创建时分配,具有全局唯一性,用于调试使用 struct binder_work work;


union {


struct rb_node rb_node; // binder 节点正常使用,unionstruct hlist_node dead_node; // binder 节点已销毁,union};


struct binder_proc *proc; // binder 所在的进程,见后面小节 struct hlist_head refs; // 所有指向该节点的 binder 引用队列 int internal_strong_refs;


int local_weak_refs;


int local_strong_refs;


binder_uintptr_t ptr; // 指向用户空间 binder_node 的指针,对应于 flat_binder_object.binderbinder_uintptr_t cookie; // 指向用户空间 binder_node 的指针,附件数据,对应于 flat_binder_object.cookieunsigned has_strong_ref:1; // 占位 1bitunsigned pending_strong_ref:1; // 占位 1bitunsigned has_weak_ref:1; // 占位 1bitunsigned pending_weak_ref:1; // 占位 1bitunsigned has_async_transaction:1; // 占位 1bitunsigned accept_fds:1; // 占位 1bitunsigned min_priority:8; // 占位 8bit,最小优先级 struct list_head async_todo; // 异步 todo 队列};

binder_buffer

struct binder_buffer {


struct list_head entry; // buffer 实体的地址 struct rb_node rb_node; // buffer 实体的地址/* by address */


unsigned free:1; // 标记是否是空闲 buffer,占位 1bitunsigned allow_user_free:1; // 是否允许用户释放,占位 1bitunsigned async_transaction:1; // 占位 1bitunsigned debug_id:29; // 占位 29bitstruct binder_transaction *transaction; // 该缓存区的需要处理的事务 struct binder_node *target_node; // 该缓存区所需处理的 Binder 实体 size_t data_size; // 数据大小 size_t offsets_size; // 数据偏移量 size_t extra_buffers_size;


uint8_t data[0]; // 数据地址};

5.如何启动 service_manager 服务

1.启动 servicemanager 进程

ServiceManager 是由 init 进程通过解析 init.rc 文件而创建的,其所对应的可执行程序 servicemanager, 所对应的源文件是 service_manager.c,进程名为 servicemanager。


system/core/rootdir/init.rc// 602service servicemanager /system/bin/servicemanager ?


class core ?


user system ?


group system ?


critical ?


onrestart restart healthd ?


onrestart restart zygote ?


onrestart restart media ?


onrestart restart surfaceflinger ?


onrestart restart drm

2.main

启动 ServiceManager 的入口函数是 service_manager.c 中的 main()方法。


frameworks/native/cmds/servicemanager/service_manager.c// 354int main(int argc, char **argv)


// 358 打开 binder 驱动,申请 128k 字节大小的内存空间---见后面小节 bs = binder_open(128*1024);


// 364 设为守护进程,成为 binder 大管理者---见后面小节 if (binder_become_context_manager(bs)) {


// 391 进入无限循环,处理 client 端发来的请求---见后面小节 binder_loop(bs, svcmgr_handler);

2-1.binder_open

2-2.binder_become_context_manager

2-2-1.binder_ioctl
2-2-2.binder_ioctl_set_ctx_mgr

2-2-2-1.binder_new_node

2-3.binder_loop

2-3-1.binder_write
2-3-2.binder_thread_write
2-3-3.binder_thread_read

6.如何获取 service_manager 服务


7.AIDL

aidl 主要负责两个进行之间进行通信,在使用上,分为两个角色:服务端(负责被调用)、客户端(负责调用)。两个进程都可以为服务端和客户端来实现互相调用。


  1. 两端建立相同的 AIDL,包名文件名一致,如果有数据类,数据类也要一模一样。

  2. 服务端实现一个服务,以及其中的 stub 方法;

  3. 客户端绑定服务,并调用方法,其中实现由 proxy 完成


使用参考:blog.csdn.net/weixin_3381…

AIDL 生成的类:参考

手动实现一个 AILD

调用的接口

public interface IPersonManager extends IInterface {


void addPerson(Person person) throws RemoteException;


List<Person> getPersonList() throws RemoteException;}


Person


public class Person implements Parcelable {


private String name;private int grade;


public Person(String name, int grade) {this.name = name;this.grade = grade;}


protected Person(Parcel in) {this.name = in.readString();this.grade = in.readInt();}


public static final Creator<Person> CREATOR = new Creator<Person>() {@Overridepublic Person createFromParcel(Parcel in) {return new Person(in);}


@Overridepublic Person[] newArray(int size) {return new Person[size];}};


@Overridepublic int describeContents() {return 0;}


@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeString(name);dest.writeInt(grade);}


@Overridepublic String toString() {return "Person{" +"name='" + name + ''' +", grade=" + grade +'}';}}

服务端

Stub


public abstract class Stub extends Binder implements IPersonManager {


private static final String DESCRIPTOR = "com.enjoy.binder.common.IPersonManager";


public Stub() {this.attachInterface(this, DESCRIPTOR);}


public static IPersonManager asInterface(IBinder binder) {if ((binder == null)) {return null;}IInterface iin = binder.queryLocalInterface(DESCRIPTOR);if ((iin != null) && (iin instanceof IPersonManager)) {return (IPersonManager) iin;}return new Proxy(binder);}


@Overridepublic IBinder asBinder() {return this;}


@Overrideprotected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {switch (code) {case INTERFACE_TRANSACTION:reply.writeString(DESCRIPTOR);return true;


case TRANSACTION_addPerson:Log.e("leo", "Stub,TRANSACTION_addPerson: " + Thread.currentThread());data.enforceInterface(DESCRIPTOR);Person arg0 = null;if ((0 != data.readInt())) {arg0 = Person.CREATOR.createFromParcel(data);}this.addPerson(arg0);reply.writeNoException();return true;


case TRANSACTION_getPersonList:data.enforceInterface(DESCRIPTOR);List<Person> result = this.getPersonList();reply.writeNoException();reply.writeTypedList(result);return true;}return super.onTransact(code, data, reply, flags);}


static final int TRANSACTION_addPerson = IBinder.FIRST_CALL_TRANSACTION;static final int TRANSACTION_getPersonList = IBinder.FIRST_CALL_TRANSACTION + 1;}


RemoteService


public class RemoteService extends Service {


private ArrayList<Person> persons;


@Nullable@Overridepublic IBinder onBind(Intent intent) {persons = new ArrayList<>();Log.e("LeoAidlService", "success onBind");return iBinder;}


private IBinder iBinder = new Stub() {@Overridepublic void addPerson(Person person) throws RemoteException {persons.add(person);}


@Overridepublic List<Person> getPersonList() throws RemoteException {return persons;}};}


AndroidManifest.xml


<serviceandroid:name=".server.RemoteService"android:exported="true"android:process=":remote"><intent-filter><action android:name="com.xxx.binder" /><category android:name="android.intent.category.DEFAULT" /></intent-filter></service>

客户端

Proxy


public class Proxy implements IPersonManager {


private static final String DESCRIPTOR = "com.enjoy.binder.common.IPersonManager";


private IBinder mRemote;


public Proxy(IBinder remote) {mRemote = remote;}


@Overridepublic void addPerson(Person person) throws RemoteException {Parcel data = Parcel.obtain();Parcel reply = Parcel.obtain();try {data.writeInterfaceToken(DESCRIPTOR);if ((person != null)) {data.writeInt(1);person.writeToParcel(data, 0);} else {data.writeInt(0);}Log.e("leo", "Proxy,addPerson: " + Thread.currentThread());//同步的情况,transcat 调用后会挂起,一般都是同步 mRemote.transact(Stub.TRANSACTION_addPerson, data, reply, 0);reply.readException();} finally {reply.recycle();data.recycle();}}


@Overridepublic List<Person> getPersonList() throws RemoteException {Parcel data = Parcel.obtain();Parcel reply = Parcel.obtain();List<Person> result;try {data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_getPersonList, data, reply, 0);reply.readException();result = reply.createTypedArrayList(Person.CREATOR);} finally {reply.recycle();data.recycle();}return result;}


@Overridepublic IBinder asBinder() {return mRemote;}}


ClientActivity


public class ClientActivity extends AppCompatActivity {

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android基础之Binder分析,flutter环境变量配置