Android進(jìn)程間通信之1——Linux基礎(chǔ)

由于Android系統(tǒng)是基于Linux系統(tǒng)的,所以有必要簡單的介紹下Linux的跨進(jìn)程通信,對大家后續(xù)了解Android的跨進(jìn)程通信是有幫助的。

一、Linux介紹

(一)Linux的全局圖介紹

Linux kernel map.png

這是makeLinux網(wǎng)站提供的一幅非常經(jīng)典的Linux內(nèi)核圖,涵蓋了內(nèi)核最為核心的方法,Linux除了驅(qū)動開發(fā)外,還有很多通用子系統(tǒng),比如CPU,memory,file system等核心模塊,即便不做底層驅(qū)動開發(fā),掌握這些模塊對于加深理解整個系統(tǒng)運(yùn)轉(zhuǎn)還是很有幫助的。

(二)Linux的源碼目錄結(jié)構(gòu)

二、內(nèi)核態(tài)與用戶態(tài)

1、內(nèi)核態(tài)和用戶態(tài)的簡介

  • ** 內(nèi)核態(tài) **:CPU可以訪問內(nèi)存所有數(shù)據(jù),包括外圍設(shè)備,例如硬盤、網(wǎng)卡,CPU可以將自己從一個程序切換到另外一個程序。
  • ** 用戶態(tài) **: 只能受限的訪問內(nèi)存,且不允許訪問外圍設(shè)備,占用CPU的能力被剝削,CPU資源可以被其他程序獲取。

2、為什么要有用戶態(tài)和內(nèi)核態(tài)

由于需要限制不同的程序之間的訪問能力,防止他們獲取別的程序的內(nèi)存數(shù)據(jù),或者獲取外圍設(shè)備的數(shù)據(jù),并發(fā)送網(wǎng)絡(luò),CPU劃分出兩個權(quán)限等級 ----用戶態(tài) 和 內(nèi)核態(tài)。

3、用戶態(tài)與內(nèi)核態(tài)的切換

3.1 切換簡介

所有用戶程序都是運(yùn)行在用戶態(tài)的,但是有時(shí)候程序確實(shí)需要做一些內(nèi)核態(tài)的事情,例如從硬盤讀取數(shù)據(jù),或者從鍵盤獲取輸入等。而唯一可以做這些事情的就是 操作系統(tǒng) ,所以這時(shí)候 程序 就需要先向 操作系統(tǒng) 請求,以 程序 的名字來執(zhí)行這些操作。這時(shí)候就需要一個這樣的機(jī)制:用戶態(tài) 切換到 內(nèi)核態(tài),但是不能控制內(nèi)核態(tài)中執(zhí)行的這種機(jī)制叫做** 系統(tǒng)調(diào)用 **,在CPU中的實(shí)現(xiàn)稱之為 "陷阱指令(Trap Instruction)"

3.2 系統(tǒng)調(diào)用機(jī)制流程:

  • 1、用戶態(tài)程序?qū)⒁恍?shù)據(jù)值放在寄存器中,或者使用參數(shù)創(chuàng)建一個堆棧(stack frame),以表明需要操作系統(tǒng)提供的服務(wù)。
  • 2、用戶態(tài)程序執(zhí)行陷阱指令
  • 3、CUP切換到內(nèi)核態(tài),并跳到內(nèi)存指定位置的指令,這些指令是操作系統(tǒng)的一部分,他們具有內(nèi)存保護(hù),不可被用戶態(tài)程序訪問
  • 4、這些指令稱之為 陷阱 (trap) 或者新系統(tǒng)調(diào)用處理器 ( system call hanlder )。他們會讀取程序放入內(nèi)存的數(shù)據(jù)參數(shù),并執(zhí)行程序請求的服務(wù)。
  • 5、系統(tǒng)調(diào)用完成后,操作系統(tǒng)會重置CPU為用戶態(tài)并返回系統(tǒng)調(diào)用的結(jié)果。

三、紅黑樹

紅黑樹是60年代中期計(jì)算機(jī)科學(xué)界尋找一種算法復(fù)雜度穩(wěn)定,容易實(shí)現(xiàn)的數(shù)據(jù)存儲算法的產(chǎn)物。紅黑樹是常用的一種數(shù)據(jù)結(jié)構(gòu),它使得對數(shù)據(jù)的索引,插入和刪除操作都能保持在O(lgn)的時(shí)間復(fù)雜度。在優(yōu)先級隊(duì)列、字典等實(shí)用領(lǐng)域都有廣泛地應(yīng)用,更是70年代提出的關(guān)系數(shù)據(jù)庫模型——B樹的鼻祖。當(dāng)然,相比于一般的數(shù)據(jù)結(jié)構(gòu),紅黑樹實(shí)現(xiàn)的難度有所增加。關(guān)鍵是后面要講解的Binder驅(qū)動里面用到了紅黑樹

(一)二叉搜索樹

在具體實(shí)現(xiàn)紅黑樹之前, 必須弄清楚它的基本含義。紅黑樹本質(zhì)上是一顆二叉搜索樹,它滿足二叉搜索樹的基本性質(zhì)——即樹中的任何節(jié)點(diǎn)的值大于它的左子節(jié)點(diǎn),且小于它的右子節(jié)點(diǎn)。

二叉搜索樹

按照二叉搜索樹組織數(shù)據(jù),使得對元素的查找非??旖荨1热缟蠄D的中的二叉搜索樹,如果查詢值為48的節(jié)點(diǎn),只需要遍歷4個節(jié)點(diǎn)即可完成。理論上,一顆平衡的二叉樹搜索樹的任意節(jié)點(diǎn)平均查找效率為樹的高度h,即O(lgn)。但是如果二叉搜索樹的失去平衡(元素在一側(cè)),搜索效率就退化為O(n),因此二叉搜索樹的平衡是搜索效率的關(guān)鍵所在。為了維護(hù)樹的平衡性,數(shù)據(jù)結(jié)構(gòu)內(nèi)出現(xiàn)了各種各樣的樹,比如AVL樹通過維持任何節(jié)點(diǎn)的左右子樹的高度差 不大于1保持樹的平衡,而紅黑樹使用顏色維持樹的平衡,使二叉搜索樹的左右子樹的高度差 保持在固定的范圍。相比于其他二叉搜索樹,紅黑樹對二叉搜索樹的平衡性維持著自身的優(yōu)勢

(二) 紅黑樹

紅黑樹,顧名思義,紅黑樹的節(jié)點(diǎn)是有顏色概念的,即非紅即黑,通過顏色的語速,紅黑樹為支持著二叉搜索樹的平衡性。一個紅黑樹必須有下面5個特征

  • 1、節(jié)點(diǎn)是紅色或黑色
  • 2、根是黑色
  • 3、所有葉子是黑色(葉子是NIL節(jié)點(diǎn))
  • 4、每個紅色節(jié)點(diǎn)的兩個子節(jié)點(diǎn)都是黑色的(從每個葉子到跟的所有路徑不能有兩個連續(xù)的紅色節(jié)點(diǎn))
  • 5、從任一節(jié)點(diǎn)到其每個葉子的所有簡單路徑都包含相同數(shù)目的黑色節(jié)點(diǎn)。

如下圖


紅黑樹圖1.png

這些特征強(qiáng)制約束了紅黑樹的關(guān)鍵性質(zhì):從跟到葉子的最長可能路徑不多于最短可能路徑的兩倍長(特征4保證了路徑最長的情況為1紅1黑,最短的情況為全黑,再結(jié)合特征5,可以推導(dǎo)出)。結(jié)果是這個樹大致上是平衡的。因?yàn)楸热绮迦搿h除和查找操作中,操作某個值的最壞情況的時(shí)間都要求與樹的高度成比例,這個高度上的理論上限允許紅黑樹在最壞的情況都是高效的,而不同于普通的(二叉搜索樹)

(三) 數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)

  • 和一般的數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)類似,我們用抽象數(shù)據(jù)類型表示紅黑樹的節(jié)點(diǎn),使用指針保存節(jié)點(diǎn)之間的相互關(guān)系。
  • 作為紅黑樹的節(jié)點(diǎn),其基本屬性有:節(jié)點(diǎn)的顏色,左節(jié)點(diǎn)的指針,右節(jié)點(diǎn)的指針、父節(jié)點(diǎn)的指針、節(jié)點(diǎn)的值

如下圖


紅河書節(jié)點(diǎn).png

為了方便紅黑樹關(guān)鍵算法的實(shí)現(xiàn),還定義了一些簡單的操作(都是內(nèi)聯(lián)函數(shù))。

//紅黑樹節(jié)點(diǎn)
template<class T>
class rb_tree_node
{
    typedef rb_tree_node_color node_color;
    typedef rb_tree_node<T> node_type;
public:
    node_color color;//顏色
    node_type*parent;//父節(jié)點(diǎn)
    node_type*left;//左子節(jié)點(diǎn)
    node_type*right;//右子節(jié)點(diǎn)
    T value;//值
    rb_tree_node(T&v);//構(gòu)造函數(shù)
    inline node_type*brother();//獲取兄弟節(jié)點(diǎn)
    inline bool on_left();//自身是左子節(jié)點(diǎn)
    inline bool on_right();//自身是右子節(jié)點(diǎn)
    inline void set_left(node_type*node);//設(shè)置左子節(jié)點(diǎn)
    inline void set_right(node_type*node);//設(shè)置左子節(jié)點(diǎn)
};

為了表示紅黑樹節(jié)點(diǎn)的顏色,我們定義了一個簡單的枚舉類型。

//紅黑樹節(jié)點(diǎn)顏色
enum rb_tree_node_color
{
    red=false,
    black=true
};

有了節(jié)點(diǎn),剩下的就是實(shí)現(xiàn)紅黑樹的構(gòu)造、插入、搜索、刪除等關(guān)鍵算法了。

//紅黑樹
template<class T>
class rb_tree
{
public:
    typedef rb_tree_node<T> node_type;
    rb_tree();
    ~rb_tree();
    void clear();
    void insert(T v);//添加節(jié)點(diǎn)
    bool insert_unique(T v);//添加唯一節(jié)點(diǎn)
    node_type* find(T v);//查詢節(jié)點(diǎn)
    bool  remove(T v);//刪除節(jié)點(diǎn)
    inline node_type* maximum();//最大值
    inline node_type* minimum();//最小值
    inline node_type* next(node_type*node);//下一個節(jié)點(diǎn)
    inline node_type* prev(node_type*node);//上一個節(jié)點(diǎn)
    void print();//輸出
    int height();//高度
    unsigned count();//節(jié)點(diǎn)數(shù)
    bool validate();//驗(yàn)證
    unsigned get_rotate_times();//獲取旋轉(zhuǎn)次數(shù)
private:
    node_type*root;//樹根
    unsigned rotate_times;//旋轉(zhuǎn)的次數(shù)
    unsigned node_count;//節(jié)點(diǎn)數(shù)
    void __clear(node_type*sub_root);//清除函數(shù)
    void __insert(node_type*&sub_root,node_type*parent,node_type*node);//內(nèi)部節(jié)點(diǎn)插入函數(shù)
    node_type* __find(node_type*sub_root,T v);//查詢
    inline node_type* __maximum(node_type*sub_root);//最大值
    inline node_type* __minimum(node_type*sub_root);//最小值
    void __rebalance(node_type*node);//新插入節(jié)點(diǎn)調(diào)整平衡
    void __fix(node_type*node,node_type*parent,bool direct);//刪除節(jié)點(diǎn)調(diào)整平衡
    void __rotate(node_type*node);//自動判斷類型旋轉(zhuǎn)
    void __rotate_left(node_type*node);//左旋轉(zhuǎn)    
    void __rotate_right(node_type*node);//右旋轉(zhuǎn)
    void __print(node_type*sub_root);//輸出
    int  __height(node_type*&sub_root);//高度
    bool __validate(node_type*&sub_root,int& count);//驗(yàn)證紅黑樹的合法性
};

在紅黑樹類中,定義了樹根(root)節(jié)點(diǎn)數(shù) (count) ,其中還記錄了紅黑樹插入、刪除時(shí)執(zhí)行的旋轉(zhuǎn)次數(shù) rotate_times。其中核心操作有 插入操作(insert) , 搜索操作 (find) , 刪除操作(remove) , 遞減操作(prev) ——尋找比當(dāng)前節(jié)點(diǎn)較小的節(jié)點(diǎn), 遞增操作(next) ——尋找比當(dāng)前節(jié)點(diǎn)比較大的節(jié)點(diǎn), 最大值(maximum)最小值(minimum) 。其中驗(yàn)證操作(__ validate) 通過遞歸操作紅黑樹,驗(yàn)證紅黑樹的基本顏色約束,用于操縱紅黑樹驗(yàn)證紅黑樹是否保持平衡。

(四) 樹的旋轉(zhuǎn)知識

當(dāng)我們在對紅黑樹進(jìn)行插入和刪除等操作時(shí),對樹做了修改,那么可能會違背紅黑樹的性質(zhì)。所以為了繼續(xù)保持紅黑樹的性質(zhì),我們可以通過對節(jié)點(diǎn)進(jìn)行重新著色,以及對樹進(jìn)行相關(guān)的旋轉(zhuǎn)操作,即修改樹中某些節(jié)點(diǎn)的顏色及指針結(jié)構(gòu),來達(dá)到對紅黑樹進(jìn)行插入和刪除結(jié)點(diǎn)等操作后,繼續(xù)保持它的性質(zhì)或平衡。
樹的旋轉(zhuǎn),分為左旋和右旋,借助下圖來做介紹:
1、左旋

左旋.png

  • 當(dāng)在某個結(jié)點(diǎn)pivot上,做左旋操作時(shí),我們假設(shè)它的右孩子不是NIL,pivot可以為任何不是不是NIL的左孩子的結(jié)點(diǎn)。
  • 左旋以pivot到y(tǒng)之間的鏈為"支軸"進(jìn)行,它使y成為該孩子樹新的根,而y的左孩子b則成為pivot的右孩子。

2、右旋

右旋.png

對于樹的旋轉(zhuǎn),能保持不變的只有原樹和搜索性質(zhì),而原樹的紅黑性質(zhì)則不能保持,在紅黑樹的數(shù)據(jù)插入和刪除后可利用旋轉(zhuǎn)和顏色重涂來恢復(fù)樹的紅黑性質(zhì)。
這樣大家對紅黑樹就有了初步了解。這里就不詳細(xì)介紹了,如果大家有興趣,可以自行去了解。

四、Linux的跨進(jìn)程通信(IPC)概述

(一)、跨進(jìn)程通信(IPC)的目的

跨進(jìn)程通信(IPC)的目的主要如下:

  • 數(shù)據(jù)傳遞
    一個進(jìn)程需要將它的數(shù)據(jù)發(fā)送給另外一個進(jìn)程,發(fā)送的數(shù)據(jù)量在一個字節(jié)到幾M字節(jié)之間
  • 共享數(shù)據(jù)
    多個進(jìn)程想要操作共享數(shù)據(jù)。
  • 通知事件
    一個進(jìn)程需要向另一個或一組進(jìn)程發(fā)送消息,通知它(它們)發(fā)生了某種事件(如進(jìn)程終止時(shí)要通知父進(jìn)程)
  • 資源共享
    多個進(jìn)程之間共享資源。為了做到這一點(diǎn),需要內(nèi)核提供鎖和同步機(jī)制
  • 進(jìn)程控制
    有些進(jìn)程希望完全控制另一個進(jìn)程的執(zhí)行(如debug進(jìn)程),此時(shí)控制進(jìn)程希望能夠攔截另一個進(jìn)程的所有步驟和異常,并能夠及時(shí)知道它的狀態(tài)改變。

(二)、Linux 進(jìn)程間通信(IPC)的發(fā)展

Linux下的跨進(jìn)程通信手段基本上是從Unix平臺上的進(jìn)程通信手段繼承而來。而對Unix發(fā)展做出大量貢獻(xiàn)的量大主力AT&T的貝爾實(shí)驗(yàn)室及BSD(加州大學(xué)伯克利分校伯克利軟件發(fā)布中心)在進(jìn)程間通信方面的側(cè)重點(diǎn)有所不同。前者對Unix早期的進(jìn)程間通信手段進(jìn)行了系統(tǒng)的改進(jìn)和擴(kuò)充,形成了"system V IPC",通信進(jìn)程局限在單個計(jì)算機(jī)內(nèi);而后者則跳過了這個限制,形成了基于套接字(socket)的進(jìn)程間通信機(jī)制。
Linux則把兩者繼承了下來。
所以可以把Linux中的進(jìn)程間通信大體分為4類

  • 基于早期Unix的進(jìn)程間通信:管道和信號
  • 基于System V的進(jìn)程間通信:System V消息隊(duì)列、System V 信號燈、System V 共享內(nèi)存
  • 基于Socket 的進(jìn)程間通信:socket
  • POSIX進(jìn)程間通信:posix 消息隊(duì)列、posix信號燈、posix共享內(nèi)存

由于Unix版本的多樣性,電子電器工程協(xié)會(IEEE) 開發(fā)了一個獨(dú)立的Unix標(biāo)準(zhǔn),這個心的ANSI Unix標(biāo)準(zhǔn)被稱為計(jì)算機(jī)環(huán)境的可移植性操作系統(tǒng)?,F(xiàn)有的大部分Unix和流行版本都是遵循POSIX標(biāo)準(zhǔn)的,而Linux從一開始就是遵循POSIX標(biāo)準(zhǔn).

三、Linux的跨進(jìn)程通信詳解

在Linux下進(jìn)程通信有以下七種:

  • 1、匿名管道(pipe)
  • 2、命名管道(FIFO)
  • 3、信號(signal)
  • 4、信號量(semaphore)
  • 5、消息隊(duì)列(message queue)
  • 6、共享內(nèi)存(share memory)
  • 7、套接字(Socket)

那我們就來詳細(xì)的了解下相關(guān)的內(nèi)容

(一)、匿名管道(pipe)

1、什么是匿名管道?

匿名管道(pipe)是Linux支持的最初Unix IPC形式之一,具有以下特點(diǎn):

  • 匿名管道是半雙工的,數(shù)據(jù)只能向一個方向流動;需要雙方通信時(shí),需要建立兩個管道;
  • 只能作用于父子進(jìn)程或者兄弟進(jìn)程之間(具有親緣關(guān)系的進(jìn)程)
  • 單獨(dú)構(gòu)成的一種獨(dú)立的文件系統(tǒng):匿名管道對于管道兩端的進(jìn)程而言,就是一個文件,但它不是普通文件,它不屬于某種文件系統(tǒng),而是自理門戶,單獨(dú)構(gòu)成一種文件系統(tǒng),去并且只存在于內(nèi)存中。
  • 數(shù)據(jù)的讀出和寫入:一個進(jìn)程向管道中寫的內(nèi)容被管道另一端的進(jìn)程讀出。寫入的內(nèi)容每次都添加在管道緩沖區(qū)的末尾,并且每次都是從緩存區(qū)的頭部讀出數(shù)據(jù)。

2、匿名管道的實(shí)現(xiàn)機(jī)制

匿名管道是右內(nèi)核管理的一個緩沖區(qū),相當(dāng)于我們放入內(nèi)存的中一個紙條。匿名管道的一端連接一個進(jìn)程的輸出。這個進(jìn)程會向管道中放入信息。匿名管道的另一端連接一個進(jìn)程的輸入,這個進(jìn)程取出被放入管道的信息。一個緩存區(qū)不需要很大,它被設(shè)計(jì)成為喚醒的數(shù)據(jù)結(jié)構(gòu),以便管道可以被循環(huán)利用。當(dāng)管道中沒有信息的話,從管道中讀取的進(jìn)程會等待,直到另一端的進(jìn)程放入信息。當(dāng)管道被放滿信息的時(shí)候,嘗試放入信息的進(jìn)程就會等待,直到另一端的進(jìn)程取出信息。兩個進(jìn)程都終結(jié)的時(shí)候,管道也會自動消失。如下圖



從原理上,匿名管道利用fork機(jī)制建立,從而讓兩個進(jìn)程可以連接到同一個PIPE上。最開始的時(shí)候,上面的兩個箭頭都連接到同一個進(jìn)程Process 1上(連接在Process 1上的兩個箭頭)。當(dāng)fork復(fù)制進(jìn)程的時(shí)候,會將這兩個連接也復(fù)制到新的進(jìn)程(Process 2)。隨后,每個進(jìn)程關(guān)閉在自己不需要的一個連接(兩個黑色的箭頭被關(guān)閉;Process 1關(guān)閉從PIPE來的輸入連接,Process 2關(guān)閉輸出到PIPE的連接),這樣,剩下的紅色連接就構(gòu)成了上圖的PIPE。
示例圖如下:


3、匿名管道實(shí)現(xiàn)細(xì)節(jié)

在Linux中,匿名管道的實(shí)現(xiàn)并沒有使用專門的數(shù)據(jù)結(jié)構(gòu),而是借助了文件系統(tǒng)的file結(jié)構(gòu),和VFS的索引節(jié)點(diǎn)inode。通過將兩個file結(jié)構(gòu)指向同一個臨時(shí)的VFS節(jié)點(diǎn),而這個VFS索引節(jié)點(diǎn)又指向了一個物理頁面而實(shí)現(xiàn)的。如下圖


管道實(shí)現(xiàn)細(xì)節(jié).png

4、關(guān)于匿名管道的讀寫

匿名管道的實(shí)現(xiàn)的源代碼在fs/pipe.c中,在pipe.c中有很多函數(shù),其中有兩個函數(shù)比較重要,即匿名管道pipe_read()讀函數(shù)和匿名管道寫函數(shù)pipe_write()。匿名管道寫函數(shù)通過將字節(jié)復(fù)制到VFS索引節(jié)點(diǎn)指向物理內(nèi)存而寫入數(shù)據(jù),而匿名管道讀函數(shù)則通過復(fù)制物理內(nèi)存而讀出數(shù)據(jù)。當(dāng)然,內(nèi)核必須利用一定的 同步機(jī)制 對管道的訪問,為此內(nèi)核使用了 、等待隊(duì)列、和 信號。
當(dāng)寫入進(jìn)程向匿名管道中寫入時(shí),它利用標(biāo)準(zhǔn)的庫函數(shù)write(),系統(tǒng)根據(jù)庫函數(shù)傳遞的文件描述符,可找到該文件的file結(jié)構(gòu)。file結(jié)構(gòu)中制定了用來進(jìn)行寫操作的函數(shù)(即寫入函數(shù))地址,于是,內(nèi)核調(diào)用該函數(shù)完成寫操作。寫入函數(shù)在向內(nèi)存中寫入數(shù)據(jù)之前,必須首先檢查VFS索引節(jié)點(diǎn)中的信息,同時(shí)滿足如下條件時(shí),才能進(jìn)行實(shí)際的內(nèi)存復(fù)制工作:

  • 內(nèi)存中有足夠的空間可以容納所有要寫入的數(shù)據(jù)。
  • 內(nèi)存沒有被讀程序鎖定。

如果同時(shí)滿足上述條件,寫入函數(shù)首先會鎖定內(nèi)存,然后從寫進(jìn)程的地址空間中復(fù)制數(shù)據(jù)到內(nèi)存。否則,寫進(jìn)程就休眠在VFS索引節(jié)點(diǎn)的等待隊(duì)列中,接下來,內(nèi)核將調(diào)用調(diào)度程序,而調(diào)度程序會選擇其他進(jìn)程運(yùn)行。寫進(jìn)程實(shí)際處于可中斷的等待狀態(tài),當(dāng)內(nèi)存中有足夠的空間可以容納寫入數(shù)據(jù),或內(nèi)存被解鎖時(shí),讀取進(jìn)程會喚醒寫入進(jìn)程,這時(shí),寫入進(jìn)程將接受到信號。當(dāng)數(shù)據(jù)寫入內(nèi)存之后,內(nèi)存被解鎖,而所有休眠在索引節(jié)點(diǎn)的讀取進(jìn)程會被喚醒。
匿名管道的讀取過程和寫入過程類型。但是,進(jìn)程可以在沒有數(shù)據(jù)或者內(nèi)存被鎖定時(shí)立即返回錯誤信息,而不是阻塞該進(jìn)程,這一來于文件或管道的打開模式。反之,進(jìn)程可以休眠在索引節(jié)點(diǎn)的等待隊(duì)列中等待寫入進(jìn)程寫入數(shù)據(jù)。當(dāng)所有的進(jìn)程完成了管道操作之后,管道的索引節(jié)點(diǎn)被丟棄,而共享數(shù)據(jù)頁被釋放。

PS:有些同學(xué)可能不清楚VFS,我這里就簡單介紹下;

*VFS(virtual File System/虛擬文件系統(tǒng)):是Linux文件系統(tǒng)對外的接口。任何要使用文件系統(tǒng)的程序都必須經(jīng)由這層接口來使用它。它是采用標(biāo)準(zhǔn)的Unix系統(tǒng)調(diào)用讀寫位于不同物理介質(zhì)上的不同文件系統(tǒng)。VFS是一個可以讓open()、read()、write()等系統(tǒng)調(diào)用不用關(guān)系底層的存儲介質(zhì)和文件系統(tǒng)類型就可以工作的粘合層。在Linux中,VFS采用的是面向?qū)ο蟮木幊谭椒?/p>

(二)、命名管道(FIFO/named PIPE)

在上面,我們介紹了匿名管道(pipe),我們知道了如何匿名管道在進(jìn)程之間傳遞數(shù)據(jù),同時(shí)也是看到了這個方式的一個缺陷,就是這些就進(jìn)程都是由一個共同的祖先進(jìn)程啟動,這給我們在不相關(guān)的進(jìn)程之間交換數(shù)據(jù)帶來了不方便。這里我將會介紹另一種通信方式——命名管道,來解決不相關(guān)進(jìn)程之間的問題。

1、什么是命名管道?

  • 命名管道也被稱為FIFO或者named pipe,它是一種特殊類型的文件,它在文件系統(tǒng)中以文件名的形式存在,但是它的行為卻和之前所講的匿名管道類似。
  • FIFO(First in, First out) 為一種特殊的文件類型,它在文件系統(tǒng)中有對應(yīng)的路徑。當(dāng)一個進(jìn)程以讀(r)的方式打開該文件,而另一個進(jìn)程以寫(w)的方式打開該文件,那么內(nèi)核就會在兩個進(jìn)程之間建立管道,所以FIFO實(shí)際上也由內(nèi)核管理,不與硬盤打交道。之所以叫FIFO,因?yàn)楣艿辣举|(zhì)上是一個** 先進(jìn)先出的隊(duì)列數(shù)據(jù)結(jié)構(gòu) ,最早放入的數(shù)據(jù)被最先讀出來,從而保證信息交流的順序。FIFO只是借用了文件系統(tǒng)(file system,命名管道是一種特殊類型的文件,因?yàn)長inux中所有事物都是文件,它是在文件系統(tǒng)中以文件名的形式存在。)來為管道命名。寫模式的進(jìn)程向FIFO中寫入,而讀模式的進(jìn)程從FIFO文件中讀出。當(dāng)刪除FIFO文件時(shí),管道連接也隨之消失。FIFO的好處在于我們可以通過 文件的路徑來識別管道,從而讓沒有親緣關(guān)系的進(jìn)程之間建立連接 **。

2、命名管道的讀寫規(guī)則:

  • 1、從FIFO中讀取數(shù)據(jù)的約定:如果一個進(jìn)程為了從FIFO中讀取數(shù)據(jù)而阻塞打開了FIFO,那么該進(jìn)程內(nèi)的讀操作 為設(shè)置了阻塞標(biāo)志的讀操作。
  • 2、從FIFO中寫入數(shù)據(jù)的約定:如果一個進(jìn)程為了想FIFO中寫入數(shù)據(jù)而阻塞打開了FIFO,那么該進(jìn)程內(nèi)的寫操作 為設(shè)置了阻塞標(biāo)志的寫操作。

3、命名管道的安全問題:

大家想一下,只使用一個FIFO文件,如果有多個進(jìn)程同時(shí)向同一個FIFO文件寫數(shù)據(jù),而只有一個讀FIFO進(jìn)程在同一個FIFO文件讀取數(shù)據(jù)時(shí),會發(fā)生怎么樣的情況呢,會發(fā)生數(shù)據(jù)塊的相互交錯是很正常的?而且個人認(rèn)為多個不同進(jìn)程向一個FIFO讀取進(jìn)程發(fā)送數(shù)據(jù)是很正常的情況。

為了解決這個問題,就是讓寫操作原子化,怎么才能使寫操作原子化呢?答案其實(shí)很簡單:系統(tǒng)規(guī)定,在一個以O(shè)_WRONLY(即阻塞方式)打開的FIFO中,如果寫入的數(shù)據(jù)長度小于等于PIPE_BUF,那么或者寫入全部字節(jié),或者一個字節(jié)都不寫入。如果所有的寫請求都是發(fā)往一個阻塞的FIFO的,并且每個寫請求的數(shù)據(jù)長度小于等于PIPE_BUF字節(jié),系統(tǒng)就可以確保數(shù)據(jù)絕不會交錯在一起

(三)、信號(Signal)

1、什么是信號?

信號是比較復(fù)雜的通信方式,用于通知接受進(jìn)程有某種事件發(fā)生,除了用于進(jìn)程間通信外,進(jìn)程還可以發(fā)送信號給進(jìn)程本身;Linux除了支持Unix早期信號語義函數(shù)sigal外,還支持語義服務(wù)Posix.1標(biāo)準(zhǔn)的信號函數(shù)sigaction(實(shí)際上,該函數(shù)是基于BSD的,BSD為了實(shí)現(xiàn)可靠信號機(jī)制,有能夠統(tǒng)一對外接口,用sigaction函數(shù)重新實(shí)現(xiàn)了signal函數(shù))

2、信號的種類

如下圖:


信號種類.png

每種信號類型都有對應(yīng)的信號處理程序(也叫信號的操作),就好像每個中斷都有一個中斷服務(wù)例程一樣。大多數(shù)信號的默認(rèn)操作是結(jié)束接受信號的進(jìn)程;然而一個進(jìn)程通??梢哉埱笙到y(tǒng)采取某些代替的操作,各種代替操作是:

  • 忽略信號。隨著這一選項(xiàng)的設(shè)置,進(jìn)程將忽略信號的出現(xiàn)。有兩個信號不可以被忽略:SIGKILL,它將結(jié)束進(jìn)程:SIGSTOP,它是作業(yè)控制機(jī)制的一部分,將掛起作業(yè)的執(zhí)行。
  • 恢復(fù)信號的默認(rèn)操作
  • 執(zhí)行一個預(yù)先安排的信號處理函數(shù)。進(jìn)程可以登記特殊的信號處理函數(shù)。當(dāng)進(jìn)程收到信號時(shí),信號處理函數(shù)將像中斷服務(wù)例程一樣被調(diào)用,當(dāng)從信號處理函數(shù)返回時(shí),控制被返回給主程序,并且繼續(xù)正常執(zhí)行。

但是,信號和中斷有所不同。中斷的響應(yīng)和處理都發(fā)生在內(nèi)核空間,而信號的響應(yīng)發(fā)生在內(nèi)核空間,信號處理程序的執(zhí)行卻發(fā)生在用戶空間。

那么什么時(shí)候檢測和響應(yīng)信號?通常發(fā)生在兩種情況下:

  • 當(dāng)前進(jìn)程由于系統(tǒng)調(diào)用、中斷或異常而進(jìn)入內(nèi)核空間以后,從內(nèi)核空間返回到用戶空間前戲
  • 當(dāng)前進(jìn)程在內(nèi)核進(jìn)入睡眠以后剛被喚醒的時(shí)候,由于檢測到信號的存在而提前返回到用戶空間

3、信號的本質(zhì)

信號是在軟件層次上對中斷機(jī)制的一種模擬,在原理上,一個進(jìn)程收到一個信號與處理器收到一個中斷請求可以說是一樣的。信號是異步的,一個進(jìn)程不必通過任何操作來等待信號的到達(dá),事實(shí)上,進(jìn)程也不知道信號到底什么時(shí)候到達(dá)。

信號是進(jìn)程間通信機(jī)制中唯一的異步通信機(jī)制,可以看作是異步通知,通知接收信號的進(jìn)程有哪些事情發(fā)生了。信號機(jī)制經(jīng)過POSIX實(shí)時(shí)擴(kuò)展后,功能更加強(qiáng)大,除了基本通知功能外,還可以傳遞附加信息。

4、信號來源

信號事件的發(fā)生有兩個來源:硬件來源(比如我們按下鍵盤或者其他硬件故障);潤健來源,最常用發(fā)送信號的系統(tǒng)函數(shù)是kill,raise,alarm和setitimer以及sigqueue函數(shù),軟件來源還包括一些非法運(yùn)算等操作。

5、關(guān)于信號處理機(jī)制的原理(內(nèi)核角度)

內(nèi)核給一個進(jìn)程發(fā)送中斷信號,是在進(jìn)程所在的進(jìn)程表項(xiàng)的信號域設(shè)置對應(yīng)于該信號的位。這里要補(bǔ)充的是,如果信號發(fā)送給一個正在睡眠的進(jìn)程,那么要看該進(jìn)程進(jìn)入睡眠的優(yōu)先級,如果進(jìn)程睡眠在可被中斷的優(yōu)先級上,則喚醒正在睡眠的進(jìn)程;否則僅設(shè)置進(jìn)程表中信號域相應(yīng)的位,而不是喚醒進(jìn)程。這一點(diǎn)比較重要,因?yàn)檫M(jìn)程檢查是否收到信號的時(shí)機(jī)是:一個進(jìn)程在即將從內(nèi)核態(tài)返回到用戶態(tài)時(shí);或者,在一個進(jìn)程進(jìn)入或離開一個適當(dāng)?shù)牡驼{(diào)度優(yōu)先級睡眠狀態(tài)時(shí)。

內(nèi)核處理一個進(jìn)程吸收的信號的時(shí)機(jī)是在一個進(jìn)程從內(nèi)核態(tài)返回用戶態(tài)時(shí)。所以,當(dāng)一個進(jìn)程在內(nèi)核態(tài)下運(yùn)行時(shí),軟中斷信號并不立即起作用,要等到將返回用戶態(tài)時(shí)才處理。進(jìn)程只有處理完信號才會返回用戶態(tài),進(jìn)程在用戶態(tài)下不會有未處理完的信號。

內(nèi)核處理一個進(jìn)程收到的軟中斷信號是在該進(jìn)程的上下文中。因此,進(jìn)程必須處于運(yùn)行狀態(tài)。如果進(jìn)程收到一個要捕捉的信號,那么進(jìn)程從內(nèi)核態(tài)返回用戶態(tài)時(shí)執(zhí)行用戶定義的函數(shù)。而且執(zhí)行用戶定義的函數(shù)的方法很巧妙,內(nèi)核是在用戶棧上創(chuàng)建一個新的層,該層中將返回地址的值設(shè)置成用戶定義的處理函數(shù)的地址,這樣進(jìn)程從內(nèi)核返回彈出棧頂時(shí)就返回到用戶定義的函數(shù)處,從函數(shù)返回再彈出棧頂時(shí),才返回原先進(jìn)入內(nèi)核的地方,接著原來的地方繼續(xù)運(yùn)行。這樣做的原因是用戶定義的處理函數(shù)不能且不允許在內(nèi)核態(tài)下執(zhí)行。

6、信號的生命周期

信號的生命周期.png

(四)、信號量(semaphore)

1、什么是信號量

信號量又稱為信號燈,它用來協(xié)調(diào)不用進(jìn)程間的數(shù)據(jù)對象,而最主要的應(yīng)用是共享內(nèi)存方式的進(jìn)程間通信。本質(zhì)上,信號量是一個計(jì)數(shù)器,他用來記錄某個資源(如共享內(nèi)存)的存取狀況。信號量的使用,主要是用來保護(hù)共享資源,使得資源在一個時(shí)刻只有一個進(jìn)程(線程)所擁有。

信號量的值為正的時(shí)候,說明它空閑。所有的線程可以鎖定而使用它。若為0,說明它被占用,測試的線程要進(jìn)入睡眠隊(duì)列中,等待被喚醒。

2、信號量的注意事項(xiàng)

為了防止出現(xiàn)因多個程序同時(shí)訪問一個共享資源而引發(fā)的一系列問題,我們需要這一種方法,它可以通過生成并使用令牌來授權(quán),在任一時(shí)刻只能由一個執(zhí)行線程訪問代碼的臨界區(qū)域
臨界區(qū)域是指執(zhí)行數(shù)據(jù)更新的代碼需要獨(dú)占式地執(zhí)行。而信號量就可以提供這樣的一種訪問機(jī)制,讓一個臨界區(qū)同一時(shí)間只有一個線程在訪問它,也就說信號量臨界區(qū)是指執(zhí)行數(shù)據(jù)更新的代碼需要獨(dú)占式地執(zhí)行。而信號量就可以提供這樣的一種訪問機(jī)制,讓一個臨界區(qū)同一時(shí)間只有一個線程在訪問它,也就說信號量是用來協(xié)調(diào)進(jìn)程對共享資源的訪問。
信號量是一個特殊的變量,程序?qū)ζ湓L問都是原子操作,且只允許對它進(jìn)行等待(即P-信號變量)和發(fā)送(即V信號變量)信息操作。
最簡單的信號量只能取0和1的變量,這也是信號量最常見的一種形式,叫做二進(jìn)制信號量。而可以取多個正整數(shù)的信號量被稱為通用信號量。

3、信號量的原理

由于信號量只能進(jìn)行兩種操作即"等待"和"發(fā)送",即P(sv)和V(sv),他們的行為是這樣:

  • P(sv):如果sv的值大于零,就給它減1;如果它的值為零,就掛起該進(jìn)程的執(zhí)行
  • V(sv):如果有其他進(jìn)程因等待sv而被掛起,就讓它恢復(fù)運(yùn)行,如果沒有進(jìn)程因等待sv而掛起,就給它加1。

舉個例子,就是兩個進(jìn)程共享信號量sv,一旦其中一個進(jìn)程執(zhí)行了P(sv)操作,他將得到信號量,并可以進(jìn)入臨界區(qū),使sv減1。而第二個進(jìn)程將被阻止進(jìn)入臨界區(qū),因?yàn)楫?dāng)它試圖執(zhí)行P(sv)時(shí),sv為0,它會掛起以等待第一個進(jìn)程離開臨界區(qū)并執(zhí)行(sv)釋放信號。

3、信號量的分類

Linux提供兩種信號量

  • 內(nèi)核信號量:由內(nèi)核控制路徑使用
  • 用戶態(tài)進(jìn)程使用的信號量:這種信號量又分為POSIX信號量和SYSTEM V信號量

POSIX信號量又分為有名信號量和無名信號量

  • 有名信號量:其值保存在文件中,所以它可以用于線程也可以用于進(jìn)程間同步
  • 無名信號量:其值保存在內(nèi)存。

POSIX信號量和SYSTEM V信號量的比較

  • 對POSIX來說,信號量是個非負(fù)數(shù)。常用于線程間同步
  • 而SYSTEM V信號量則是一個或者多個信號量集合,他對應(yīng)的是一個信號量的結(jié)構(gòu)體,這個結(jié)構(gòu)體為SYSTEM V IPC服務(wù)的,信號量只不過是它的一部分。常用語進(jìn)程間同步。
  • POSIX信號量的引用頭文件是<semaphore,h>,而SYSTEM V信號量的引用頭文件是<sys/sem.h>

從使用的角度,System V信號量是簡單的.

(五)、消息隊(duì)列(message)

1、消息隊(duì)列也稱為報(bào)文隊(duì)列:

消息隊(duì)列也成為報(bào)文隊(duì)列,消息隊(duì)列是隨內(nèi)核持續(xù)的,只有在內(nèi)核重啟或者顯式刪除一個消息隊(duì)列時(shí),該消息隊(duì)列才會真正刪除系統(tǒng)中記錄消息隊(duì)列的數(shù)據(jù)結(jié)構(gòu)體 struct ipc_ids_msg_ids位于內(nèi)核中,系統(tǒng)中所有消息隊(duì)列都可以在結(jié)構(gòu)msg_ids中找到訪問入口。

2、消息隊(duì)列的原理及注意事項(xiàng):

消息隊(duì)列其實(shí)就是一個消息的鏈表,每個消息都有一個隊(duì)列頭,稱為struct_msg_queue,這個隊(duì)列頭描述了消息隊(duì)列的key值,用戶ID,組ID等信息,但它存于內(nèi)核中而結(jié)構(gòu)體struct msqid_ds能夠返回或設(shè)置消息隊(duì)列的信息,這個結(jié)構(gòu)體位于用戶空間中,與msg_queue結(jié)構(gòu)相似的消息隊(duì)列允許一個或多個進(jìn)程向它寫入或讀取消息,消息隊(duì)列是消息的鏈表。
消息是按消息類型訪問,進(jìn)程必須指定消息類型來讀取消息,同樣,當(dāng)向消息隊(duì)列中寫入消息時(shí)必須給出消息的類型,如果讀隊(duì)列使用消息的類型為0,則讀取隊(duì)列中的第一條消息。
內(nèi)核空間的結(jié)構(gòu)體msg_queue描述了對應(yīng)key值消息隊(duì)列的情況,而對應(yīng)用戶空間的msqid_ds這個結(jié)構(gòu)體,因此,可以操作msgid_ds這個結(jié)構(gòu)體來操作消息隊(duì)列。

(六)、共享內(nèi)存(share memory)

共享內(nèi)存是進(jìn)程間通信中最簡單的方式之一。

1、什么是共享內(nèi)存?

共享內(nèi)存是系統(tǒng)處于多個進(jìn)程之間通訊的考慮,而預(yù)留的一塊內(nèi)存區(qū)。共享內(nèi)存允許兩個或更多的進(jìn)程訪問同一塊內(nèi)存,就如同malloc()函數(shù)向不同進(jìn)程返回了指向同一個物理內(nèi)存區(qū)域的指針。當(dāng)一個進(jìn)程改變了這塊地址中的內(nèi)容的時(shí)候,其他進(jìn)程都會覺察到這個更改。

2、關(guān)于共享內(nèi)存

當(dāng)一個程序加載進(jìn)內(nèi)存后,它就被分成叫做頁的塊。通信將存在內(nèi)存的兩個頁之間或者兩個獨(dú)立的進(jìn)程之間。
當(dāng)一個程序想和另外一個程序通信的時(shí)候,那內(nèi)存將會為這兩個程序生成一塊公共的內(nèi)存區(qū)域。這塊被兩個進(jìn)程分享的內(nèi)存區(qū)域叫做共享內(nèi)存。
由于所有進(jìn)程共享同一塊內(nèi)存,共享內(nèi)存在各種進(jìn)程間通信方式中具有最高的效率。訪問共享內(nèi)存區(qū)域和訪問進(jìn)程獨(dú)有的內(nèi)存區(qū)域一樣快,并不需要通過系統(tǒng)調(diào)用或者其他需要切入內(nèi)核的過程來完成。同時(shí)它也也避免了對數(shù)據(jù)的跟蹤不必要的復(fù)制。
如果沒有共享內(nèi)存的概念,那一個進(jìn)程不能存取另外一個進(jìn)程的內(nèi)存部分,因而導(dǎo)致共享數(shù)據(jù)或者通信失效。因?yàn)橄到y(tǒng)內(nèi)核沒有對訪問共享內(nèi)存進(jìn)行同步,開發(fā)者必須提供自己的同步措施。
解決了這些問題的常用方法是是通過信號量進(jìn)行同步。不過通常我們程序只有一個進(jìn)程訪問了共享內(nèi)存,因此在集中展示了共享內(nèi)存機(jī)制的同時(shí),我們避免了讓代碼被同步邏輯搞的混亂不堪。

為了簡化共享數(shù)據(jù)的完整性和避免同時(shí)存取數(shù)據(jù),內(nèi)核提供了一種專門存取共享內(nèi)存資源的機(jī)制。這稱為互斥體或者M(jìn)utex對象。

3、Mutex對象

例如,在數(shù)據(jù)被寫入前不允許進(jìn)程從共享內(nèi)存中讀取信息、不允許兩個進(jìn)程同時(shí)向一個共享內(nèi)存地址寫入數(shù)據(jù)等。
當(dāng)一個進(jìn)程想和另一個進(jìn)程通信的時(shí)候,它將按以下順序運(yùn)行:

  • 1、獲取Mutex對象,鎖定共享區(qū)域
  • 2、將要通信的數(shù)據(jù)寫入共享區(qū)域
  • 3、釋放Mutex對象

當(dāng)一個進(jìn)程從這個區(qū)域讀取數(shù)據(jù)的時(shí)候,它將重復(fù)同樣的步驟,只是將第二步變成讀取。

4、內(nèi)存模型

要使用一塊共享內(nèi)存

  • 進(jìn)程必須首先分配它
  • 隨后需要訪問這個共享內(nèi)存塊的每一個進(jìn)程都必須將這個共享內(nèi)存綁定到自己的地址空間中。
  • 當(dāng)完成通信之后,所有進(jìn)程都脫離共享內(nèi)存,并且由一個進(jìn)程釋放該共享內(nèi)存塊。

在/proc/sys/kernel/目錄下,記錄著共享內(nèi)存的一些限制,如一個共享內(nèi)存區(qū)的最大字節(jié)數(shù)shmmax,系統(tǒng)范圍內(nèi)最大的共享內(nèi)存區(qū)標(biāo)志符數(shù)shmmni等。

5、Linux系統(tǒng)內(nèi)存模型

在Linux系統(tǒng)中,每個進(jìn)程的虛擬內(nèi)存是被分為許多頁面的。這些內(nèi)存頁面中包含了實(shí)際的數(shù)據(jù)。每個進(jìn)程都會維護(hù)一個從內(nèi)存地址到虛擬內(nèi)存頁面之間的映射關(guān)系。盡管每個進(jìn)程都有自己的內(nèi)存地址,不同的進(jìn)程可以同時(shí)將同一個頁面頁面映射到自己的地址空間,從而達(dá)到共享內(nèi)存的目的。
分配一個新的共享內(nèi)存塊會創(chuàng)建新的內(nèi)存頁面。因?yàn)樗羞M(jìn)程都希望共享對同一塊內(nèi)存的訪問,只應(yīng)由一個進(jìn)程創(chuàng)建一塊新的共享內(nèi)存。再次分配一塊已經(jīng)存在的內(nèi)存塊不會創(chuàng)建新的頁面,而只是會返回一個標(biāo)示該內(nèi)存塊的標(biāo)識符。
一個進(jìn)程如需使用這個共享內(nèi)存塊,則首先需要將它綁定到自己的地址空間中。
這樣會創(chuàng)建一個從進(jìn)程本身虛擬地址到共享頁面的映射關(guān)系。當(dāng)對共享內(nèi)存的使用結(jié)束之后,這個映射關(guān)系將被刪除。
當(dāng)再也沒有進(jìn)程需要使用這個共享內(nèi)存塊的時(shí)候,必須有一個(有且只有一個)進(jìn)程負(fù)責(zé)釋放這個被共享的內(nèi)存頁面。
所有共享內(nèi)存塊的大小必須是系統(tǒng)頁面大小的整數(shù)倍。系統(tǒng)頁面大小指的是系統(tǒng)中單個內(nèi)存頁面包含的字節(jié)數(shù)。在Linux系統(tǒng)中,內(nèi)存頁面大小是4KB,不過您仍然應(yīng)通過調(diào)用getPageSize獲取這個值。

6、Linux共享內(nèi)存的實(shí)現(xiàn)步驟

共享內(nèi)存的實(shí)現(xiàn)分為兩個步驟:

  • 創(chuàng)建共享內(nèi)存,使用shmget函數(shù)
  • 映射共享內(nèi)存,將這段創(chuàng)建的共享內(nèi)存映射到具體的進(jìn)程空間中,使用shmat函數(shù)

(七)、套接字(socket)

套接字也是一種進(jìn)程間通信機(jī)制,與其他通信機(jī)制不同的是,它可用于不同機(jī)器間的進(jìn)程通信。
更為一般的進(jìn)程間通信機(jī)制,可用于不同機(jī)器之間的進(jìn)程間通信。起初是右Unix系統(tǒng)的BSD分之開發(fā)出來的,但是現(xiàn)在一般可以移植其他類Unix系統(tǒng)上。比如Linux和System V的變種都支持套接字。

(八) Linux的幾種跨進(jìn)程通信的方式的比較

1、效率比較

PS:無連接是指無需調(diào)用某種行動是OPEN,就有發(fā)送消息的能力流控制,如果系統(tǒng)資源短缺或者不能接受更多的消息,則發(fā)送進(jìn)程能進(jìn)進(jìn)行流量控制。

2、優(yōu)缺點(diǎn)比較

  • 匿名管道(pipe):速度慢,容量有限,只有父子進(jìn)程能通訊
  • 有名管道(FIFO): 任務(wù)進(jìn)程都能通訊,但速度慢
  • 消息隊(duì)列(message queue):容量收到系統(tǒng)限制,且要注意第一次讀的時(shí)候,要考慮上一次沒有讀完數(shù)據(jù)問題。
  • 信號量:不能傳遞復(fù)雜消息,只能用來同步
  • 共享內(nèi)存區(qū):能夠容易控制容量,速度快,但要保持同步,比如一個進(jìn)程在寫的時(shí)候,另一個進(jìn)程要注意讀寫的問題。相當(dāng)于線程中的線程安全,當(dāng)然,共享內(nèi)存區(qū)同樣可以做線程間通訊,不過沒有這個必要,線程間本來就已經(jīng)共享了同一進(jìn)程內(nèi)的一塊內(nèi)存

3、使用場景

  • 如果用戶傳遞的信息較少或是需要通過信號來觸發(fā)某些行為,上面提到的軟中斷限號機(jī)制不失為一種簡潔有效的一種進(jìn)程間通信方式。但若是進(jìn)程間要求傳遞的信息量比較大或者進(jìn)程間存在交換數(shù)據(jù)的要求,那就需要考慮別的通信方式.
  • 匿名管道簡單方便,但局限于單向通信的工作方式,并且只能創(chuàng)建它的進(jìn)程及其子孫進(jìn)程之間實(shí)現(xiàn)管道的共享。
  • 有名管道雖然可以提供給任意關(guān)系的進(jìn)程使用,但是由于其長期存在于系統(tǒng)之中,使用不當(dāng)容易出錯。所以不建議初級開發(fā)者使用。
  • 消息緩存可以不再局限于父子進(jìn)程,而允許任意進(jìn)程間通過共享消息隊(duì)列來實(shí)現(xiàn)進(jìn)程間通信,并由系統(tǒng)調(diào)用函數(shù)來實(shí)現(xiàn)消息發(fā)送和接受方之間的同步,從而使得用戶在使用消息緩沖進(jìn)行通信時(shí)不再需要考慮同步問題,使用方便,但是信息的復(fù)制需要額外的消耗CPU的時(shí)間,不適宜信息量大或操作頻繁的場合。
    共享內(nèi)存針對消息緩存的缺點(diǎn)而改進(jìn),利用了內(nèi)存緩存區(qū)直接交換信息,無需復(fù)制,快捷、信息量大的是其優(yōu)點(diǎn)。但是共享內(nèi)存的通信方式是通過將共享內(nèi)存緩存直接附加到進(jìn)程的虛擬地址空間中來實(shí)現(xiàn)的。因此這些進(jìn)程之間的讀寫操作的同步問題操作系統(tǒng)無法實(shí)現(xiàn)。必須由各進(jìn)程利用其它同步工具解決。另外, 由于內(nèi)存實(shí)體存在于計(jì)算機(jī)系統(tǒng)中,所以只能由處于同一個計(jì)算機(jī)系統(tǒng)中的其它進(jìn)程共享,不方便網(wǎng)絡(luò)通信。

補(bǔ)充一點(diǎn):
共享內(nèi)存塊提供了在任意數(shù)量的進(jìn)程之間進(jìn)行高效雙向通信的機(jī)制。每個使用者都可以讀取寫入數(shù)據(jù),但是所有程序之間必須達(dá)成并遵守一定的協(xié)議,以防止諸如在讀取信息之前覆寫內(nèi)存空間等競爭狀態(tài)的出現(xiàn)。不行的是,Linux無法嚴(yán)格保證提供對共享內(nèi)存塊的獨(dú)占訪問,同時(shí),多個使用共享內(nèi)存塊的進(jìn)程之間必須協(xié)調(diào)使用同一個鍵值。

參考文獻(xiàn)

Android跨進(jìn)程通信IPC之1——Linux基礎(chǔ)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android跨進(jìn)程通信IPC整體內(nèi)容如下 1、Android跨進(jìn)程通信IPC之1——Linux基礎(chǔ)2、Andro...
    隔壁老李頭閱讀 16,042評論 19 113
  • 一.管道機(jī)制(pipe) 1.Linux的fork操作 在計(jì)算機(jī)領(lǐng)域中,尤其是Unix及類Unix系統(tǒng)操作系統(tǒng)中,...
    Geeks_Liu閱讀 3,810評論 1 9
  • 1 進(jìn)程介紹 1.1 進(jìn)程和程序 所謂進(jìn)程是由正文段(text)、用戶數(shù)據(jù)段(user segment)以及系統(tǒng)數(shù)...
    瘋狂小王子閱讀 1,346評論 0 7
  • 每個人都有一份好,只給屬于心里的好,這份獨(dú)特的好,也許有人會珍惜到老,也許有一天就是一句大家好才是真的好。那份感覺...
    芊芊好好閱讀 334評論 0 0
  • 人類最大的情緒體驗(yàn),無非就是愛和吃,所以所有中外節(jié)日都離不開愛的普及和饕餮的盛宴,這無疑對單身汪們造成了更大悲憤。...
    羅小宸閱讀 506評論 0 0

友情鏈接更多精彩內(nèi)容