Binder传输数据
一次完整的 Binder IPC 通信过程通常是这样:
- 首先 Binder 驱动在内核空间创建一个数据接收缓存区。
- 接着在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系。
- 发送方进程通过系统调用 copyfromuser() 将数据 copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。
MMAP的原理
Linux通过将一个虚拟内存区域与一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容,这个过程称为内存映射(memory mapping)。
对文件进行mmap,会在进程的虚拟内存分配地址空间,创建映射关系。
实现这样的映射关系后,就可以采用指针的方式读写操作这一段内存,而系统会自动回写到对应的文件磁盘上。
所有的系统资源管理都是在内核空间中完成的。比如读写磁盘文件,分配回收内存,从网络接口读写数据等等。用户空间通过系统调用让内核空间完成这些功能。
写文件流程:
1、调用write,告诉内核需要写入数据的开始地址与长度。 2、内核将数据拷贝到内核缓存。 3、由操作系统调用,将数据拷贝到磁盘,完成写入。
Binder框架中ServiceManager的作用?
Binder框架 是基于 C/S 架构的。由一系列的组件组成,包括 Client、Server、ServiceManager、Binder驱动,其中 Client、Server、Service Manager 运行在用户空间,Binder 驱动运行在内核空间。如下图所示:
4.Binder驱动
Binder架构
Binder的jni方法注册讲解
1.zygote启动
1-1.启动zygote进程
zygote是由init进程通过解析 init.zygote.rc 文件而创建的,zygote所对应的可执行程序 app_process,所对应的源文件是 app_main.cpp ,进程名为zygote。
// system/core/rootdir/init.zygote32.rc
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --startsystem-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
writepid /dev/cpuset/foreground/tasks
1-2.执行app_main.cpp中的main方法
启动zygote的入口函数是 app_main.cpp 中的main方法。
//frameworks/base/cmds/app_process/app_main.cpp
// 186
int main(int argc, char* const argv[])
// 248 将zygote标志位置为true。
if (strcmp(arg, “–zygote”) == 0) {
zygote = true;
}
// 306 运行AndroidRuntime.cpp的start方法
if (zygote) {
runtime.start(“com.android.internal.os.ZygoteInit”, args, zygote);
}
1-3.AndroidRuntime::start
调用startReg方法来完成jni方法的注册。
frameworks/base/core/jni/AndroidRuntime.cpp
//frameworks/base/core/jni/AndroidRuntime.cpp
// 1007
void AndroidRuntime::start(const char* className, const Vector& options, bool zygote)
// 1051
if (startReg(env) < 0) {
//1440
int AndroidRuntime::startReg(JNIEnv* env)
// 1459 注册jni方法
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
// 1283
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env) {
// 循环注册jni方法
for (size_t i = 0; i < count; i++) {
if (array[i].mProc(env) < 0) {
return -1;
}
}
return 0;
}
// 1296
static const RegJNIRec gRegJNI[] = {
// 1312
REG_JNI(register_android_os_Binder),
}
2.register_android_os_Binder
//frameworks/base/core/jni/android_util_Binder.cpp
// 1282
int register_android_os_Binder(JNIEnv* env) {
if (int_register_android_os_Binder(env) < 0)
return -1;
if (int_register_android_os_BinderInternal(env) < 0)
return -1;
if (int_register_android_os_BinderProxy(env) < 0)
return -1;
}
2-1.int_register_android_os_Binder
//frameworks/base/core/jni/android_util_Binder.cpp
// 843
static const JNINativeMethod gBinderMethods[] = {
/* name, signature, funcPtr /
{ “getCallingPid”, “()I”, (void)android_os_Binder_getCallingPid },
{ “getCallingUid”, “()I”, (void*)android_os_Binder_getCallingUid },
{ “clearCallingIdentity”, “()J”, (void*)android_os_Binder_clearCallingIdentity },
{ “restoreCallingIdentity”, “(J)V”, (void*)android_os_Binder_restoreCallingIdentity},
{ “setThreadStrictModePolicy”, “(I)V”, (void*)android_os_Binder_setThreadStrictModePolicy },
{ “getThreadStrictModePolicy”, “()I”, (void*)android_os_Binder_getThreadStrictModePolicy },
{ “flushPendingCommands”, “()V”, (void*)android_os_Binder_flushPendingCommands },
{ “init”, “()V”, (void*)android_os_Binder_init },
{ “destroy”, “()V”, (void*)android_os_Binder_destroy },
{ “blockUntilThreadAvailable”, “()V”, (void*)android_os_Binder_blockUntilThreadAvailable }
};
// 857
const char* const kBinderPathName = “android/os/Binder”;
// 859
static int int_register_android_os_Binder(JNIEnv* env) {
// 查找文件 kBinderPathName = “android/os/Binder”,返回对应Class对象
jclass clazz = FindClassOrDie(env, kBinderPathName);
// 通过gBinderOffsets结构体,保存Java层Binder类的信息,为JNI层访问Java层提供通道
gBinderOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gBinderOffsets.mExecTransact = GetMethodIDOrDie(env, clazz, “execTransact”, “(IJJI)Z”);
gBinderOffsets.mObject = GetFieldIDOrDie(env, clazz, “mObject”, “J”);
// 通过RegisterMethodsOrDie,将为gBinderMethods数组完成映射关系,从而为Java层访问 JNI层提供通道
return RegisterMethodsOrDie(
env, kBinderPathName,
gBinderMethods,
NELEM(gBinderMethods));
}
2-2.int_register_android_os_BinderInternal
//frameworks/base/core/jni/android_util_Binder.cpp
// 925
static const JNINativeMethod gBinderInternalMethods[] = {
/* name, signature, funcPtr /
{ “getContextObject”, “()Landroid/os/IBinder;”, (void)android_os_BinderInternal_getContextObject },
{ “joinThreadPool”, “()V”, (void*)android_os_BinderInternal_joinThreadPool },
{ “disableBackgroundScheduling”, “(Z)V”, (void*)android_os_BinderInternal_disableBackgroundScheduling },
{ “handleGc”, “()V”, (void*)android_os_BinderInternal_handleGc } };
// 933
const char* const kBinderInternalPathName = “com/android/internal/os/BinderInternal”;
// 935
static int int_register_android_os_BinderInternal(JNIEnv* env){
// 查找文件kBinderInternalPathName = “com/android/internal/os/BinderInternal”,返回Class对象
jclass clazz = FindClassOrDie(env, kBinderInternalPathName);
// 通过gBinderInternalOffsets,保存Java层BinderInternal类的信息,为JNI层访问java 层提供通道
gBinderInternalOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gBinderInternalOffsets.mForceGc = GetStaticMethodIDOrDie(env, clazz, “forceBinderGc”, “()V”);
// 通过RegisterMethodsOrDie(),将为gBinderInternalMethods数组完成映射关系,从而为 Java层访问JNI层提供通道
return RegisterMethodsOrDie(
env, kBinderInternalPathName,
gBinderInternalMethods,
NELEM(gBinderInternalMethods));
}
2-3.int_register_android_os_BinderProxy
//frameworks/base/core/jni/android_util_Binder.cpp
// 1241
static const JNINativeMethod gBinderProxyMethods[] = {
/* name, signature, funcPtr /
{“pingBinder”, “()Z”, (void)android_os_BinderProxy_pingBinder},
{“isBinderAlive”, “()Z”, (void*)android_os_BinderProxy_isBinderAlive},
{“getInterfaceDescriptor”, “()Ljava/lang/String;”, (void*)android_os_BinderProxy_getInterfaceDescriptor},
{“transactNative”, “(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z”, (void*)android_os_BinderProxy_transact},
{“linkToDeath”, “(Landroid/os/IBinderKaTeX parse error: Expected 'EOF', got '}' at position 63: …oxy_linkToDeath}̲, {"unlinkToDe…DeathRecipient;I)Z”, (void*)android_os_BinderProxy_unlinkToDeath},
{“destroy”, “()V”, (void*)android_os_BinderProxy_destroy}, };
// 1252
const char* const kBinderProxyPathName = “android/os/BinderProxy”;
// 1254
static int int_register_android_os_BinderProxy(JNIEnv* env) {
// 查找文件 kBinderProxyPathName = “android/os/BinderProxy”,返回对应Class对象
jclass clazz = FindClassOrDie(env, “java/lang/Error”);
gErrorOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
// 通过gBinderProxyOffsets,保存Java层BinderProxy类的信息,为JNI层访问Java提供通道
clazz = FindClassOrDie(env, kBinderProxyPathName);
gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gBinderProxyOffsets.mConstructor = GetMethodIDOrDie(env, clazz, “”, " ()V");
gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, “sendDeathNotice”, “(Landroid/os/IBinder$DeathRecipient;)V”);
gBinderProxyOffsets.mObject = GetFieldIDOrDie(env, clazz, “mObject”, “J”);
gBinderProxyOffsets.mSelf = GetFieldIDOrDie(env, clazz, “mSelf”,“Ljava/lang/ref/WeakReference;”);
gBinderProxyOffsets.mOrgue = GetFieldIDOrDie(env, clazz, “mOrgue”, “J”);
clazz = FindClassOrDie(env, “java/lang/Class”);
gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, “getName”, " ()Ljava/lang/String;");
// 通过RegisterMethodsOrDie(),将为gBinderProxyMethods数组完成映射关系,从而为Java 层访问JNI层提供通道
return RegisterMethodsOrDie(
env, kBinderProxyPathName,
gBinderProxyMethods, NELEM(gBinderProxyMethods));
}
Binder驱动注册讲解
1.binder_init
主要工作:
- 分配内存
- 初始化设备
- 放入链表 binder_devices
kernel/drivers/staging/android/binder.c
// 4290 设备驱动入口函数
device_initcall(binder_init);
// 4213
static int __init binder_init(void)
// 4220 创建名为binder的单线程的工作队列
binder_deferred_workqueue = create_singlethread_workqueue(“binder”);
// 4269
ret = init_binder_device(device_name);
kernel/drivers/staging/android/binder.c
// 4186
static 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
主要工作:
- 创建binder_proc对象
- 当前进程信息proc
- filp->private_data = proc
- 添加到binder_procs链表中
kernel/drivers/staging/android/binder.c
// 3454
static int binder_open(struct inode *nodp, struct file *filp)
// 3462 为binder_proc结构体在kernel分配内存空间
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
// 3465 将当前线程的task保存到binder进程的tsk
get_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对象创建数加1
hlist_add_head(&proc->proc_node, &binder_procs); // 将proc_node节点添加到 binder_procs的队列头部
proc->pid = current->group_leader->pid; // 进程pid
INIT_LIST_HEAD(&proc->delivered_death); // 初始化已分发的死亡通知列表
filp->private_data = proc; // 将这个binder_proc与filp关联起来,这样下次通过filp就能找 到这个proc了
binder_unlock(func); // 释放同步锁
3.binder_mmap
主要工作:
- 通过用户空间的虚拟内存大小,分配一块内核的虚拟内存
- 分配一块物理内存(4KB)
- 把这块物理内存分别映射到用户空间的虚拟内存和内核的虚拟内存。
kernel/drivers/staging/android/binder.c
// 3355 vma:进程的虚拟内存
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
// 3366 保证映射内存大小不超过4M
if ((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
// 576
static 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
// 3355
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
// 3425
list_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
// 494
static void binder_insert_free_buffer(struct binder_proc *proc,struct binder_buffer *new_buffer)
// 511
while (*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
// 3241
static 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,并将当前线程添加到当前的proc
thread = 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
// 3136
static 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))) {
// 3160
if (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拷贝到ubuf
if (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,
};
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
文末
架构师不是天生的,是在项目中磨练起来的,所以,我们学了技术就需要结合项目进行实战训练,那么在Android里面最常用的架构无外乎 MVC,MVP,MVVM,但是这些思想如果和模块化,层次化,组件化混和在一起,那就不是一件那么简单的事了,我们需要一个真正身经百战的架构师才能讲解透彻其中蕴含的深理。
一线互联网Android面试题总结含详解(初级到高级专题)
z8ebEW-1711548912549)]
[外链图片转存中…(img-niLwBZNA-1711548912549)]
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
[外链图片转存中…(img-A53IjZkR-1711548912551)]
文末
架构师不是天生的,是在项目中磨练起来的,所以,我们学了技术就需要结合项目进行实战训练,那么在Android里面最常用的架构无外乎 MVC,MVP,MVVM,但是这些思想如果和模块化,层次化,组件化混和在一起,那就不是一件那么简单的事了,我们需要一个真正身经百战的架构师才能讲解透彻其中蕴含的深理。
[外链图片转存中…(img-dMgPfZ5e-1711548912552)]
[外链图片转存中…(img-hYXgIkl8-1711548912552)]
一线互联网Android面试题总结含详解(初级到高级专题)
[外链图片转存中…(img-aGlDi2Jh-1711548912553)]