Android历程间通讯

源代码 2024-10-2 22:02:30 115 0 来自 中国
Linux历程间通讯

底子概念

内核态/用户态

1.png 如上图所示,从宏观上来看,Linux使用体系的体系架构分为用户态和内核态(大概用户空间和内核空间)。使用体系的资源是有限的,如果访问资源的使用过多,一定会斲丧过多的资源,而且如果不对这些使用加以区分,很大概造成资源访问的冲突。以是,为了镌汰有限资源的访问和使用冲突,Unix/Linux的操持哲学之一就是:对差别的使用赋予差别的执行等级,就是所谓特权的概念。简单说就是有多大本领做多大的事,与体系干系的一些特殊关键的使用必须由最高特权的程序来完成。Intel的X86架构的CPU提供了0到3四个特权级,数字越小,特权越高,Linux使用体系中重要接纳了0和3两个特权级,分别对应的就是内核态和用户态。当一个使命(历程)执行体系调用而陷入内核代码中执行时,称历程处于内核运行态(内核态),此时处理器处于特权级最高的(0级)内核代码中执行。历程在执行用户自己的代码的时候,我们称其处于用户运行态(用户态),此时处理器在特权级最低的(3级)用户代码中运行。
历程空间

[图片上传失败...(image-a8d6b8-1649494164150)]
如上是32为使用体系的历程空间分别,它的寻址空间(假造存储空间)就是 2 的 32 次方,也就是 4GB。使用体系的核心是内核,独立于平凡的应用程序,可以访问受掩护的内存空间,也可以访问底层硬件设备的权限。为了掩护用户历程不能直接使用内核,包管内核的安全,使用体系从逻辑上将假造空间分别为用户空间(User Space)和内核空间(Kernel Space)。针对 Linux 使用体系而言,将最高的 1GB 字节供内核使用,称为内核空间;较低的 3GB 字节供各历程使用,称为用户空间。
体系调用

固然从逻辑上举行了用户空间和内核空间的分别,但不可避免的用户空间必要访问内核资源,比如文件使用、访问网络等等。为了突破隔离限定,就必要借助体系调用来实现。体系调用是用户空间访问内核空间的唯一方式,包管了所有的资源访问都是在内核的控制下举行的,避免了用户程序对体系资源的越权访问,提升了体系安全性和稳固性。
历程隔离

简单的说就是使用体系中,历程与历程间内存是不共享的。两个历程就像两个平行的天下,A 历程没法直接访问 B 历程的数据,这就是历程隔离的平凡表明。A 历程和 B 历程之间要举行数据交互就得接纳特殊的通讯机制:历程间通讯(IPC)。
历程间通讯

根本原理

通常的做法是消息发送方将要发送的数据存放在内存缓存区中,通过体系调用进入内核态。然后内核程序在内核空间分配内存,开发一块内核缓存区,调用 copy_from_user() 函数将数据从用户空间的内存缓存区拷贝到内核空间的内核缓存区中。同样的,吸收方历程在吸收数据时在自己的用户空间开发一块内存缓存区,然后内核程序调用 copy_to_user() 函数将数据从内核缓存区拷贝到吸收历程的内存缓存区。如许数据发送方历程和数据吸收方历程就完成了一次数据传输,我们称完成了一次历程间通讯。如下图:
分类

管道

管道分为著名管道和无名管道。
无名管道是一种半双工的通讯方式,数据只能单向活动,而且只能在具有亲缘关系的历程间使用.历程的亲缘关系一样寻常指的是父子关系。无明管道一样寻常用于两个差别历程之间的通讯。当一个历程创建了一个管道,并调用fork创建自己的一个子历程后,父历程关闭读管道端,子历程关闭写管道端,如许提供了两个历程之间数据活动的一种方式。
著名管道也是一种半双工的通讯方式,但是它允许无亲缘关系历程间的通讯。
信号

信号是一种比力复杂的通讯方式,用于关照吸收历程某个事故已经发生。
信号量

信号量是一个计数器,可以用来控制多个线程对共享资源的访问,它不是用于交换大批数据,而用于多线程之间的同步。它常作为一种锁机制,防止某历程在访问资源时别的历程也访问该资源。因此,重要作为历程间以及同一个历程内差别线程之间的同步本领。
消息队列

消息队列是消息的链表,存放在内核中并由消息队列标识符标识.消息队列降服了信号通报信息少,管道只能承载无格式字节省以及缓冲区巨细受限等特点.消息队列是UNIX下差别历程之间可实现共享资源的一种机制,UNIX允许差别历程将格式化的数据流以消息队列形式发送给恣意历程.对消息队列具有使用权限的历程都可以使用msget完成对消息队列的使用控制.通过使用消息范例,历程可以按任何次序读信息,或为消息安排优先级次序。
共享内存

共享内存就是映射一段能被其他历程所访问的内存,这段共享内存由一个历程创建,但多个历程都可以访问.共享内存是最快的IPC(历程间通讯)方式,它是针对别的历程间通讯方式运行服从低而专门操持的.它每每与其他通讯机制,如信号量,共同使用,来实现历程间的同步与通讯。
套接字

socket,即套接字是一种通讯机制,依附这种机制,客户/服务器(即要举行通讯的历程)体系的开发工作既可以在本地单机上举行,也可以跨网络举行。也就是说它可以让不在同一台盘算机但通过网络毗连盘算机上的历程举行通讯。也由于如许,套接字明确地将客户端和服务器区分开来。
Binder

明确了 Linux IPC 干系概念和通讯原理,接下来我们来了解下Binder IPC的原理。
Binder IPC 原理

Binder 驱动

跨历程通讯是必要内核空间做支持的。传统的 IPC 机制如管道、Socket 都是内核的一部门,因此通过内核支持来实现历程间通讯自然是没问题的。但是 Binder 并不是 Linux 体系内核的一部门,那怎么办呢?这就得益于 Linux 的 动态内核可加载模块(Loadable Kernel Module,LKM)的机制;模块是具有独立功能的程序,它可以被单独编译,但是不能独立运行。它在运行时被链接到内核作为内核的一部门运行。如许,Android 体系就可以通过动态添加一个内核模块运行在内核空间,用户历程之间通过这个内核模块作为桥梁来实现通讯。在 Android 体系中,这个运行在内核空间,负责各个用户历程通过 Binder 实现通讯的内核模块就叫 Binder 驱动(Binder Dirver)
内存映射

Binder IPC 机制中涉及到的内存映射通过 mmap() 来实现,mmap() 是使用体系中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存地域映射到内核空间。映射关系创建后,用户对这块内存地域的修改可以直接反应到内核空间;反之内核空间对这段地域的修改也能直接反应到用户空间。
内存映射能镌汰数据拷贝次数,实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反映在映射的内存地域,从而被对方空间实时感知。也正由于云云,内存映射能够提供对历程间通讯的支持。
实现原理

一次完备的 Binder IPC 通讯过程通常是如许:
1、起首 Binder 驱动在内核空间创建一个 数据吸收缓存区 ;
2、接着在内核空间开发一块内核缓存区,创建 内核缓存区 和 内核中数据吸收缓存区 之间的映射关系,以及 内核中数据吸收缓存区 和 吸收历程用户空间地点 的映射关系;
3、发送方历程通过体系调用 copyfromuser() 将数据 copy 到内核中的内核缓存区,由于内核缓存区和吸收历程的用户空间存在内存映射,因此也就相当于把数据发送到了吸收历程的用户空间,如许便完成了一次历程间的通讯。
3.png 与传统IPC的比力

性能
Binder相比传统IPC,通过内存映射镌汰了数据拷贝次数,从而提供了传输的性能。
IPC方式拷贝次数共享内存0Binder1管道/消息队列/信号量/Socket2稳固性
Binder 基于 C/S 架构,客户端(Client)有什么需求就丢给服务端(Server)去完成,架构清晰、职责明确又相互独立,自然稳固性更好。共享内存固然无需拷贝,但是控制负责,难以使用。从稳固性的角度讲,Binder 机制是优于内存共享的。
安全性
Binder通过UID/PID来包管通讯双方的正当性,进而到达IPC间消息传输的安全性。Binder对每个历程都分配了UID/PID,当通过Binder举行IPC时,体系会去查对UID/PID是否正当,只要体系认为正当的PID,才气完成Binder的IPC调用。云云,在IPC上增长一道关卡,保障IPC通讯安全,以是相对其他IPC方式更具安全性。
Binder通讯模子

Client, Server, ServiceManager 都是通过体系调用 open, mmap,和ioctl 来访问设备文件 /dev/binder, 从而实现与 Binder 驱动的交互来间接的实现跨历程通讯.
Binder 驱动
Binder 驱动就如同路由器一样,是整个通讯的核心;驱动负责历程之间 Binder 通讯的创建,Binder 在历程之间的通报,Binder 引用计数管理,数据包在历程之间的通报和交互等一系列底层支持。
ServiceManager
ServiceManager 和 DNS 类似,作用是将字符形式的 Binder 名字转化成 Client 中对该 Binder 的引用,使得 Client 能够通过 Binder 的名字得到对 Binder 实体的引用。注册了名字的 Binder 叫实名 Binder,就像网站一样除了除了有 IP 地点不测尚有自己的网址。Server 创建了 Binder,并为它起一个字符形式,可读易记得名字,将这个 Binder 实体连同名字一起以数据包的形式通过 Binder 驱动发送给 ServiceManager ,关照 ServiceManager 注册一个名为“张三”的 Binder,它位于某个 Server 中。驱动为这个穿越历程界限的 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager。ServiceManger 收到数据后从中取着名字和引用填入查找表。
ServierManager 是一个历程,Server 是另一个历程,Server 向 ServiceManager 中注册 Binder 一定涉及到历程间通讯。ServiceManager 和其他历程同样接纳 Bidner 通讯,ServiceManager 是 Server 端,有自己的 Binder 实体,其他历程都是 Client,必要通过这个 Binder 的引用来实现 Binder 的注册,查询和获取。ServiceManager 提供的 Binder 比力特殊,它没著名字也不必要注册。当一个历程使用 BINDER_SET_CONTEXT_MGR 下令将自己注册成 ServiceManager 时 Binder 驱动会主动为它创建 Binder 实体。其次这个 Binder 实体的引用在所有 Client 中都固定为 0 而无需通过别的本领得到。也就是说,一个 Server 想要向 ServiceManager 注册自己的 Binder 就必须通过这个 0 号引用和 ServiceManager 的 Binder 通讯。类比互联网,0 号引用就比如是域名服务器的地点,你必须预先动态大概手工配置好。要留意的是,这里说的 Client 是相对于 ServiceManager 而言的,一个历程大概应用程序大概是提供服务的Server,但对于 ServiceManager 来说它仍然是个 Client。
Client  Server 向 ServiceManager 中注册了 Binder 以后, Client 就能通过名字得到 Binder 的引用了。Client 也使用保存的 0 号引用向 ServiceManager 哀求访问某个 Binder: 我申请访问名字叫张三的 Binder 引用。ServiceManager 收到这个哀求后从哀求数据包中取出 Binder 名称,在查找表里找到对应的条目,取出对应的 Binder 引用作为复兴发送给发起哀求的 Client。从面向对象的角度看,Server 中的 Binder 实体现在有两个引用:一个位于 ServiceManager 中,一个位于发起哀求的 Client 中。如果接下来有更多的 Client 哀求该 Binder,体系中就会有更多的引用指向该 Binder ,就像 Java 中一个对象有多个引用一样。
Binder通讯过程

5.png Binder应用

binder在Android的应用非常多,比如Activity的启动、Broadcast的发送、Service的绑定等,而且常用的Messager、AIDL、ContentProvider都是使用Binder来实现的。
参考

https://www.cnblogs.com/bakari/p/5520860.html
https://blog.csdn.net/ypbsyy/article/details/79915117
https://www.linuxprobe.com/linux-process-method.html
https://www.jianshu.com/p/38c75edb4301
https://www.jianshu.com/p/41d3bbecff2c
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 16:47, Processed in 0.317407 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表