Binder native层源码分析(六):sm返回查询结果

本文深入解析Binder机制中服务如何从服务管理器(sm)传递至客户端的过程。重点分析了svcmgr_handler处理SVC_MGR_GET_SERVICE请求的流程,包括bio_put_ref创建flat_binder_object对象,binder_send_reply构造回复数据,以及binder_translate_handle实现服务传递的细节。

假设现在有一个客户端向sm请求MediaPlayer服务,本篇文章分析sm怎么返回服务的查询结果,即svcmgr_handler中怎么处理SVC_MGR_GET_SERVICE请求。

处理SVC_MGR_GET_SERVICE请求

case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
    //得到服务名, 这里应该是String16("media.player")
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        //从service list中找到该服务名对应的handle
        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
        if (!handle)
            break;
        bio_put_ref(reply, handle);
        return 0;

前面的博文已经分析了,handle值代表MediaPlayerService的binder_node对应的binder_ref的红黑树键值,这里从service list找到handle后,交给bio_put_ref处理。

bio_put_ref

void bio_put_ref(struct binder_io *bio, uint32_t handle)
{
    struct flat_binder_object *obj;

    if (handle)
        obj = bio_alloc_obj(bio);
    else
        obj = bio_alloc(bio, sizeof(*obj));

    if (!obj)
        return;

    obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    obj->type = BINDER_TYPE_HANDLE;
    obj->handle = handle;
    obj->cookie = 0;
}

根据handle值创建了一个flat_binder_object对象,类型为BINDER_TYPE_HANDLE,写入bio(即reply)的offs区域。

binder_send_reply

在上篇博文的binder_parse中我们知道,当调用完svcmgr_handler处理请求后,会调用binder_send_reply。

//binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
void binder_send_reply(struct binder_state *bs,
                       struct binder_io *reply,
                       binder_uintptr_t buffer_to_free,
                       int status)
{
    struct {
        uint32_t cmd_free;
        binder_uintptr_t buffer;
        uint32_t cmd_reply;
        struct binder_transaction_data txn;
    } __attribute__((packed)) data;

    data.cmd_free = BC_FREE_BUFFER;//第一个协议
    data.buffer = buffer_to_free;
    data.cmd_reply = BC_REPLY;//第二个协议
    data.txn.target.ptr = 0;
    data.txn.cookie = 0;
    data.txn.code = 0;
    if (status) {
        data.txn.flags = TF_STATUS_CODE;
        data.txn.data_size = sizeof(int);
        data.txn.offsets_size = 0;
        data.txn.data.ptr.buffer = (uintptr_t)&status;
        data.txn.data.ptr.offsets = 0;
    } else {
        data.txn.flags = 0;
        data.txn.data_size = reply->data - reply->data0;
        data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);
        data.txn.data.ptr.buffer = (uintptr_t)reply->data0;
        data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;
    }
    binder_write(bs, &data, sizeof(data));
}

binder_send_reply构造了两个不同协议的数据并发送,第一个协议是BC_FREE_BUFFER,数据是本进程通信空间的地址,该空间曾被用于拷贝发送方发送的数据。由于binder_parse对数据的分析已经结束,所以这里发送BC_FREE_BUFFER协议的数据的目的应该释放这一空间。

第二个协议是BC_REPLY,数据部分是由binder_io reply填充而来的binder_transaction_data。reply中记录了handle信息。

binder_transaction分段1

前面已经分析过binder_transaction对BC_TRANSACTION的处理代码了,这里只分析binder_transaction对BC_REPLY的处理代码。

	if (reply) {
		binder_inner_proc_lock(proc);
		//在上篇博文中,binder_thread_read的结尾会把读到的t放进sm的stack中,
		//这里将其取出
		in_reply_to = thread->transaction_stack;
		if (in_reply_to == NULL) {
		//错误处理...
		}
		if (in_reply_to->to_thread != thread) {
		//错误处理...
		}
		//恢复之前的transaction_stack
		thread->transaction_stack = in_reply_to->to_parent;
		binder_inner_proc_unlock(proc);
		//取出发送进程,也就是reply的目标进程
		target_thread = binder_get_txn_from_and_acq_inner(in_reply_to);
		if (target_thread == NULL) {
			return_error = BR_DEAD_REPLY;
			return_error_line = __LINE__;
			goto err_dead_binder;
		}
		if (target_thread->transaction_stack != in_reply_to) {
		//错误处理...
		}
		target_proc = target_thread->proc;
		target_proc->tmp_ref++;
		binder_inner_proc_unlock(target_thread->proc);
	} 

从transaction_stack中找到了reply的目标线程。之后就是创建binder_transaction t并对其赋值,复制数据等,和源码分析四中分析的代码大同小异。再然后就进入了for循环处理obj,而我们传入的是BINDER_TYPE_HANDLE类型的obj,处理代码如下:

		case BINDER_TYPE_HANDLE:
		case BINDER_TYPE_WEAK_HANDLE: {
			struct flat_binder_object *fp;

			fp = to_flat_binder_object(hdr);
			ret = binder_translate_handle(fp, t, thread);
			if (ret < 0) {
				return_error = BR_FAILED_REPLY;
				return_error_param = ret;
				return_error_line = __LINE__;
				goto err_translate_failed;
			}
		} break;

读取obj后交给binder_translate_handle处理。

binder_translate_handle实现服务的传递

static int binder_translate_handle(struct flat_binder_object *fp,
				   struct binder_transaction *t,
				   struct binder_thread *thread)
{
	struct binder_proc *proc = thread->proc;//即sm的binder_proc
	struct binder_proc *target_proc = t->to_proc;//即客户端的binder_proc
	struct binder_node *node;
	struct binder_ref_data src_rdata;
	int ret = 0;
	//根据handle从sm的binder_proc中得到binder_ref,再返回binder_ref->node
	node = binder_get_node_from_ref(proc, fp->handle,
			fp->hdr.type == BINDER_TYPE_HANDLE, &src_rdata);
	if (!node) {
		binder_user_error("%d:%d got transaction with invalid handle, %d\n",
				  proc->pid, thread->pid, fp->handle);
		return -EINVAL;
	}
	if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
		ret = -EPERM;
		goto done;
	}

	binder_node_lock(node);
	//如果目标进程和node进程(服务进程)一致,则进入该if。
	//不会进入该if,跳过
	if (node->proc == target_proc) {
		if (fp->hdr.type == BINDER_TYPE_HANDLE)
			fp->hdr.type = BINDER_TYPE_BINDER;
		else
			fp->hdr.type = BINDER_TYPE_WEAK_BINDER;
		fp->binder = node->ptr;
		fp->cookie = node->cookie;
		if (node->proc)
			binder_inner_proc_lock(node->proc);
		binder_inc_node_nilocked(node,
					 fp->hdr.type == BINDER_TYPE_BINDER,
					 0, NULL);
		if (node->proc)
			binder_inner_proc_unlock(node->proc);
		trace_binder_transaction_ref_to_node(t, node, &src_rdata);
		binder_debug(BINDER_DEBUG_TRANSACTION,
			     "        ref %d desc %d -> node %d u%016llx\n",
			     src_rdata.debug_id, src_rdata.desc, node->debug_id,
			     (u64)node->ptr);
		binder_node_unlock(node);
	} else {//进入该else
		struct binder_ref_data dest_rdata;

		binder_node_unlock(node);
		//源码分析四已经分析过该函数。如果忘了建议回看,这个函数非常重要。
		ret = binder_inc_ref_for_node(target_proc, node,
				fp->hdr.type == BINDER_TYPE_HANDLE,
				NULL, &dest_rdata);
		if (ret)
			goto done;

		fp->binder = 0;
		fp->handle = dest_rdata.desc;//更新handle值为desc
		fp->cookie = 0;
		trace_binder_transaction_ref_to_ref(t, node, &src_rdata,
						    &dest_rdata);
		binder_debug(BINDER_DEBUG_TRANSACTION,
			     "        ref %d desc %d -> ref %d desc %d (node %d)\n",
			     src_rdata.debug_id, src_rdata.desc,
			     dest_rdata.debug_id, dest_rdata.desc,
			     node->debug_id);
	}
done:
	binder_put_node(node);
	return ret;
}

又见到了binder_inc_ref_for_node,源码分析四中已经分析过它了,这里它的作用就是在客户端binder_proc中创建一个binder_ref指向服务的binder_node,并找一个空闲值(desc)作为该binder_ref在红黑树中的键值。

而后desc又赋给了obj的handle成员,这样当客户端得到obj时,它就得到了binder_ref的键值,也就可以取得binder_node,进而和服务端通信。当客户端用obj的handle值构造BpBinder后,就取得了服务端,这样就实现了服务从sm到客户端的传递。

binder_transaction分段2

	if (reply) {
	//tcomple放进本线程todo队列
		binder_enqueue_thread_work(thread, tcomplete);
		binder_inner_proc_lock(target_proc);
		if (target_thread->is_dead) {
			binder_inner_proc_unlock(target_proc);
			goto err_dead_proc_or_thread;
		}
		BUG_ON(t->buffer->async_transaction != 0);
		//pop目标线程的transaction_stack
		binder_pop_transaction_ilocked(target_thread, in_reply_to);
		//把t->work放进目标线程todo队列
		binder_enqueue_thread_work_ilocked(target_thread, &t->work);
		binder_inner_proc_unlock(target_proc);
		//唤醒目标线程
		wake_up_interruptible_sync(&target_thread->wait);
		binder_restore_priority(current, in_reply_to->saved_priority);
		binder_free_transaction(in_reply_to);

很简单,就是对todo队列和transaction_stack进行更新。

总结

总结一下,服务传递给客户端的过程其实就是在客户端创建引用的过程。创建过程分为两步,第一步是要得到服务的binder_node,该binder_node可以通过sm得到,因为服务向sm注册的时候会在sm留下一个binder_ref引用。第二步是创建一个指向binder_node的binder_ref引用和键值,放进客户binder_proc的红黑树中。键值就是handle值。

上述过程决定了不同进程对同一服务的handle值是不同的。handle值说白了就是binder_ref在红黑树中的键值。每个进程都有不同的红黑树,它们的键值肯定不相同。

到这里,Binder底层实现的主要代码基本上都过完了,下一篇文章将做最后的扫尾工作,介绍BBinder。

<think>好的,我现在需要处理用户关于Android Native的架构与源码解析的问题。用户已经提供了之前的对话历史和相关引用,我需要仔细分析这些信息来构建一个全面且准确的回答。 首先,回顾用户的问题,他们希望了解Android Native的架构和源码,特别是关于Binder、系统服务、Native库以及开发实践中的要点。用户之前的问题涉及到Binder的源码解析,所以需要确保这次回答能够延续之前的深度,并扩展更多Native的内容。 接下来,我需要参考用户提供的引用内容。引用[1]提到了Android Framework的源码解析,可能涉及系统服务的启动流程。引用[3]提到了system/core/init/main.cpp中的SecondStageMain,这应该是init进程的启动阶段,属于Native的关键部分。引用[2]关于NativeCrash捕获的工具,可能在调试部分有用。引用[4]虽然主要讲RN,但提到Native Module Thread,可能涉及Native的线程管理。 根据这些引用,我需要确定Native的核心组件,比如Binder、HAL、系统服务(如Zygote、SurfaceFlinger)、关键库(如libutils、libbinder)以及启动流程。同时,用户可能对源码结构、通信机制和调试方法感兴趣。 需要确保回答结构清晰,分模块解析,并包含源码示例和实际开发中的注意事项。此外,用户之前的回答中提到了Binder的驱动Native和Framework,这次应该进一步深入Native的其他部分,比如系统服务启动、HAL交互等。 可能需要注意以下几点: 1. 系统架构分,明确Native的位置和作用。 2. 关键组件如init进程、Zygote、系统服务的启动流程。 3. Native库的功能和源码结构,比如libbinder、libutils。 4. 与硬件的交互,如HAL。 5. 开发中的常见问题,如JNI的使用、Native Crash的调试。 需要避免重复之前关于Binder的内容,但可以适当链接,保持回答的连贯性。同时,确保引用正确的源码位置,如引用[3]中的init进程的main.cpp,引用[1]中的系统服务启动流程。 在组织内容时,应该分模块,比如架构概述、核心组件、源码解析、开发实践,每个部分下再细分。例如,核心组件可以包括init进程、系统服务、Native库;源码解析可以分析关键代码片段;开发实践则涵盖JNI和调试技巧。 还需要验证引用内容是否准确,比如引用[3]中的SecondStageMain是否确实属于init进程的启动阶段,以及相关代码路径是否正确。另外,引用[1]中的系统服务注册可能需要联系ServiceManager,这在之前的Binder讨论中已经涉及,这里可以简要提及。 最后,生成相关问题时要围绕用户可能感兴趣的扩展点,比如Native的性能优化、HAL的具体实现、系统启动流程的深入分析等。</think>#### Android Native架构与源码深度解析 --- ### 一、Native架构定位 Android Native位于Linux内核与Java Framework之间,通过C/C++实现核心系统服务与硬件抽象。其核心作用包括: - **硬件交互**:通过HAL(Hardware Abstraction Layer)屏蔽硬件差异 - **高性能计算**:图形渲染(如SurfaceFlinger)、音频处理等 - **系统服务支撑**:Binder驱动、Zygote进程、init进程等关键组件 架构分示意图: ```mermaid graph TD A[Java Framework] -->|JNI| B[Native] B -->|系统调用| C[Linux内核] B -->|HAL接口| D[硬件驱动] ``` --- ### 二、核心组件与源码解析 #### 1. **Init进程** - **源码路径**:`system/core/init/main.cpp` - **启动阶段**: ```cpp int main(int argc, char** argv) { if (!strcmp(argv[1], "second_stage")) { return SecondStageMain(argc, argv); // 第二阶段初始化 } } ``` 负责解析`init.rc`文件,启动关键系统服务(如Zygote、ServiceManager)[^3] #### 2. **Zygote进程** - **源码路径**:`frameworks/base/cmds/app_process` - **核心功能**: - 预加载Java类资源(`preload()`方法) - 通过socket监听创建新应用进程 ```cpp void AppRuntime::start(const char* className) { JNIEnv* env; JavaVMAttachArgs args; env->CallStaticVoidMethod(startClass, startMeth, strArray); } ``` #### 3. **系统服务(SystemServer)** - **Native服务示例**: - **SurfaceFlinger**(图形合成):`frameworks/native/services/surfaceflinger` - **AudioFlinger**(音频处理):`frameworks/av/services/audioflinger` - **服务注册流程**: ```cpp // 注册服务到ServiceManager sp<IServiceManager> sm = defaultServiceManager(); sm->addService(String16("surfaceflinger"), new SurfaceFlinger()); ``` #### 4. **Native库(libbinder、libutils)** - **核心类解析**: - **IBinder**:跨进程通信基类,派生`BpBinder`(客户端)和`BBinder`(服务端) - **Parcel**:序列化工具类,实现数据打包 ```cpp // 数据序列化示例 Parcel data; data.writeInt32(100); data.writeString16(String16("android")); ``` --- ### 三、关键机制实现 #### 1. **JNI交互机制** - **JNINativeMethod注册**: ```cpp static JNINativeMethod gMethods[] = { {"nativeMethod", "(I)V", (void*)nativeMethodImpl} }; int register_com_example_MyClass(JNIEnv* env) { return jniRegisterNativeMethods(env, "com/example/MyClass", gMethods, 1); } ``` #### 2. **Binder通信优化** - **共享内存机制**:通过`mmap`分配内核缓冲区,减少数据拷贝次数 - **异步调用**:使用`FLAG_ONEWAY`标记实现非阻塞通信 ```cpp // 异步调用示例 data.writeInt32(IPCThreadState::self()->getCallingPid()); remote()->transact(TRANSACTION_ASYNC_CALL, data, &reply, IBinder::FLAG_ONEWAY); ``` #### 3. **HAL硬件抽象** - **结构体定义**: ```cpp // hardware/libhardware/include/hardware/camera.h struct camera_module { hw_module_t common; int (*get_number_of_cameras)(void); int (*get_camera_info)(int camera_id, struct camera_info *info); }; ``` 厂商通过实现`HAL_MODULE_INFO_SYM`结构体注入驱动代码 --- ### 四、开发实践要点 #### 1. **Native Crash分析** - **调试工具链**: ```bash # 使用addr2line解析堆栈 /path/to/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-addr2line -e app.so 0x1234 ``` 结合`ndk-stack`工具自动解析crash日志[^2] #### 2. **性能优化技巧** - **内存管理**:使用智能指针(`sp<T>`、`wp<T>`)避免泄漏 - **线程优化**:通过`ThreadPool`管理Native线程,避免频繁创建销毁 #### 3. **ANR监控** - **Watchdog机制**:在关键服务中设置心跳检测 ```cpp class Watchdog : public Thread { void run() { while (!exitPending()) { sleep(30); checkSystemState(); // 检测系统状态 } } }; ``` --- ### 五、源码学习路径建议 1. **基础库**:从`libutils`(基本工具类)和`libcutils`(Android特有工具)入手 2. **Binder机制**:重点研究`frameworks/native/libs/binder` 3. **系统服务**:选择SurfaceFlinger或AudioFlinger进行专项研究 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值