大佬就是强!意外收获字节跳动内部资料—

void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);

int munmap(void* start,size_t length);

这里的参数的含义是:

  1. start:映射区的开始地址,设置为0时表示由系统决定映射区的起始地址;

  2. length:映射区的长度。长度单位是以字节为单位,不足一内存页按一内存页处理;

  3. prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过 o r运算合理地组合在一起;

  4. flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体;

  5. fd:有效的文件描述词。一般是由 open() 函数返回,其值也可以设置为-1,此时需要指定 flags 参数中的 MAP_ANON,表明进行的是匿名映射;

  6. off_toffset:被映射对象内容的起点。

成功执行时,mmap() 返回被映射区的指针,munmap() 返回0。失败时,mmap() 返回 MAP_FAILED[其值为(void *)-1],munmap() 返回 -1.

4.3 ServiceManger 启动

Binder 中的 ServiceManager 并非 Java 层的 ServiceManager,而是 Native 层的。启动 ServiceManager 由 init 进程通过解析 init.rc 文件而创建。启动的时候会找到上述源码目录中的 service_manager.c 文件中,并调用它的 main() 方法,

// platform/framework/native/cmds/servicemanager.c

int main(int argc, char** argv)

{

struct binder_state *bs;

char *driver;

if (argc > 1) {

driver = argv[1];

} else {

driver = “/dev/binder”;

}

// 1. 打开 binder 驱动

bs = binder_open(driver, 128*1024);

// …

// 2. 将当前的 ServiceManger 设置成上下文

if (binder_become_context_manager(bs)) {

return -1;

}

// …

// 3. 启动 binder 循环,进入不断监听状态

binder_loop(bs, svcmgr_handler);

return 0;

}

ServcieManager 启动的过程就是上面三个步骤,无需过多说明。下面我们给出这三个方法具体实现的。在下面的代码中你将看到我们之前介绍的三个函数的实际应用。相应有了前面的铺垫之后你理解起来不成问题 😃

// platform/framework/native/cmds/servicemanager.c

struct binder_state binder_open(const char driver, size_t mapsize)

{

struct binder_state *bs;

struct binder_version vers;

bs = malloc(sizeof(*bs));

if (!bs) {

errno = ENOMEM;

return NULL;

}

// 打开设备驱动

bs->fd = open(driver, O_RDWR | O_CLOEXEC);

if (bs->fd < 0) {

goto fail_open;

}

// 向驱动发送指令,获取binder版本信息

if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||

(vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {

goto fail_open;

}

bs->mapsize = mapsize;

// 通过系统调用,mmap 内存映射,mmap 必须是 page 的整数倍

bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);

if (bs->mapped == MAP_FAILED) {

goto fail_map;

}

return bs;

fail_map:

close(bs->fd);

fail_open:

free(bs);

return NULL;

}

在上面的代码中,先使用 open() 函数打开设备驱动(就是一个打开文件的操作),然后使用 ioctl() 函数向上面的设备驱动发送指令以获取设备信息。最后,通过 mmap() 函数实现内存映射,并将上述的文件描述符传入。这里的 binder_state 是一个结构体,定义如下。其实就是用来描述 binder 的状态。从上面我们也能看到它的三个变量的赋值过程。

// platform/framework/native/cmds/servicemanager.c

struct binder_state

{

int fd;

void *mapped;

size_t mapsize;

};

当然,在上面的代码中,我们又见到了久违的 goto 指令。它们主要用来处理发生一些异常的情况。

打开了驱动之后,注册为上下文的方法更加简单,

// platform/framework/native/cmds/servicemanager.c

int binder_become_context_manager(struct binder_state *bs)

{

return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);

}

复制代码

就是一个 ioctl 函数,使用指令 BINDER_SET_CONTEXT_MGR 将当前的 ServiceManager 注册为上下文。

最后就是启动 Binder 循环了。它的逻辑也没有想象中得复杂,就是启动了 for 循环,

// platform/framework/native/cmds/servicemanager.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;

readbuf[0] = BC_ENTER_LOOPER;

// 将 BC_ENTER_LOOPER 命令发送给 binder 驱动,内部调用 ioctl 函数

binder_write(bs, readbuf, sizeof(uint32_t));

for (;😉 {

bwr.read_size = sizeof(readbuf);

bwr.read_consumed = 0;

bwr.read_buffer = (uintptr_t) readbuf;

// 使用 iotcl 函数读取

res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

if (res < 0) {

break;

}

// 解析

res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);

if (res == 0) {

break;

}

if (res < 0) {

break;

}

}

}

从上面看出,函数将会在 binder_write() 中将命令发送给 Binder 驱动,以启动循环。其实内部也是调用 ioctl 函数实现的。然后程序会启动一个循环来不断读取、解析。这是服务器很典型的操作了。

当然,我们上面分析的是 ServiceManager 中向 Binder 写命令的过程,而驱动如何解析呢?当然是在驱动中实现了,详细的过程可以查看 Binder 驱动部分的源码。

4.4 Binder 的跨进程通信过程

下面我们以 AMS 作为例子来讲解下 Binder 跨进程通信的实现过程。首先,当我们调用 startActivity() 方法的时候,最终将会进入 ActivityManager 以获取 AMS,

// platform/framework/base/core/java/android/app/ActivityManager.java

final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);

final IActivityManager am = IActivityManager.Stub.asInterface(b);

return am;

这里会使用 ServiceManger 来按名称查找 AMS,查找到 Binder 对象之后将其转换成 AMS 就可以使用了。之前,我们也说过用来查找 AMS 的 SeerviceManager 本身也是一种服务。所以,它这里的方法也是通过 Binder 来实现的。那么,我们就从这里的 getService() 方法入手。

// platform/framework/base/core/java/android/os/ServiceManager.java

public static IBinder getService(String name) {

try {

IBinder service = sCache.get(name);

if (service != null) {

return service;

} else {

return Binder.allowBlocking(rawGetService(name));

}

} catch (RemoteException e) { /* … */ }

return null;

}

这里会先尝试从缓存当中取 Binder,取不到的话就从远程进行获取。这里使用 rawGetService() 方法来从远程获取 Binder,代码如下,

// platform/framework/base/core/java/android/os/ServiceManager.java

private static IBinder rawGetService(String name) throws RemoteException {

final IBinder binder = getIServiceManager().getService(name);

// …

return binder;

}

// platform/framework/base/core/java/android/os/ServiceManager.java

private static IServiceManager getIServiceManager() {

if (sServiceManager != null) {

return sServiceManager;

}

sServiceManager = ServiceManagerNative

.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));

return sServiceManager;

}

rawGetService() 方法中会使用 ServiceManagerNativegetService() 方法从远程获取 Binder. 这里的 ServiceManagerNative 本质上只是一个代理类,它实际的逻辑是由 BinderInternal.getContextObject() 返回的 Binder 实现的。

也许你已经晕了,怎么那么多 Binder……我来说明下。当要查找 AMS 的时候实际上是一个跨进程的调用过程,也就是实际的查找的逻辑是在另一个进程实现,因此需要 Binder 来通信。而查找 AMS 的远程对象实际上就是我们上面所说的 ServiceManager (Native 层的而不是 Java 层的,Java 层的 ServiceManager 是一个代理类,是用来从远程获取服务的)。

因此,按照上面的描述,BinderInternal.getContextObject() 返回的就应该是远程的 Binder 对象。于是方法进入 Native 层,

// platform/framework/base/core/jni/android_util_Binder.cpp

static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)

{

sp b = ProcessState::self()->getContextObject(NULL);

return javaObjectForIBinder(env, b);

}

这里的 ProcessState::self() 是否熟悉呢?你是否还记得在上一篇文章中,我们介绍 Android 系统启动过程的时候介绍过它。我们曾经使用它来开启 Binder 的线程池。这里的 self() 方法其实是用来获取一个单例对象的。我们可以直接由 getContextObject() 进入 getStrongProxyForHandle() 方法。从下面的方法中我们可以看出,这里调用了 BpBindercreate() 方法创建了一个 BpBinder 实例并返回,也就是我们的 ServiceManager.

// plaftorm/framework/native/libs/binder/ProcessState.cpp

sp ProcessState::getStrongProxyForHandle(int32_t handle)

{

sp result;

AutoMutex _l(mLock);

handle_entry* e = lookupHandleLocked(handle);

if (e != nullptr) {

IBinder* b = e->binder;

if (b == nullptr || !e->refs->attemptIncWeak(this)) {

// …

// 调用 BpBinder

b = BpBinder::create(handle);

e->binder = b;

if (b) e->refs = b->getWeakRefs();

result = b;

} else {

result.force_set(b);

e->refs->decWeak(this);

}

}

当我们拿到了 ServiceManager 的 Binder 之后就可以调用它的 getService() 方法来获取服务了,

// platform/framework/base/core/java/android/os/ServiceManagerNative.java

public IBinder getService(String name) throws RemoteException {

Parcel data = Parcel.obtain();

Parcel reply = Parcel.obtain();

data.writeInterfaceToken(IServiceManager.descriptor);

data.writeString(name);

mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);

IBinder binder = reply.readStrongBinder();

reply.recycle();

data.recycle();

return binder;

}

这里的 mRemote 就是之前返回的 BpBinder,这里调用它的 transact() 方法,并传入了一个方法标记 GET_SERVICE_TRANSACTION.

// platform/framework/native/libs/binder/BpBinder.cpp

status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

if (mAlive) {

status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags);

if (status == DEAD_OBJECT) mAlive = 0;

return status;

}

return DEAD_OBJECT;

}

显然这里会调用 IPCThreadState 的 self() 方法先获取一个单例的对象,然后调用它的 transact() 方法继续方法的执行。

// platform/framework/native/libs/binder/IPCThreadState.cpp

status_t IPCThreadState::transact(int32_t handle, uint32_t code,

const Parcel& data, Parcel* reply, uint32_t flags)

{

status_t err;

// …

err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);

// …

if ((flags & TF_ONE_WAY) == 0) { // OneWay 类型的调用,同步的

// …

if (reply) {

// 等待相应

err = waitForResponse(reply);

} else {

Parcel fakeReply;

err = waitForResponse(&fakeReply);

}

IF_LOG_TRANSACTIONS() {

TextOutput::Bundle _b(alog);

if (reply) alog << indent << *reply << dedent << endl;

else alog << “(none requested)” << endl;

}

} else { // 异步的

err = waitForResponse(nullptr, nullptr);

}

return err;

}

上面会调用 writeTransactionData() 方法用来将数据写入到 Parcel 中。然后将会进入 waitForResponse() 方法处理与 ServiceManager 交互的结果。而真实的交互发生的地方位于 talkWithDriver() 方法,

// platform/framework/native/libs/binder/IPCThreadState.cpp

status_t IPCThreadState::talkWithDriver(bool doReceive)

{

if (mProcess->mDriverFD <= 0) {

return -EBADF;

}

binder_write_read bwr;

const bool needRead = mIn.dataPosition() >= mIn.dataSize();

const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;

bwr.write_size = outAvail;

bwr.write_buffer = (uintptr_t)mOut.data();

if (doReceive && needRead) {

bwr.read_size = mIn.dataCapacity();

bwr.read_buffer = (uintptr_t)mIn.data();

} else {

bwr.read_size = 0;

bwr.read_buffer = 0;

}

if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

bwr.write_consumed = 0;

bwr.read_consumed = 0;

status_t err;

do {

// 通过 ioctl 读写操作,与 Binder Driver 进行交互

if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)

err = NO_ERROR;

else

err = -errno;

if (mProcess->mDriverFD <= 0) {

err = -EBADF;

}

} while (err == -EINTR);

// …

return err;

}

binder_write_read 结构体用来与 Binder 设备交换数据的结构, 通过 ioctl 与 mDriverFD 通信,是真正与 Binder 驱动进行数据读写交互的过程。先向service manager进程发送查询服务的请求(BR_TRANSACTION)。然后,service manager 会在之前开启的循环中监听到,并使用 svcmgr_handler() 方法进行处理。

// platform/framework/native/cmds/servicemanager.c

int svcmgr_handler(struct binder_state *bs,

struct binder_transaction_data *txn,

struct binder_io *msg,

struct binder_io *reply)

{

// …

switch(txn->code) {

case SVC_MGR_GET_SERVICE:

case SVC_MGR_CHECK_SERVICE:

s = bio_get_string16(msg, &len);

if (s == NULL) {

return -1;

}

handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);

if (!handle)

break;

bio_put_ref(reply, handle);

return 0;

case SVC_MGR_ADD_SERVICE: // …

case SVC_MGR_LIST_SERVICES: // …

}

return 0;

}

显然,这里会从 binder_transaction_data 中取出 code,即 SVC_MGR_GET_SERVICE,然后使用 do_find_service() 方法查找服务。然后再 binder_send_reply() 应答发起者将结果返回即可。

4.5 Binder 高效通信的原因

上面我们梳理了 Binder 通信的过程,从上面我们似乎并没有看到能证明 Binder 高效的证据。那么 Binder 究竟靠什么实现高效的呢?

实际上,Binder 之所以高效,从我们上面的代码还真看不出来。因为,我们上面的代码并没有涉及 Binder 驱动部分。正如我们之前描述的那样,ServiceManager、客户端和服务器实际是靠 Binder 驱动这个中间媒介进行交互的。而 Binder 高效的地方就发生在 Binder 驱动部分。

就像图片描述的那样,当两个进程之间需要通信的时候,Binder 驱动会在两个进程之间建立两个映射关系:内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系。这样,当把数据从 1 个用户空间拷贝到内核缓冲区的时候,就相当于拷贝到了另一个用户空间中。这样只需要做一次拷贝,省去了内核中暂存这个步骤,提升了一倍的性能。实现内存映射靠的就是上面的 mmap() 函数。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长。而不成体系的学习效果低效漫长且无助。时间久了,付出巨大的时间成本和努力,没有看到应有的效果,会气馁是再正常不过的。

所以学习一定要找到最适合自己的方式,有一个思路方法,不然不止浪费时间,更可能把未来发展都一起耽误了。

如果你是卡在缺少学习资源的瓶颈上,那么刚刚好我能帮到你。以上知识笔记全部免费分享,如有需要获取知识笔记的朋友,可以点击我的GitHub免费领取。

p()` 函数。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-bQAaM0tp-1710693037776)]
[外链图片转存中…(img-wvZncvcu-1710693037776)]
[外链图片转存中…(img-0RrXkFsj-1710693037777)]
[外链图片转存中…(img-aChsbcP2-1710693037777)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-6bl2arTW-1710693037777)]

最后

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长。而不成体系的学习效果低效漫长且无助。时间久了,付出巨大的时间成本和努力,没有看到应有的效果,会气馁是再正常不过的。

所以学习一定要找到最适合自己的方式,有一个思路方法,不然不止浪费时间,更可能把未来发展都一起耽误了。

如果你是卡在缺少学习资源的瓶颈上,那么刚刚好我能帮到你。以上知识笔记全部免费分享,如有需要获取知识笔记的朋友,可以点击我的GitHub免费领取。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值