binder和socket区别及原理

1. 疑问

  1. binder的数据拷贝到底是1次还是2次?
  2. 跨进程通信中,为什么binder对于数据的拷贝次数少于socket?
  3. binder和socket数据拷贝是具体怎么操作的呢?
  4. 什么时候用binder,什么时候用socket?

2. binder原理

    对于进程A和进程B通过binder通信,进程A通过binder传递数据给进程B。那么进程A操作的动作为write数据,进程B操作的动作为read数据。

    在Android AIDL中,这个流程可以理解为 进程A为客户端,进程B为服务端。对于进程B,在起来后,就会为目标方法在内核空间中分配对应的mmap物理地址,以及用户空间里的虚拟映射地址。这个只能执行读数据的操作,没有写数据的权限。

    进程A 通过上层接口在用户空间生成目标数据,记为data。接着通过binder接口传入数据data。此时程序会将包含data的事务由用户空间拷贝到内核空间的进程B的物理地址里。     这里有一点需要理解的就是,上文说到了事务,它是对数据的一个封装,内容包含了数据,目标进程的标识,元数据。这也是binder和socket的一个区别。     事务被拷贝到内核空间后,binder驱动会分析事务里的目标进程标识,将事务的地址放到进程B的处理队列中。进程B从队列里读取到这个事件后,会进行read操作,通过进程B的虚拟内存地址直接从内核空间里读取数据。

    到这里,一次binder流程走完了。但是我们一般在AIDL中使用binder,AIDL是通过方法和进程B交互的,方法除了入参还有返回值,所以这里实际使用中还需要等待进程B回复结果

    举个例子:我们使用AIDL时候,比如进程A 存在方法getVersion(int curTime) 这个方法从进程B获取版本,那么方法是同步的,且A 有write数据给进程B,进程B还有返回数据给到A,这个方法才算执行结束。

    所以对于进程A,数据write到进程B的mmap物理内存空间后,自己也会根据自己是否需要接收B返回的数据,来开辟进程A的mmap地址,从而接收结果。

整个流程如图:

    从流程图里可以看到一次Android AIDL通信有2次binder交互,一次binder交互只会存在一次数据拷贝。

3. binder原理分析中想到的几种异常场景

3.1 如果多个进程同时和B进行通信,流程如何?

    对于进程B,作为服务端的存在,他创建时候,分配的binder物理内存存在多个槽,每个write的进程write进去的数据都是独立的,如果发现内存不够,那么write的进程就会阻塞,被放入到TODO队列中,直到内存释放才会进行write。

3.2 如果进程A write时候,目标进程B未存在,怎么办
  1. 如果在进程A访问进程B时候,发现进程B不存在,那么在A write之前,binder驱动就会告知进程A。我们使用AIDL时候,对应的代码就是获取不到目标Service对象,或者获取到的对象是Null。
  2. 如果在进程A write之后,进程B处理数据时候忽然挂了,那么此时binder驱动也会检测到,会通知进程A,抛出一个DeadObjectException异常。
3.3 如果进程A write之后,进程B正在处理数据中,进程A挂了

    进程B会正常进行,进程B write之后,binder驱动会清除进程A的记录。

4. socket原理

Socket分为跨进程Socket和网络通信Socket,两者在接口调用上没差别,但是在底层流程上有一些区别。

4.1 跨进程Socket

    对于Socket跨进程通信,目前也分为两种,Linux专门针对Socket跨进程通信优化出一种新的Socket交互方式,Unix Domain Socket。Android 的类 LocalSocket对其进行了封装。

    我们先分析传统的基于tcp/ip协议的Socket跨进程通信

4.1.1 tcp/ip socket IPC

    对于传统的tcp/ip socket本地跨进程通信:

  1. 进程A调用send方法发送数据(记为Data)后,内核会先将数据从进程A的用户空间拷贝到内核空间里的该Socket的数据发送缓存区排队等待,然后内核的TCP协议栈会依次从数据发送缓冲区里取数据封装成协议包。
  2. Data被封装为协议包后,IP协议栈会解析其目标地址,发现其目标地址是localhost(127.0.0.1),接着会将其发送给lo接口的输入队列中等待处理。
  3. lo接口从队列中读取到数据后,会将数据回环发送给TCP协议栈等待解包
  4. TCP协议栈拿到数据进行解包处理后,获取到Data,放到进程B的Socket的接收缓冲区里。
  5. 进程B的接收缓冲区收到数据后,内核会唤醒进程B,让其进行读取数据操作。将数据Data从内核空间拷贝到进程B的用户空间,至此,一次单向的Socket通信结束。

所以从图中可以看到,Socket比较binder,在内核中多了一次数据拷贝,即从发送缓冲区到接收缓冲区的拷贝。另外需要注意的是,接收端读取数据时候,还要从内核拷贝数据到用户空间,这里又多了一次拷贝。综合来说,传统的Socket IPC本地通信,一共存在3次数据拷贝。

4.1.2 Unix Domain Socket

    它是一种可以使用Socket进行本地高效率通信。

  1. 进程A和进程B使用Unix Domain Socket 通信。
  2. 进程A 写数据,CPU会将数据从进程A的用户空间拷贝到内核中的Socket发送队列中
  3. CPU 分析数据接收方,将数据在队列中的地址指针发送给进程B
  4. 进程B被唤醒,主动读取数据,将数据从内核中的发送队列里拷贝到进程B的用户空间

从流程里可以看到 Unix Domain Socket对于数据的拷贝,在内核中由于不存在两个进程之间的数据拷贝,所以总拷贝次数会少一次。

  1. 对比
  • binder
    • 数据拷贝 1次
    • 在Android中使用,在AIDL中使用时候,一次接口调用实际走两个binder,所以一次接口调用数据拷贝经历2次
    • 一般没有特殊需求,我们使用AIDL来进行跨进程通信,舒适度和代码整洁度最佳。
  • 传统Socket
    • 数据拷贝3次
    • 一般常用于网络通信。如果即可跨进程又可以走网络通信,使用传统Socket适配度最好。
  • Unix Domain Socket
    • 数据拷贝2次
    • 一般如果是系统服务和APP服务跨好多层进行交互,使用AIDL实现复现的情况下,可以使用Socket,减少设计复杂度。
相关推荐
千里马学框架4 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone4 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc4 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo4 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077004 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼4 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone4 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen4 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone4 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui