一、 IPC经典实现
进程间通信(Inter-process communication, IPC)是指运行在不同进程(不论是否在同一台机器)中的若干线程间的数据交互。Android中有以下几种方式:
- 共享内存(Shared Memory)
由于进程间可以通过直接共享访问同一块内存区域,减少了数据的复制操作,因而速度上的优势比较明显。
实现步骤:创建内存共享区,并将共享区映射到需要共享的各进程空间里,之后便可访问并利用该区域进行信息交互,由于内存共享区没有同步机制,需要参与通信的诸进程自己协商处理。结束后需要撤销内存映射区并删除内存共享区。 - 管道(Pipe)
通信双方分立管道两边,进行数据的传输通信
管道同时具有“读取”端(read end)和“写入”端(write end).
管道是单向的,如果一个进程既要“读”也要“写”,就需要建立两根管道。
管道有容量限制。当Pipe满时写操作将阻塞;反之,读操作将阻塞。 - UNIX Domain Socket(UDS)
UDS是专门针对单机内的进程间通信提出来的,有时也成为IPC Socket. Android中是有最多的一种IPC机制是Binder,其次就是UDS。
Network Socket是以TCP/IP协议栈为基础的,需要分包、重组等一系列操作。而UDS因为是本机内的“安全可靠操作”,实现机制上并不依赖这些协议。UDS的基本流程和Network Socket相似,通信流程图如下:
-
RPC(Remote Procedure Calls)
RPC涉及的通信双方通常运行于两台不同的机器中。完整RPC通信过程如下:
a. 客户端调用Stub接口;
b. Stub根据操作系统的要求进行打包,并执行相应的系统调用;
c. 服务器端Stub解包并调用与数据包匹配的进程;
d. 进程执行操作;
e. 服务器以上述步骤的逆向过程将结果返回给客户端。
二、 同步机制的经典实现
这里所讲的同步,是指多个(包括两个)进程间存在时序关系,需要协同工作以完成一项任务。如果它们并不满足协同的条件,而只是因为共享具有排他性的资源时所产生的关系,则成为互斥。
-
信号量(Semaphore)
信号量与PV原语时使用最为广泛的互斥方法之一,包括以下元素:
a. Semaphore S(信号量), 用于指示共享资源的可用数量.
b. Operation P (有时也称wait()), P操作可减少S的计数.
c. Operation V (有时也称signal()), V操作可增加S的计数.
当某个进程进入共享区时,先要执行P操作,想退出共享区时执行V操作. PV原语都属于原子操作,它们的执行过程不允许被中断.
Dijkstar创建的信号量机制只需要有限的几个元素和简单的操作就能解决同步问题,这也是它广泛流行的一大原因.
- Mutex(Mutual Exclusion)
Mutex即互斥体,通常是对某一排他资源的共享控制 - 要么这个资被占用(unlocked),要么就是可以访问的(unlocked).
只允许取0或1(即locked/unlocked)的Semaphore,叫做Binary Semaphore. 在很多操作系统中,Binary Semaphore和Mutex没有本质差异,前者是特定的Semaphore机制,而后者相较于Semaphore在实现上更为简单. - 管程(Monitor)
由于采用Semaphore机制的程序易读性差,对信号量的管理过于分散.为了使资源的互斥访问更利于维护,提出了"管程" : 可以被多个进程/线程安全访问的对象(object)或模块(module).
它实际上是对Semaphore机制的延伸和改善,是一种控制更为简单的控制手段.
管程中的方法都是受mutual exclusion保护的,意味着在同一时刻只允许有一个访问者使用它们.管程还具备如下属性: 安全性; 互斥性; 共享性.
很多流行编程语言都实现了管程机制 : Java, Delphi, Python, Ruby C#等 - Linux Futex
Futex(Fast Userspace muTEXes) 同步机制,优势就是快.
在Linux2.6.x中称为内核主基线的一部分.Android中的ART虚拟机如果开启了ART_USE_FUTEXES宏,则ART虚拟机中的同步机制会以Futex为基石来实现.
Mutex加锁的基本逻辑是:如果可以获取到锁,则直接返回;否则就进入挂起状态.在挂起等待后,如果用户态获取的状态已经发生了改变,则需要内核自行判断当前的最新状态. 对于不存在竞争的情况下,采用futex机制在用户态就可以完成锁的获取,而不需要通过系统调用进入内核态,从而提高了效率.
三、 Android中的同步机制
Android封装的同步类包括:
- Mutex - 进程间的同步
Android中的Mutex只是对pthread提供的API的简单再封装.即可以处理进程内同步的情况,也可以解决进程间同步的问题.Mutex是用来保证共享资源的互斥使用的.
Mutex只有0和1两种状态,提供了3个重要的接口函数:
status_t lock(); // 获取资源锁
status_t unlock(); // 释放资源锁
status_t tryLock(); // 不论成功与否都会及时返回,而不是等待.
- Condition - 条件判断
核心思想是判断"条件是否已经满足" - 满足的话马上返回,继续执行未完成的动作;否则就进入休眠状态,知道条件满足时有人唤醒它.
它是依赖Mutex来完成的. 和Mutex一样,它支持跨进程共享.主要接口:
/**
* wait方法直接调用了pthread提供的API方法,pthread_cond_wait的
* 逻辑语义如下:
* a. 释放锁mutex
* b. 进入休眠操作
* c. 唤醒后再获取锁
*/
inline status_t Condition::wait(Mutex& mutex) {
return -pthread_cond_wait(&mCond, &mutex.mMutex);
}
/* 也是在某个条件下等待,增加了超时退出功能 */
status_t waitRelative(Mutex& mutex, nesec_t reltime);
void signal(); // 条件满足时通知相应等待者
void broadcast(); // 条件满足时通知所有等待者
- Barrier - "栅栏,障碍"
Barrier是对Condition的一个应用,是填充了"具体条件"的Condition. Barrier类是专门为SurfaceFlinger设计的,而Mutex和Condition是作为常用的Utility提供给整个Android系统使用的.
/* frameworks/native/services/surfaceflinger/Barrier.h */
class Barrier {
public:
inline Barrier() : state(CLOSED) {}
inline ~Barrier() {}
void open() {
Mutex::Autolock _l(lock);
state = OPEND;
cv.broadcast();
}
void close() {
Mutex::Autolock _l(lock);
state = CLOSED;
cv.broadcast();
}
void wait() const {
Mutex::Autolock _l(lock);
while( sate == CLOSED) {
cv.wait(lock);
}
}
private:
enum {OPENED, CLOSED}
mutable Mutex lock;
mutable Condition cv;
volatile int state;
}
以上满足的场景类似于,当汽车通过前,必须要先确认栅栏是开启的,于是调用wait().如果条件不满足只能停下来等待,直到管理员调用open()将栅栏打开.
加解锁的自动化操作 - Autolock
在Mutex内部还有一个Autolock嵌套类,字面意思应该是为了实现加,解锁的自动化操作.
当Autolock构造时,会主动调用内部成员变量mLock的lock()方法来获取一个锁. 而析构时的情况正好相反,调用它的unlock()方法释放锁. 这样假如一个Autolock对象是局部变量的话,那他的生命周期结束时就会自动把资源锁解除.读写锁 - ReaderWriterMutex
ReaderWriterMutex是Art虚拟机一种特殊的Mutex.
Exclusive和Shared分别代表Write和Read权限,说明这个锁是允许多个对象共享Read锁,但同时只允许有唯一一个对象拥有Write锁.ReaderWriterMutex有三种状态:Free(还没被任何对象所持有的情况) /Exclusive/Shared.ReaderWriterMutex状态表如下:
State | ExclusiveLock | ExclusiveUnlock | SharedLock | SharedUnlock |
---|---|---|---|---|
Free | Exclusive | error | SharedLock(1) | error |
Exclusive | Block | Free | Block | error |
Free(n) | Block | error | SharedLock(n+1) | SharedLock(n-1) |
注:主要内容摘录自书籍 深入理解Android内核设计思想,林学森 著