《Android内核设计思想》(一)进程间的通信

进程间通信(Inter-process communication, IPC)

   操作系统的进程通常拥有独立的内存空间,并通过严格的机制防止进程间的非法访问。进程间的信息交流就需要进程间通讯实现信息互通。

共享内存

   共享内存是常用的进程间通信机制,其实现方法是两个进程直接共享访问一块内存区域。一般情况下,实现共享内存的步骤如下:
1.创建内存共享区,进程A通过操作系统提供的API从内存中申请一块共享区域。
2.将内存共享区映射到进程A的空间中
3.进程B将内存映射到自己的空间中
4.共享内存的各个进程实现内存映射后通过该内存区域进行信息交换
5.完成进程间通信后,各进程撤销之前的映射操作,然后删除共享内存区域。

管道

   管道也是操作系统的常见的进程间通信方式,适用于所有POSIX系统及Windows系列产品。管道形象的形容了进程间的行为,进程A和B分立管道的两边,进行数据的传输通信。管道是单向的,意味着一个进程完成读写两个操作的时候需要建立两根管道。管道有着容量限制,当pipe满时写操作将被阻塞;当pipe空时读操作将被阻塞。
   管道常用于父子进程这种特殊的进程关系,如果两个进程没有任何关系,则无法通过管道是实现信息通信。这也是命名管道(Named Pipe)得以发展的原因。命名管道是服务器进程和一个或多个客户进程之间通信的单向或双向管道。不同于匿名管道的是:命名管道可以在不相关的进程之间和不同计算机之间使用,服务器建立命名管道时给它指定一个名字,任何进程都可以通过该名字打开管道的另一端,根据给定的权限和服务器进程通信。

2Unix Domain Socket(UDC)

   基于TCP/IP协议的Socket广泛应用于通信领域,也被称为Network Socket。UNIX Domain Socket是针对单机内的进程间通信提出来的,有时也被称为IPC Socket。UDS的基本流程与传统Socket一致,以下提供了一个UDS的范例,其功能如下:

  • 服务器端监听IPC请求
  • 客户端发起IPC请求
  • 双方成功建立IPC连接
  • 客户端向服务器端发送数据,证明IPC通信是有效的
    建立UDC的过程相对繁琐,通过soketpair()函数可以大大地简化通信双方地工作。

Remote Procedure Calls

   RPC涉及的通信双方通常运行于两台不同的机器中,一个完整的RPC通信需要以下几个步骤:
1.客户端进程调用Stub接口
2.Stub根据操作系统的要求进行打包,并执行相应的系统调用
3.由内核完成与服务器端的具体交互,将客户端的数据包发送给服务器端的内核
4.服务器端Stub解包并调用与数据包相匹配的进程
5.进程进行操作
6.服务器以上述步骤的逆向过程将结果返回给客户端
   

同步机制

   操作系统支持多线程的并发执行,则它们之间难免出现相互制约的情况。例如两个进程需要共享唯一的硬件设备,或者一个进程的工作依赖于另一方对共享资源的执行结果。同步机制不但是操作系统的实现重点,在应用程序的设计也具有举足轻重的作用。
   如果多个进程间存在时序关系,需要协同工作以完成一项内务,称为同步。如果并不满足协同的条件,而只是应为共享具有排他性的资源时所产生的关系,则称为互斥。

信号量

   信号量是最为广泛的互斥方法之一,它包括以下几个元素:Semaphore S 信号量、Operation P(wait函数), Operation V(signal函数)。信号量S用于指示共享资源的可用数量,P原语可以减少S计数,V则增加它的计数。

Mutex

   
   Mutex是Mutual Exclusion的缩写,其释义为互斥体。Mutex通常是对某一排他资源的共享控制——要么这个资源被占用(locked),要么资源可以被访问(unlocked)。Mutex和Binary Semaphore机制没有本质差异。

管程(Monitor)

   管程是可以被多个进程/线程安全访问的对象(object)或模块(module)。Semaphore机制的程序可读性相对较差,对于信号量的管理也分散在各个参与对象中,因此可能引发死锁或进程饿死等问题。为了使资源的互斥访问更利于维护,科学家们提出了管程的概念。管程中的方法都是受Mutual Exclusion保护,同一时刻只允许有一个访问者使用它们,此外,管程还具有如下属性:安全性、互斥性、共享性。

Linux Futex

   Futex(Fast Userspace muTEXes)是由Hubertus Franke等人发明的同步机制,其核心又是即为“fast”,主要体现在它在应用程序空间中就可以应对大多数的同步场景,从而减少系统调用和上下文切换的时间。Futex在Android中的一个重要应用场景是ART虚拟机。对于不存在竞争的场景下,futex机制可以在用户态完成锁的获取。

   

Android的同步机制

   无论是什么操作系统,其同步机制的技术本质都是类似的。目前Android封装的同步类包括: Mutex,Condition和Barrier。Mutex的头文件是frameworks/native/include/utils/Mutex.h,Android中的Mutex只是对pthread提供的api的简单再封装,所以函数声明和实现体都放在同一个头文件中,此外Mutex还包含一个Autolock的嵌套类,它是利用变量生命周期特点而设计的一个辅助类。Condition是“条件变量”再Android中的实现类,其头文件是framework/native/include/utils/Condition.h。Barrier是同时基于Mutex和Condition实现的一个模型,其头文件是framework/native/include/utils/Barrier.h。

Condition

   Condition的核心思想是判断“条件是否已经满足”,满足时马上返回,继续执行未完成的动作,否则进入休眠等待,指导条件满足时有人唤醒。

Barrier

   Barrier表示“栅栏、障碍”,即Barrier是填充了具体条件的Condition。Barrier类是专门为SurfaceFlinger设计的,并不是像Mutex和Condition是作为常用的Utility提供给整个Android系统使用。Barrier总共提供了3个接口函数: wait(),open()和close()。“条件”是代码中的state==OPENED,另一个状态是CLOSED,这有嗲类似于汽车栅栏的开启和关闭。当汽车通过前,必须先确认栅栏是开启的,于是调用wait(),当条件不满足时,汽车需要停下来等待,函数首先获得一个Mutex锁,然后调用Condition对象,这是因为state是互斥资源。

class Barrier{
public:
   inline Barrier(): state(CLOSED) {}
   inline ~Barrier() {} 
   void open() {
           Mutex::Autolock _l(lock);
           state = OPENED;
           cv.broadcast();
    }
    void close() {
            Mutex::Autolock _l(lock);
            state = CLOSED; 
     }
     void wait() const {
             Mutex::Autolock _l(lock);
             while(state == CLOSED) {
                     cv.wait(lock);
             }
     }
private:
      enum {OPENED, CLOSED};
       mutable Mutex lock;
       mutable Conditon cv;
       volatile int state;       
};

Autolock

   在Mutex类的内部有一个Autolock的嵌套类,它是为了实现加、解锁的自动化操作,这个类的构造和析构函数如下:

class Autolock{
public:
      inline Autolock(Mutex& mutex) : mLock(mutex) {mLock.lock(); }
      inline Autolock(Mutex& mutex) : mLock(mutex) {mLock.lock(); }
      inline ~Autolock() {mLock.unlock(); }
private:
      Mutex& mLock;
};

   当Autolock构造时会自动调用成员变量mLock的lock()方法来获得锁,析构时调用unlock()方法释放锁,这样当Autolock对象的生命周期结束时就会自动把资源锁解。

Reader WriterMutex

   Android Art虚拟机中用到互斥和锁操作的地方非常多,为此它实现了一整套自己的mutex机制,ReaderWriterMutex是其中一种特殊的mutex。与普通的mutex相比,它主要提供了如下以下差异接口:

void ExclusiveLock(Thread* self) ACQUIRE();
void ExclusiveUnlock(Thread* self) RELEASE();
bool ExclusiveLockWithTimeout(Thread* self, int64_t ms, int32_t ns)
         EXCLUSIVE_TRYLOCK_FUNCTION(true);
void SharedLock(Thread* self) ACQUIRE_SHARED() ALWAYS_INLINE;
void SharedUnlock(Thread* self) RELEASE_SHARED() ALWAYS_INLINE;

Exclusive和Shared分别代表Write和Read权限,读写锁允许多个对象共享Read锁而只能有一个对象拥有Write锁。此外,读写锁还有Free、Exclusive和Shared三种状态。

文章内容来自——林学森,深入理解Android内核设计思想,北京:人民邮电出版社,2017.7

最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容