• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆-167  評(píng)論-8  文章-0  trackbacks-0

            一個(gè)大型的應(yīng)用系統(tǒng),往往需要眾多進(jìn)程協(xié)作,進(jìn)程(Linux進(jìn)程概念見附1)間通信的重要性顯而易見。本系列文章闡述了 Linux環(huán)境下的幾種主要進(jìn)程間通信手段,并針對(duì)每個(gè)通信手段關(guān)鍵技術(shù)環(huán)節(jié)給出詳細(xì)實(shí)例。為達(dá)到闡明問題的目的,本文還對(duì)某些通信手段的內(nèi)部實(shí)現(xiàn)機(jī)制進(jìn) 行了分析。

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



             

            其 中,最初Unix IPC包括:管道、FIFO、信號(hào);System V IPC包括:System V消息隊(duì)列、System V信號(hào)燈、System V共享內(nèi)存區(qū);Posix IPC包括: Posix消息隊(duì)列、Posix信號(hào)燈、Posix共享內(nèi)存區(qū)。有兩點(diǎn)需要簡(jiǎn)單說明一下:1)由于Unix版本的多樣性,電子電氣工程協(xié)會(huì)(IEEE)開 發(fā)了一個(gè)獨(dú)立的Unix標(biāo)準(zhǔn),這個(gè)新的ANSI Unix標(biāo)準(zhǔn)被稱為計(jì)算機(jī)環(huán)境的可移植性操作系統(tǒng)界面(PSOIX)。現(xiàn)有大部分Unix和流行版本都是遵循POSIX標(biāo)準(zhǔn)的,而Linux從一開始就遵 循POSIX標(biāo)準(zhǔn);2)BSD并不是沒有涉足單機(jī)內(nèi)的進(jìn)程間通信(socket本身就可以用于單機(jī)內(nèi)的進(jìn)程間通信)。事實(shí)上,很多Unix版本的單機(jī) IPC留有BSD的痕跡,如4.4BSD支持的匿名內(nèi)存映射、4.3+BSD對(duì)可靠信號(hào)語(yǔ)義的實(shí)現(xiàn)等等。

            圖 一給出了linux 所支持的各種IPC手段,在本文接下來的討論中,為了避免概念上的混淆,在盡可能少提及Unix的各個(gè)版本的情況下,所有問題的討論最終都會(huì)歸結(jié)到Linux環(huán)境下的進(jìn)程間通信上來。并且,對(duì)于Linux所支持通信手段的不同實(shí)現(xiàn)版本(如對(duì)于共享內(nèi)存來說,有Posix共享內(nèi)存區(qū)以及System V共享內(nèi)存區(qū)兩個(gè)實(shí)現(xiàn)版本),將主要介紹Posix API。

            linux下進(jìn)程間通信的幾種主要手段簡(jiǎn)介:

            1. 管道(Pipe)及有名管道(named pipe):

            管道可用于具有親緣關(guān)系進(jìn)程間的通信,有名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無(wú)親緣關(guān)系進(jìn)程間的通信;

            2. 信 號(hào)(Signal):

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

            3. 報(bào)文(Message)隊(duì)列(消息隊(duì)列):

            消息隊(duì)列是消息的鏈接表,包括Posix消息隊(duì)列system V消息隊(duì)列。有足夠權(quán)限的進(jìn)程可以向隊(duì)列中添加消息,被賦予讀權(quán)限的進(jìn)程則可以讀走隊(duì)列中的消息。消息隊(duì)列克服了信號(hào)承載信息量少,管道只能承載無(wú)格式字節(jié)流以及緩沖區(qū)大小受限等缺點(diǎn)。

            4. 共享內(nèi)存:

            使得多個(gè)進(jìn)程可以訪問同一塊內(nèi)存空間,是最快的可用IPC形式。是針對(duì)其他通信機(jī)制運(yùn)行效率較低而設(shè)計(jì)的。往往與其它通信機(jī)制,如信號(hào)量結(jié)合使用,來達(dá)到進(jìn)程間的同步及互斥。

            5. 信號(hào)量(semaphore):

            主要作為進(jìn)程間以及同一進(jìn)程不同線程之間的同步手段。

            6. 套接口(Socket):

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

            下面將對(duì)上述通信機(jī)制做具體闡述。

            一般來說,linux下的進(jìn)程包含以下幾個(gè)關(guān)鍵要素:

            ·         有一段可執(zhí)行程序;

            ·         有專用的系統(tǒng)堆棧空間;

            ·         內(nèi)核中有它的控制塊(進(jìn)程控制塊),描述進(jìn)程所占用的資源,這樣,進(jìn)程才能接受內(nèi)核的調(diào)度;

            ·         具有獨(dú)立的存儲(chǔ)空間

            進(jìn)程和線程有時(shí)候并不完全區(qū)分,而往往根據(jù)上下文理解其含義。

             

            參考資料

            ·         UNIX環(huán)境高級(jí)編程,作者:W.Richard Stevens,譯者:尤晉元等,機(jī)械工業(yè)出版社。具有豐富的編程實(shí)例,以及關(guān)鍵函數(shù)伴隨Unix的發(fā)展歷程。

            ·         linux內(nèi)核源代碼情景分析(上、下),毛德操、胡希明著,浙江大學(xué)出版社,提供了對(duì)linux內(nèi)核非常好的分析,同時(shí),對(duì)一些關(guān)鍵概念的背景進(jìn)行了詳細(xì)的說明;對(duì)linux環(huán)境下的進(jìn)程進(jìn)行了概括說明。

            ·         UNIX網(wǎng)絡(luò)編程第二卷:進(jìn)程間通信,作者:W.Richard Stevens,譯者:楊繼張,清華大學(xué)出版社。一本比較全面闡述Unix環(huán)境下進(jìn)程間通信的書(沒有信號(hào)和套接口,套接口在第一卷中)。

             

            進(jìn)程間通信與應(yīng)用程序間通信及其實(shí)現(xiàn)技術(shù)

            ---- 摘 要 本文討論了進(jìn)程間通信與應(yīng)用程序間通信的含義及相應(yīng)的實(shí)現(xiàn)技術(shù),并對(duì)這些技術(shù)的原理、特性等進(jìn)行了深入的分析和比較。

            ---- 關(guān)鍵詞 信號(hào) 管道 消息隊(duì)列 共享存儲(chǔ)段 信號(hào)燈 遠(yuǎn)程過程調(diào)用 Socket套接字MQSeries

            引言

            ---- 進(jìn)程間通信的主要目的是實(shí)現(xiàn)同一計(jì)算機(jī)系統(tǒng)內(nèi)部的相互協(xié)作的進(jìn)程之間的數(shù)據(jù)共享與信息交換,由于這些進(jìn)程處于同一軟件和硬件環(huán)境下,利用操作系統(tǒng)提供的的 編程接口,用戶可以方便地在程序中實(shí)現(xiàn)這種通信;應(yīng)用程序間通信的主要目的是實(shí)現(xiàn)不同計(jì)算機(jī)系統(tǒng)中的相互協(xié)作的應(yīng)用程序之間的數(shù)據(jù)共享與信息交換,由于應(yīng) 用程序分別運(yùn)行在不同計(jì)算機(jī)系統(tǒng)中,它們之間要通過網(wǎng)絡(luò)之間的協(xié)議才能實(shí)現(xiàn)數(shù)據(jù)共享與信息交換。進(jìn)程間通信和應(yīng)用程序間通信及相應(yīng)的實(shí)現(xiàn)技術(shù)有許多相同之 處,也各有自己的特色。即使是同一類型的通信也有多種的實(shí)現(xiàn)方法,以適應(yīng)不同情況的需要。

            ---- 為了充分認(rèn)識(shí)和掌握這兩種通信及相應(yīng)的實(shí)現(xiàn)技術(shù),本文將就以下幾個(gè)方面對(duì)這兩種通信進(jìn)行深入的討論:?jiǎn)栴}的由來、解決問題的策略和方法、每種方法的工作原理和實(shí)現(xiàn)、每種實(shí)現(xiàn)方法的特點(diǎn)和適用的范圍等。

            進(jìn)程間的通信及其實(shí)現(xiàn)技術(shù)

            ---- 用戶提交給計(jì)算機(jī)的任務(wù)最終都是通過一個(gè)個(gè)的進(jìn)程來完成的。在一組并發(fā)進(jìn)程中的任何兩個(gè)進(jìn)程之間,如果都不存在公共變量,則稱該組進(jìn)程為不相交的。在不相 交的進(jìn)程組中,每個(gè)進(jìn)程都獨(dú)立于其它進(jìn)程,它的運(yùn)行環(huán)境與順序程序一樣,而且它的運(yùn)行環(huán)境也不為別的進(jìn)程所改變。運(yùn)行的結(jié)果是確定的,不會(huì)發(fā)生與時(shí)間相關(guān) 的錯(cuò)誤。

            ---- 但是,在實(shí)際中,并發(fā)進(jìn)程的各個(gè)進(jìn)程之間并不是完全互相獨(dú)立的,它們之間往往存在著相互制約的關(guān)系。進(jìn)程之間的相互制約關(guān)系表現(xiàn)為兩種方式:

            ---- (1) 間接相互制約:共享CPU

            ---- (2) 直接相互制約:競(jìng)爭(zhēng)和協(xié)作

            ---- 競(jìng)爭(zhēng)——進(jìn)程對(duì)共享資源的競(jìng)爭(zhēng)。為保證進(jìn)程互斥地訪問共享資源,各進(jìn)程必須互斥地進(jìn)入各自的臨界段。

            ---- 協(xié)作——進(jìn)程之間交換數(shù)據(jù)。為完成一個(gè)共同任務(wù)而同時(shí)運(yùn)行的一組進(jìn)程稱為同組進(jìn)程,它們之間必須交換數(shù)據(jù),以達(dá)到協(xié)作完成任務(wù)的目的,交換數(shù)據(jù)可以通知對(duì)方可以做某事或者委托對(duì)方做某事。

            ---- 共享CPU問題由操作系統(tǒng)的進(jìn)程調(diào)度來實(shí)現(xiàn),進(jìn)程間的競(jìng)爭(zhēng)和協(xié)作由進(jìn)程間的通信來完成。進(jìn)程間的通信一般由操作系統(tǒng)提供編程接口,由程序員在程序中實(shí)現(xiàn)。UNIX在這個(gè)方面可以說最具特色,它提供了一整套進(jìn)程間的數(shù)據(jù)共享與信息交換的處理方法——進(jìn)程通信機(jī)制(IPC)。因此,我們就以UNIX為例來分析 進(jìn)程間通信的各種實(shí)現(xiàn)技術(shù)。

            ---- 在UNIX中,文件(File)、信號(hào)(Signal)、無(wú)名管道(Unnamed Pipes)、有名管道(FIFOs)是傳統(tǒng)IPC功能;新的IPC功能包括消息隊(duì)列(Message queues)、共享存儲(chǔ)段(Shared memory segment)和信號(hào)燈(Semapores)。

            ---- (1) 信號(hào)

            ---- 信號(hào)機(jī)制是UNIX為進(jìn)程中斷處理而設(shè)置的。它只是一組預(yù)定義的值,因此不能用于信息交換,僅用于進(jìn)程中斷控制。例如在發(fā)生浮點(diǎn)錯(cuò)、非法內(nèi)存訪問、執(zhí)行無(wú) 效指令、某些按鍵(如ctrl-c、del等)等都會(huì)產(chǎn)生一個(gè)信號(hào),操作系統(tǒng)就會(huì)調(diào)用有關(guān)的系統(tǒng)調(diào)用或用戶定義的處理過程來處理。

            ---- 信號(hào)處理的系統(tǒng)調(diào)用是signal,調(diào)用形式是:

            ---- signal(signalno,action)

            ---- 其中,signalno是規(guī)定信號(hào)編號(hào)的值,action指明當(dāng)特定的信號(hào)發(fā)生時(shí)所執(zhí)行的動(dòng)作。

            ---- (2) 無(wú)名管道和有名管道

            ---- 無(wú)名管道實(shí)際上是內(nèi)存中的一個(gè)臨時(shí)存儲(chǔ)區(qū),它由系統(tǒng)安全控制,并且獨(dú)立于創(chuàng)建它的進(jìn)程的內(nèi)存區(qū)。管道對(duì)數(shù)據(jù)采用先進(jìn)先出方式管理,并嚴(yán)格按順序操作,例如不能對(duì)管道進(jìn)行搜索,管道中的信息只能讀一次。

            ---- 無(wú)名管道只能用于兩個(gè)相互協(xié)作的進(jìn)程之間的通信,并且訪問無(wú)名管道的進(jìn)程必須有共同的祖先。

            ---- 系統(tǒng)提供了許多標(biāo)準(zhǔn)管道庫(kù)函數(shù),如:

            pipe()——打開一個(gè)可以讀寫的管道;

            close()——關(guān)閉相應(yīng)的管道;

            read()——從管道中讀取字符;

            write()——向管道中寫入字符;

            ---- 有名管道的操作和無(wú)名管道類似,不同的地方在于使用有名管道的進(jìn)程不需要具有共同的祖先,其它進(jìn)程,只要知道該管道的名字,就可以訪問它。管道非常適合進(jìn)程之間快速交換信息。

            ---- (3) 消息隊(duì)列(MQ)

            ---- 消息隊(duì)列是內(nèi)存中獨(dú)立于生成它的進(jìn)程的一段存儲(chǔ)區(qū),一旦創(chuàng)建消息隊(duì)列,任何進(jìn)程,只要具有正確的的訪問權(quán)限,都可以訪問消息隊(duì)列,消息隊(duì)列非常適合于在進(jìn)程間交換短信息。

            ---- 消息隊(duì)列的每條消息由類型編號(hào)來分類,這樣接收進(jìn)程可以選擇讀取特定的消息類型——這一點(diǎn)與管道不同。消息隊(duì)列在創(chuàng)建后將一直存在,直到使用msgctl系統(tǒng)調(diào)用或iqcrm -q命令刪除它為止。

            ---- 系統(tǒng)提供了許多有關(guān)創(chuàng)建、使用和管理消息隊(duì)列的系統(tǒng)調(diào)用,如:

            ---- int msgget(key,flag)——創(chuàng)建一個(gè)具有flag權(quán)限的MQ及其相應(yīng)的結(jié)構(gòu),并返回一個(gè)唯一的正整數(shù)msqid(MQ的標(biāo)識(shí)符);

            ---- int msgsnd(msqid,msgp,msgsz,msgtyp,flag)——向隊(duì)列中發(fā)送信息;

            ---- int msgrcv(msqid,cmd,buf)——從隊(duì)列中接收信息;

            ---- int msgctl(msqid,cmd,buf)——對(duì)MQ的控制操作;

            ---- (4) 共享存儲(chǔ)段(SM)

            ---- 共享存儲(chǔ)段是主存的一部分,它由一個(gè)或多個(gè)獨(dú)立的進(jìn)程共享。各進(jìn)程的數(shù)據(jù)段與共享存儲(chǔ)段相關(guān)聯(lián),對(duì)每個(gè)進(jìn)程來說,共享存儲(chǔ)段有不同的虛擬地址。系統(tǒng)提供的有關(guān)SM的系統(tǒng)調(diào)用有:

            ---- int shmget(key,size,flag)——創(chuàng)建大小為size的SM段,其相應(yīng)的數(shù)據(jù)結(jié)構(gòu)名為key,并返回共享內(nèi)存區(qū)的標(biāo)識(shí)符shmid;

            ---- char shmat(shmid,address,flag)——將當(dāng)前進(jìn)程數(shù)據(jù)段的地址賦給shmget所返回的名為shmid的SM段;

            ---- int shmdr(address)——從進(jìn)程地址空間刪除SM段;

            ---- int shmctl (shmid,cmd,buf)——對(duì)SM的控制操作;

            ---- SM的大小只受主存限制,SM段的訪問及進(jìn)程間的信息交換可以通過同步讀寫來完成。同步通常由信號(hào)燈來實(shí)現(xiàn)。SM非常適合進(jìn)程之間大量數(shù)據(jù)的共享。

            ---- (5) 信號(hào)燈

            ---- 在UNIX中,信號(hào)燈是一組進(jìn)程共享的數(shù)據(jù)結(jié)構(gòu),當(dāng)幾個(gè)進(jìn)程競(jìng)爭(zhēng)同一資源時(shí)(文件、共享內(nèi)存或消息隊(duì)列等),它們的操作便由信號(hào)燈來同步,以防止互相干擾。

            ---- 信號(hào)燈保證了某一時(shí)刻只有一個(gè)進(jìn)程訪問某一臨界資源,所有請(qǐng)求該資源的其它進(jìn)程都將被掛起,一旦該資源得到釋放,系統(tǒng)才允許其它進(jìn)程訪問該資源。信號(hào)燈通常配對(duì)使用,以便實(shí)現(xiàn)資源的加鎖和解鎖。

            ---- 進(jìn)程間通信的實(shí)現(xiàn)技術(shù)的特點(diǎn)是:操作系統(tǒng)提供實(shí)現(xiàn)機(jī)制和編程接口,由用戶在程序中實(shí)現(xiàn),保證進(jìn)程間可以進(jìn)行快速的信息交換和大量數(shù)據(jù)的共享。但是,上述方式主要適合在同一臺(tái)計(jì)算機(jī)系統(tǒng)內(nèi)部的進(jìn)程之間的通信。

            應(yīng)用程序間的通信及其實(shí)現(xiàn)技術(shù)

            ---- 同進(jìn)程之間的相互制約一樣,不同的應(yīng)用程序之間也存在競(jìng)爭(zhēng)和協(xié)作的關(guān)系。UNIX操作系統(tǒng)也提供一些可用于應(yīng)用程序之間實(shí)現(xiàn)數(shù)據(jù)共享與信息交換的編程接 口,程序員可以通過自己編程來實(shí)現(xiàn)。如遠(yuǎn)程過程調(diào)用和基于TCP/IP協(xié)議的套接字(Socket)編程。但是,相對(duì)普通程序員來說,它們涉及的技術(shù)比較 深,編程也比較復(fù)雜,實(shí)現(xiàn)起來困難較大。

            ---- 于是,一種新的技術(shù)應(yīng)運(yùn)而生——通過將有關(guān)通信的細(xì)節(jié)完全掩蓋在某個(gè)獨(dú)立軟件內(nèi)部,即底層的通訊工作和相應(yīng)的維護(hù)管理工作由該軟件內(nèi)部來實(shí)現(xiàn),用戶只需要將通信任務(wù)提交給該軟件去完成,而不必理會(huì)它的具體工作過程——這就是所謂的中間件技術(shù)。

            ---- 我們?cè)谶@里分別討論這三種常用的應(yīng)用程序間通信的實(shí)現(xiàn)技術(shù)——遠(yuǎn)程過程調(diào)用、會(huì)話編程技術(shù)和MQSeries消息隊(duì)列技術(shù)。其中遠(yuǎn)程過程調(diào)用和會(huì)話編程屬 于比較低級(jí)的方式,程序員參與的程度較深,而MQSeries消息隊(duì)列則屬于比較高級(jí)的方式,即中間件方式,程序員參與的程度較淺。

            ---- 3.1 遠(yuǎn)程過程調(diào)用(RPC)

            ---- 遠(yuǎn)程過程調(diào)用是按下述方式工作的:當(dāng)一個(gè)應(yīng)用程序A需要與遠(yuǎn)程的另一個(gè)應(yīng)用程序B交換信息或要求B提供協(xié)助時(shí),A將在本地產(chǎn)生一個(gè)請(qǐng)求,通過通訊鏈路,通知B接收信息或提供相應(yīng)的服務(wù),B完成相關(guān)處理后將確認(rèn)信息或結(jié)果返回給A。

            ---- RPC機(jī)制強(qiáng)調(diào)通信的兩個(gè)應(yīng)用程序所處的環(huán)境和平臺(tái)中必須是相同的,而且必須同時(shí)處于運(yùn)行狀態(tài)。做遠(yuǎn)程調(diào)用時(shí),兩者必須先建立連接,而且通訊鏈路質(zhì)量對(duì)它的效果影響很大。

            ---- RPC的優(yōu)點(diǎn)是應(yīng)用程序采用調(diào)用/返回方式通訊,擁有很高的潛在效率,但需要應(yīng)用程序間的緊密藕合,通訊線路必須在通信期間一直保持良好的狀態(tài),而且必須進(jìn)行大量的底層通訊的編程工作。

            ---- 3.2 會(huì)話編程

            ---- 會(huì)話編程類似于人們打電話,撥號(hào)——接通——說話——對(duì)方回答——掛機(jī)。基于TCP/IP協(xié)議的Socket編程就是一種典型的會(huì)話編程方式。它可適用于客戶/服務(wù)通信方式,還能適用于點(diǎn)——點(diǎn)通信方式。

            ---- 下面,我們分別介紹服務(wù)器端和客戶端的具體任務(wù)。

            ---- 服務(wù)器端

            ---- 服務(wù)進(jìn)程首先創(chuàng)建一個(gè)套接口,使用Socket()調(diào)用;然后,將該套接口與本機(jī)的IP地址和某一空閑端口相關(guān)聯(lián),使用Bind()調(diào)用;這時(shí),服務(wù)端就 可以用Listen()調(diào)用來偵聽來自客戶程序的數(shù)據(jù);套接口一旦處于聽模式,服務(wù)進(jìn)程將可以接收一個(gè)連接,并允許傳遞數(shù)據(jù),使用Accept()調(diào)用來 完成;最后使用Read()調(diào)用來讀入數(shù)據(jù),同時(shí),還可以用Write()調(diào)用來向發(fā)送進(jìn)程寫回一些數(shù)據(jù),如確認(rèn)信息或回顯信息。

            客戶端

            ---- 客戶進(jìn)程也是首先創(chuàng)建一個(gè)套接口,使用Socket()調(diào)用;然后,客戶進(jìn)程就使用Connect()調(diào)用試圖連接一個(gè)服務(wù);連接成功之后,就可以利用Write()調(diào)用向服務(wù)器發(fā)送數(shù)據(jù),同時(shí),還可以使用Read()調(diào)用讀取服務(wù)器寫回的數(shù)據(jù)。

            ---- 目前的網(wǎng)絡(luò)一般都支持TCP/IP協(xié)議,UNIX和WINDOWS也都提供相應(yīng)的編程接口,用戶可以隨心所欲地編制出合乎自己要求的通信程序。現(xiàn)行大多數(shù) 的應(yīng)用程序間的通信采取的就是這種方式。但是,這種Socket編程技術(shù),要求程序員必須熟悉相關(guān)概念,自己設(shè)計(jì)控制流程,客戶和服務(wù)進(jìn)程必須相互配合且 必須都處于運(yùn)行狀態(tài),技術(shù)上有一定的難度。

            ---- 3.3 MQSeries消息隊(duì)列

            ---- 為了簡(jiǎn)化應(yīng)用程序間的通信,使得通信既具有較高的可靠性,又保證實(shí)現(xiàn)的簡(jiǎn)單性,我們希望能有一種獨(dú)立的通信軟件,應(yīng)用程序只需將任務(wù)提交給該軟件,由該軟 件自動(dòng)去完成信息的傳遞工作,這即是我們前面提到的中間件技術(shù)。IBM公司的MQSeries就是基于這種技術(shù)的商業(yè)化產(chǎn)品。

            ---- 應(yīng)用程序A和B位于同一計(jì)算機(jī),而應(yīng)用程序C位于遠(yuǎn)程的其它計(jì)算機(jī)系統(tǒng)中。當(dāng)應(yīng)用程序A需要和B通訊時(shí),它通過調(diào)用MQSeries接口將消息放入隊(duì)列 Q1,應(yīng)用程序B在適當(dāng)?shù)臅r(shí)候讀取該消息,或消息本身到達(dá)后喚醒應(yīng)用程序B。當(dāng)應(yīng)用程序A需要和C通訊時(shí),它通過相同的方式將消息放入隊(duì)列Q2,應(yīng)用程序 C在適當(dāng)?shù)臅r(shí)候讀取該消息。

            ---- 應(yīng)用程序之間的消息傳遞是通過隊(duì)列來實(shí)現(xiàn)的,是間接的。由于不存在直接連接,C關(guān)閉時(shí)A仍然能正常運(yùn)行,不僅如此,當(dāng)C不在運(yùn)行時(shí),消息還可以觸發(fā)該程序。

            ---- MQSeries優(yōu)點(diǎn)可以確保信息是永久的、可恢復(fù)的;確保信息成功發(fā)送且僅有一次發(fā)送,可以支持關(guān)鍵業(yè)務(wù),如證券交易信息的傳遞;確保信息傳遞是保密 的;同時(shí),使用MQSeries,不需要應(yīng)用程序和通訊介質(zhì)以及遠(yuǎn)程應(yīng)用程序之間的耦合,也不需要應(yīng)用程序同時(shí)運(yùn)行。MQSeries是應(yīng)用程序間通信的 首選技術(shù)。

            ---- MQSeries接口提供的調(diào)用主要有:

            ---- MQCONN——連接一個(gè)隊(duì)列管理器,以后它發(fā)送和讀入的消息的所有消息都由這個(gè)隊(duì)列管理器管理;

            ---- MQOPEN——打開該應(yīng)用程序所連接的隊(duì)列;

            ---- MQPUT——將消息寫入已打開的隊(duì)列中;

            ---- MQGET——從該隊(duì)列中讀出消息;

            ---- MQINQ——獲得關(guān)于隊(duì)列的屬性;

            ---- MQCLOSE——關(guān)閉隊(duì)列(對(duì)隊(duì)列執(zhí)行完所有操作后);

            ---- MQPUT1——它執(zhí)行三個(gè)操作,先調(diào)用MQOPEN打開隊(duì)列,然后調(diào)用MQPUT寫入一條消息,最后調(diào)用MQCLOSE關(guān)閉隊(duì)列;

            ---- MQDISC——斷開和隊(duì)列管理器的連接(對(duì)隊(duì)列管理器的所有操作完成后);

            ---- 3.4 三種實(shí)現(xiàn)技術(shù)的特性比較

            表1清楚地列出了RPC、 Socket編程、MQSeries的不同特性。

            比較項(xiàng)目

            Socket編程

            RPC

            MQSeries

            屬性

            會(huì)話

            遠(yuǎn)程調(diào)用

            消息隊(duì)列

            類型

            會(huì)話

            調(diào)用/返回

            隊(duì)列

            編程接口

            非阻塞

            阻塞

            非阻塞

            通信對(duì)方運(yùn)行

            應(yīng)用程序類型

            面向連接

            面向連接

            無(wú)連接

            數(shù)據(jù)流模式

            點(diǎn)-點(diǎn),客戶機(jī)/服務(wù)器

            客戶機(jī)/服務(wù)器

            所有模式

            邏輯路由

            永久數(shù)據(jù)

            表1 三種中間件的特性比較

            結(jié)束語(yǔ)

            ---- 各種進(jìn)程間通信和應(yīng)用程序間通信的實(shí)現(xiàn)技術(shù)都具有自己的特點(diǎn)和使用范圍。管道、消息隊(duì)列、共享內(nèi)存等技術(shù)最適用于同一計(jì)算機(jī)系統(tǒng)內(nèi)部的進(jìn)程間通信,以保證 高效率。而遠(yuǎn)程過程調(diào)用、Socket會(huì)話編程、MQSeries則最適用于遠(yuǎn)程的應(yīng)用程序之間通信,可以簡(jiǎn)化通信的編程,當(dāng)然也保證通信的可靠性。尤其 是MQSeries,它是一個(gè)比較完善的中間件產(chǎn)品,為許多的信息系統(tǒng)所選用。如我公司的帳務(wù)系統(tǒng)與各金融系統(tǒng)的話費(fèi)信息的交換選擇的就是 MQSeries。有時(shí),在一個(gè)信息系統(tǒng)里面,既存在進(jìn)程間通信的需求,也存在應(yīng)用程序間通信的需求,這時(shí)就必須分別選擇兩種不同的實(shí)現(xiàn)技術(shù)。因此,在實(shí) 際信息系統(tǒng)建設(shè)的過程中,我們?cè)谶x擇哪種實(shí)現(xiàn)技術(shù)時(shí),應(yīng)根據(jù)信息系統(tǒng)的不同情況和不同需求,根據(jù)系統(tǒng)開發(fā)和維護(hù)的成本,選擇一種或是幾種實(shí)現(xiàn)技術(shù),以求得 整個(gè)系統(tǒng)的優(yōu)化。

            -

             

             

             

            Linux環(huán)境進(jìn)程間通信: 共享內(nèi)存

            采用共享內(nèi)存通信的一個(gè)顯而易見的好處是效率高,因?yàn)檫M(jìn)程可以直接讀寫內(nèi)存,而不需要任何數(shù)據(jù)的拷貝。對(duì)于像管道和消息隊(duì)列等通信方式,則需要在內(nèi)核和用戶空間進(jìn)行四次的數(shù)據(jù)拷貝,而共享內(nèi)存則只拷貝兩次數(shù)據(jù)[1]:一次從輸入文件到共享內(nèi)存區(qū),另一次從共享內(nèi)存區(qū)到輸出文件。實(shí)際上,進(jìn)程之間在共享內(nèi)存時(shí),并不總是讀寫少量數(shù)據(jù)后就解除映射,有新的通信時(shí),再重新建立共享內(nèi)存區(qū)域。而是保持共享區(qū)域,直到通信完畢為止,這樣,數(shù)據(jù)內(nèi)容一直保存在共享內(nèi)存中,并沒有寫回文件。共享內(nèi)存中的內(nèi)容往往是在解除映射時(shí)才寫回文件的。因此,采用共享內(nèi)存的通信方式效率是非常高的。

            Linux的2.2.x內(nèi)核支持多種共享內(nèi)存方式,如mmap()系統(tǒng)調(diào)用,Posix共享內(nèi)存,以及系統(tǒng)V共享內(nèi)存。linux發(fā)行版本如Redhat 8.0支持mmap()系統(tǒng)調(diào)用及系統(tǒng)V共享內(nèi)存,但還沒實(shí)現(xiàn)Posix共享內(nèi)存,本文將主要介紹mmap()系統(tǒng)調(diào)用及系統(tǒng)V共享內(nèi)存API的原理及應(yīng)用。

            一、內(nèi)核怎樣保證各個(gè)進(jìn)程尋址到同一個(gè)共享內(nèi)存區(qū)域的內(nèi)存頁(yè)面

            1、page cache及swap cache中頁(yè)面的區(qū)分:

            一個(gè)被訪問文件的物理頁(yè)面都駐留在page cache或swap cache中,一個(gè)頁(yè)面的所有信息由struct page來描述。struct page中有一個(gè)域?yàn)橹羔榤apping ,它指向一個(gè)struct address_space類型結(jié)構(gòu)。page cache或swap cache中的所有頁(yè)面就是根據(jù)address_space結(jié)構(gòu)以及一個(gè)偏移量來區(qū)分的。

            2、文件與address_space結(jié)構(gòu)的對(duì)應(yīng):

            一個(gè)具體的文件在打開后,內(nèi)核會(huì)在內(nèi)存中為之建立一個(gè)struct inode結(jié)構(gòu),其中的i_mapping域指向一個(gè)address_space結(jié)構(gòu)。這樣,一個(gè)文件就對(duì)應(yīng)一個(gè)address_space結(jié)構(gòu),一個(gè)address_space與一個(gè)偏移量能夠確定一個(gè)page cache 或swap cache中的一個(gè)頁(yè)面。因此,當(dāng)要尋址某個(gè)數(shù)據(jù)時(shí),很容易根據(jù)給定的文件及數(shù)據(jù)在文件內(nèi)的偏移量而找到相應(yīng)的頁(yè)面。

            3、進(jìn)程調(diào)用mmap():

            此時(shí),只是在進(jìn)程空間內(nèi)新增了一塊相應(yīng)大小的緩沖區(qū),并設(shè)置了相應(yīng)的訪問標(biāo)識(shí),但并沒有建立進(jìn)程空間到物理頁(yè)面的映射。因此,第一次訪問該空間時(shí),會(huì)引發(fā)一個(gè)缺頁(yè)異常。

            4、對(duì)于共享內(nèi)存映射情況:

            缺頁(yè)異常處理程序首先在swap cache中尋找目標(biāo)頁(yè)(符合address_space以及偏移量的物理頁(yè)),如果找到,則直接返回地址;如果沒有找到,則判斷該頁(yè)是否在交換區(qū)(swap area),如果在,則執(zhí)行一個(gè)換入操作;如果上述兩種情況都不滿足,處理程序?qū)⒎峙湫碌奈锢眄?yè)面,并把它插入到page cache中。進(jìn)程最終將更新進(jìn)程頁(yè)表。 注:對(duì)于映射普通文件情況(非共享映射),缺頁(yè)異常處理程序首先會(huì)在page cache中根據(jù)address_space以及數(shù)據(jù)偏移量尋找相應(yīng)的頁(yè)面。如果沒有找到,則說明文件數(shù)據(jù)還沒有讀入內(nèi)存,處理程序會(huì)從磁盤讀入相應(yīng)的頁(yè)面,并返回相應(yīng)地址,同時(shí),進(jìn)程頁(yè)表也會(huì)更新。

            5、所有進(jìn)程在映射同一個(gè)共享內(nèi)存區(qū)域時(shí):

            情況都一樣,在建立線性地址與物理地址之間的映射之后,不論進(jìn)程各自的返回地址如何,實(shí)際訪問的必然是同一個(gè)共享內(nèi)存區(qū)域?qū)?yīng)的物理頁(yè)面。 注:一個(gè)共享內(nèi)存區(qū)域可以看作是特殊文件系統(tǒng)shm中的一個(gè)文件,shm的安裝點(diǎn)在交換區(qū)上。

            上面涉及到了一些數(shù)據(jù)結(jié)構(gòu),圍繞數(shù)據(jù)結(jié)構(gòu)理解問題會(huì)容易一些。

            二、mmap()及其相關(guān)系統(tǒng)調(diào)用

            mmap()系統(tǒng)調(diào)用使得進(jìn)程之間通過映射同一個(gè)普通文件實(shí)現(xiàn)共享內(nèi)存。普通文件被映射到進(jìn)程地址空間后,進(jìn)程可以向訪問普通內(nèi)存一樣對(duì)文件進(jìn)行訪問,不必再調(diào)用read(),write()等操作。

            注:實(shí)際上,mmap()系統(tǒng)調(diào)用并不是完全為了用于共享內(nèi)存而設(shè)計(jì)的。它本身提供了不同于一般對(duì)普通文件的訪問方式,進(jìn)程可以像讀寫內(nèi)存一樣對(duì)普通文件的操作。而Posix或系統(tǒng)V的共享內(nèi)存IPC則純粹用于共享目的,當(dāng)然mmap()實(shí)現(xiàn)共享內(nèi)存也是其主要應(yīng)用之一。

            1、mmap()系統(tǒng)調(diào)用形式如下:

            void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset )

             參數(shù)fd為即將映射到進(jìn)程空間的文件描述字,一般由open()返回,同時(shí),fd可以指定為-1,此時(shí)須指定flags參數(shù)中的MAP_ANON,表明進(jìn)行的是匿名映射(不涉及具體的文件名,避免了文件的創(chuàng)建及打開,很顯然只能用于具有親緣關(guān)系的進(jìn)程間通信)。len是映射到調(diào)用進(jìn)程地址空間的字節(jié)數(shù),它從被映射文件開頭offset個(gè)字節(jié)開始算起。prot 參數(shù)指定共享內(nèi)存的訪問權(quán)限。可取如下幾個(gè)值的或:PROT_READ(可讀) , PROT_WRITE (可寫), PROT_EXEC (可執(zhí)行), PROT_NONE(不可訪問)。flags由以下幾個(gè)常值指定:MAP_SHARED , MAP_PRIVATE , MAP_FIXED,其中,MAP_SHARED , MAP_PRIVATE必選其一,而MAP_FIXED則不推薦使用。offset參數(shù)一般設(shè)為0,表示從文件頭開始映射。參數(shù)addr指定文件應(yīng)被映射到進(jìn)程空間的起始地址,一般被指定一個(gè)空指針,此時(shí)選擇起始地址的任務(wù)留給內(nèi)核來完成。函數(shù)的返回值為最后文件映射到進(jìn)程空間的地址,進(jìn)程可直接操作起始地址為該值的有效地址。這里不再詳細(xì)介紹mmap()的參數(shù),讀者可參考mmap()手冊(cè)頁(yè)獲得進(jìn)一步的信息。

            2、系統(tǒng)調(diào)用mmap()用于共享內(nèi)存的兩種方式:

                         (1)使用普通文件提供的內(nèi)存映射:適用于任何進(jìn)程之間;此時(shí),需要打開或創(chuàng)建一個(gè)文件,然后再調(diào)用mmap();典型調(diào)用代碼如下:

                         ptr=mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0); 通過mmap()實(shí)現(xiàn)共享內(nèi)存的通信方式有許多特點(diǎn)和要注意的地方,我們將在范例中進(jìn)行具體說明。

            2         使用特殊文件提供匿名內(nèi)存映射:適用于具有親緣關(guān)系的進(jìn)程之間;由于父子進(jìn)程特殊的親緣關(guān)系,在父進(jìn)程中先調(diào)用mmap(),然后調(diào)用fork()。那么在調(diào)用fork()之后,子進(jìn)程繼承父進(jìn)程匿名映射后的地址空間,同樣也繼承mmap()返回的地址,這樣,父子進(jìn)程就可以通過映射區(qū)域進(jìn)行通信了。注意,這里不是一般的繼承關(guān)系。一般來說,子進(jìn)程單獨(dú)維護(hù)從父進(jìn)程繼承下來的一些變量。而mmap()返回的地址,卻由父子進(jìn)程共同維護(hù)。 對(duì)于具有親緣關(guān)系的進(jìn)程實(shí)現(xiàn)共享內(nèi)存最好的方式應(yīng)該是采用匿名內(nèi)存映射的方式。此時(shí),不必指定具體的文件,只要設(shè)置相應(yīng)的標(biāo)志即可,參見范例2。

             

            fd=open(name, flag, mode);

            if(fd<0)

            ...

            3、系統(tǒng)調(diào)用munmap()

            int munmap( void * addr, size_t len ) 該調(diào)用在進(jìn)程地址空間中解除一個(gè)映射關(guān)系,addr是調(diào)用mmap()時(shí)返回的地址,len是映射區(qū)的大小。當(dāng)映射關(guān)系解除后,對(duì)原來映射地址的訪問將導(dǎo)致段錯(cuò)誤發(fā)生。

            4、系統(tǒng)調(diào)用msync()

            int msync ( void * addr , size_t len, int flags) 一般說來,進(jìn)程在映射空間的對(duì)共享內(nèi)容的改變并不直接寫回到磁盤文件中,往往在調(diào)用munmap()后才執(zhí)行該操作。可以通過調(diào)用msync()實(shí)現(xiàn)磁盤上文件內(nèi)容與共享內(nèi)存區(qū)的內(nèi)容一致。

            三、mmap()范例

            下面將給出使用mmap()的兩個(gè)范例:范例1給出兩個(gè)進(jìn)程通過映射普通文件實(shí)現(xiàn)共享內(nèi)存通信;范例2給出父子進(jìn)程通過匿名映射實(shí)現(xiàn)共享內(nèi)存。系統(tǒng)調(diào)用mmap()有許多有趣的地方,下面是通過mmap()映射普通文件實(shí)現(xiàn)進(jìn)程間的通信的范例,我們通過該范例來說明mmap()實(shí)現(xiàn)共享內(nèi)存的特點(diǎn)及注意事項(xiàng)。

            范例1:兩個(gè)進(jìn)程通過映射普通文件實(shí)現(xiàn)共享內(nèi)存通信

            范例1包含兩個(gè)子程序:map_normalfile1.c及map_normalfile2.c。編譯兩個(gè)程序,可執(zhí)行文件分別為map_normalfile1及map_normalfile2。兩個(gè)程序通過命令行參數(shù)指定同一個(gè)文件來實(shí)現(xiàn)共享內(nèi)存方式的進(jìn)程間通信。map_normalfile2試圖打開命令行參數(shù)指定的一個(gè)普通文件,把該文件映射到進(jìn)程的地址空間,并對(duì)映射后的地址空間進(jìn)行寫操作。map_normalfile1把命令行參數(shù)指定的文件映射到進(jìn)程地址空間,然后對(duì)映射后的地址空間執(zhí)行讀操作。這樣,兩個(gè)進(jìn)程通過命令行參數(shù)指定同一個(gè)文件來實(shí)現(xiàn)共享內(nèi)存方式的進(jìn)程間通信。

            下面是兩個(gè)程序代碼:

            /*-------------map_normalfile2.c-----------*/

            #include <sys/mman.h>

            #include <sys/types.h>

            #include <fcntl.h>

            #include <unistd.h>

            typedef struct{

            char name[4];

            int age;

            }people;

             

            main(int argc, char** argv) // map a normal file as shared mem:

            {

            int fd,i;

            people *p_map;

            char temp;

            fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);

            lseek(fd,sizeof(people)*5-1,SEEK_SET);

            write(fd,"",1);

            p_map = (people*) mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );

            close( fd );

            temp = 'a';

            for(i=0; i<10; i++)

            {

                 temp += 1;

                 memcpy( ( *(p_map+i) ).name, &temp,2 );

                 ( *(p_map+i) ).age = 20+i;

            }

            printf(" initialize over /n ");

            sleep(10);

            munmap( p_map, sizeof(people)*10 );

            printf( "umap ok /n" );

            }

            /*-------------map_normalfile2.c-----------*/

            #include <sys/mman.h>

            #include <sys/types.h>

            #include <fcntl.h>

            #include <unistd.h>

            typedef struct{

            char name[4];

            int age;

            }people;

             

            main(int argc, char** argv) // map a normal file as shared mem:

            {

            int fd,i;

            people *p_map;

            fd=open( argv[1],O_CREAT|O_RDWR,00777 );

            p_map = (people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);

            for(i = 0;i<10;i++)

            {

                 printf( "name: %s age %d;/n",(*(p_map+i)).name, (*(p_map+i)).age );

            }

            munmap( p_map,sizeof(people)*10 );

            }

            map_normalfile1.c首先定義了一個(gè)people數(shù)據(jù)結(jié)構(gòu),(在這里采用數(shù)據(jù)結(jié)構(gòu)的方式是因?yàn)椋蚕韮?nèi)存區(qū)的數(shù)據(jù)往往是有固定格式的,這由通信的各個(gè)進(jìn)程決定,采用結(jié)構(gòu)的方式有普遍代表性)。map_normfile1首先打開或創(chuàng)建一個(gè)文件,并把文件的長(zhǎng)度設(shè)置為5個(gè)people結(jié)構(gòu)大小。然后從mmap()的返回地址開始,設(shè)置了10個(gè)people結(jié)構(gòu)。然后,進(jìn)程睡眠10秒鐘,等待其他進(jìn)程映射同一個(gè)文件,最后解除映射。

            map_normfile2.c只是簡(jiǎn)單的映射一個(gè)文件,并以people數(shù)據(jù)結(jié)構(gòu)的格式從mmap()返回的地址處讀取10個(gè)people結(jié)構(gòu),并輸出讀取的值,然后解除映射。

            分別把兩個(gè)程序編譯成可執(zhí)行文件map_normalfile1和map_normalfile2后,在一個(gè)終端上先運(yùn)行./map_normalfile2 /tmp/test_shm,程序輸出結(jié)果如下:

            initialize over

            umap ok

             

            在map_normalfile1輸出initialize over 之后,輸出umap ok之前,在另一個(gè)終端上運(yùn)行map_normalfile2 /tmp/test_shm,將會(huì)產(chǎn)生如下輸出(為了節(jié)省空間,輸出結(jié)果為稍作整理后的結(jié)果):

            name: b age 20; name: c age 21; name: d age 22; name: e age 23; name: f age 24;

            name: g age 25; name: h age 26; name: I age 27; name: j age 28; name: k age 29;

            在map_normalfile1 輸出umap ok后,運(yùn)行map_normalfile2則輸出如下結(jié)果:

            name: b age 20; name: c age 21; name: d age 22; name: e age 23; name: f age 24;

            name: age 0; name: age 0; name: age 0; name: age 0; name: age 0;

             

            從程序的運(yùn)行結(jié)果中可以得出的結(jié)論

            1、 最終被映射文件的內(nèi)容的長(zhǎng)度不會(huì)超過文件本身的初始大小,即映射不能改變文件的大小;

            2、 可以用于進(jìn)程通信的有效地址空間大小大體上受限于被映射文件的大小,但不完全受限于文件大小。打開文件被截短為5個(gè)people結(jié)構(gòu)大小,而在map_normalfile1中初始化了10個(gè)people數(shù)據(jù)結(jié)構(gòu),在恰當(dāng)時(shí)候(map_normalfile1輸出initialize over 之后,輸出umap ok之前)調(diào)用map_normalfile2會(huì)發(fā)現(xiàn)map_normalfile2將輸出全部10個(gè)people結(jié)構(gòu)的值,后面將給出詳細(xì)討論。 注:在linux中,內(nèi)存的保護(hù)是以頁(yè)為基本單位的,即使被映射文件只有一個(gè)字節(jié)大小,內(nèi)核也會(huì)為映射分配一個(gè)頁(yè)面大小的內(nèi)存。當(dāng)被映射文件小于一個(gè)頁(yè)面大小時(shí),進(jìn)程可以對(duì)從mmap()返回地址開始的一個(gè)頁(yè)面大小進(jìn)行訪問,而不會(huì)出錯(cuò);但是,如果對(duì)一個(gè)頁(yè)面以外的地址空間進(jìn)行訪問,則導(dǎo)致錯(cuò)誤發(fā)生,后面將進(jìn)一步描述。因此,可用于進(jìn)程間通信的有效地址空間大小不會(huì)超過文件大小及一個(gè)頁(yè)面大小的和。

            3、 文件一旦被映射后,調(diào)用mmap()的進(jìn)程對(duì)返回地址的訪問是對(duì)某一內(nèi)存區(qū)域的訪問,暫時(shí)脫離了磁盤上文件的影響。所有對(duì)mmap()返回地址空間的操作只在內(nèi)存中有意義,只有在調(diào)用了munmap()后或者msync()時(shí),才把內(nèi)存中的相應(yīng)內(nèi)容寫回磁盤文件,所寫內(nèi)容仍然不能超過文件的大小。

            范例2:父子進(jìn)程通過匿名映射實(shí)現(xiàn)共享內(nèi)存

             

            #include <sys/mman.h>

            #include <sys/types.h>

            #include <fcntl.h>

            #include <unistd.h>

            typedef struct{

            char name[4];

            int age;

            }people;

             

            main(int argc, char** argv)

            {

            int i;

            people *p_map;

            char temp;

            p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);

            if(fork() == 0)

            {

                 sleep(2);

                 for(i = 0;i<5;i++)

                      printf("child read: the %d people's age is %d/n",i+1,(*(p_map+i)).age);

                 (*p_map).age = 100;

                 munmap(p_map,sizeof(people)*10); //實(shí)際上,進(jìn)程終止時(shí),會(huì)自動(dòng)解除映射。

                 exit();

            }

             

            temp = 'a';

            for(i = 0;i<5;i++)

            {

                 temp += 1;

                 memcpy((*(p_map+i)).name, &temp,2);

                 (*(p_map+i)).age=20+i;

            }

            sleep(5);

            printf( "parent read: the first people,s age is %d/n",(*p_map).age );

            printf("umap/n");

            munmap( p_map,sizeof(people)*10 );

            printf( "umap ok/n" );

            }

             

             

            考察程序的輸出結(jié)果,體會(huì)父子進(jìn)程匿名共享內(nèi)存:

            child read: the 1 people's age is 20

            child read: the 2 people's age is 21

            child read: the 3 people's age is 22

            child read: the 4 people's age is 23

            child read: the 5 people's age is 24

            parent read: the first people,s age is 100

            umap

            umap ok

             

            四、對(duì)mmap()返回地址的訪問

            前面對(duì)范例運(yùn)行結(jié)構(gòu)的討論中已經(jīng)提到,linux采用的是頁(yè)式管理機(jī)制。對(duì)于用mmap()映射普通文件來說,進(jìn)程會(huì)在自己的地址空間新增一塊空間,空間大小由mmap()的len參數(shù)指定,注意,進(jìn)程并不一定能夠?qū)θ啃略隹臻g都能進(jìn)行有效訪問。進(jìn)程能夠訪問的有效地址大小取決于文件被映射部分d Settings/dangxin/My Documents/My Pictures/jincheng.JPG的大小。簡(jiǎn)單的說,能夠容納文件被映射部分大小的最少頁(yè)面?zhèn)€數(shù)決定了進(jìn)程從mmap()返回的地址開始,能夠有效訪問的地址空間大小。超過這個(gè)空間大小,內(nèi)核會(huì)根據(jù)超過的嚴(yán)重程度返回發(fā)送不同的信號(hào)給進(jìn)程。可用如下圖示說明:

            注意:文件被映射部分而不是整個(gè)文件決定了進(jìn)程能夠訪問的空間大小,另外,如果指定文件的偏移部分,一定要注意為頁(yè)面大小的整數(shù)倍。下面是對(duì)進(jìn)程映射地址空間的訪問范例:

            #include <sys/mman.h>

            #include <sys/types.h>

            #include <fcntl.h>

            #include <unistd.h>

            typedef struct{

            char name[4];

            int age;

            }people;

             

            main(int argc, char** argv)

            {

            int fd,i;

            int pagesize,offset;

            people *p_map;

            pagesize = sysconf(_SC_PAGESIZE);

            printf("pagesize is %d/n",pagesize);

            fd = open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);

            lseek(fd,pagesize*2-100,SEEK_SET);

            write(fd,"",1);

            offset = 0;

            //此處offset = 0編譯成版本;offset = pagesize編譯成版本

            p_map = (people*)mmap(NULL,pagesize*3,PROT_READ|PROT_WRITE,MAP_SHARED,fd,offset);

            close(fd);

            for(i = 1; i<10; i++)

            {

                 (*(p_map+pagesize/sizeof(people)*i-2)).age = 100;

                 printf("access page %d over/n",i);

                 (*(p_map+pagesize/sizeof(people)*i-1)).age = 100;

                 printf("access page %d edge over, now begin to access page %d/n",i, i+1);

                 (*(p_map+pagesize/sizeof(people)*i)).age = 100;

                 printf("access page %d over/n",i+1);

            }

            munmap(p_map,sizeof(people)*10);

            }

             

            如程序中所注釋的那樣,把程序編譯成兩個(gè)版本,兩個(gè)版本主要體現(xiàn)在文件被映射部分的大小不同。文件的大小介于一個(gè)頁(yè)面與兩個(gè)頁(yè)面之間(大小為:pagesize*2-99),版本1的被映射部分是整個(gè)文件,版本2的文件被映射部分是文件大小減去一個(gè)頁(yè)面后的剩余部分,不到一個(gè)頁(yè)面大小(大小為:pagesize-99)。程序中試圖訪問每一個(gè)頁(yè)面邊界,兩個(gè)版本都試圖在進(jìn)程空間中映射pagesize*3的字節(jié)數(shù)。

             

            版本1的輸出結(jié)果如下:

            pagesize is 4096

            access page 1 over

            access page 1 edge over, now begin to access page 2

            access page 2 over

            access page 2 over

            access page 2 edge over, now begin to access page 3

            Bus error //被映射文件在進(jìn)程空間中覆蓋了兩個(gè)頁(yè)面,此時(shí),進(jìn)程試圖訪問第三個(gè)頁(yè)面

             

            版本2的輸出結(jié)果如下:

            pagesize is 4096

            access page 1 over

            access page 1 edge over, now begin to access page 2

            Bus error  //被映射文件在進(jìn)程空間中覆蓋了一個(gè)頁(yè)面,此時(shí),進(jìn)程試圖訪問第二個(gè)頁(yè)面

            結(jié)論:采用系統(tǒng)調(diào)用mmap()實(shí)現(xiàn)進(jìn)程間通信是很方便的,在應(yīng)用層上接口非常簡(jiǎn)潔。內(nèi)部實(shí)現(xiàn)機(jī)制區(qū)涉及到了linux存儲(chǔ)管理以及文件系統(tǒng)等方面的內(nèi)容,可以參考一下相關(guān)重要數(shù)據(jù)結(jié)構(gòu)來加深理解。


            轉(zhuǎn)自:http://blog.csdn.net/dancing999/article/details/2042473





            posted on 2011-08-03 13:32 老馬驛站 閱讀(13846) 評(píng)論(0)  編輯 收藏 引用 所屬分類: linux
            久久国产免费| 久久夜色精品国产亚洲av| 精品一区二区久久久久久久网站| 丰满少妇人妻久久久久久| 久久国产V一级毛多内射| 久久成人小视频| 精品午夜久久福利大片| 伊人久久大香线蕉无码麻豆| 精品久久8x国产免费观看| 久久精品免费网站网| 久久人人爽人人爽人人AV东京热| 久久精品国产一区二区三区| 国产精品一久久香蕉国产线看观看 | 久久综合狠狠综合久久| 久久精品国产一区二区电影| 国内精品久久久久伊人av| 亚洲美日韩Av中文字幕无码久久久妻妇| 久久精品亚洲中文字幕无码麻豆 | 99久久99久久精品国产片| 久久99久久99精品免视看动漫| 国产L精品国产亚洲区久久| 囯产极品美女高潮无套久久久| 久久丝袜精品中文字幕| 久久精品国产半推半就| 国内精品久久久人妻中文字幕| 伊人久久国产免费观看视频| 精品久久久久久久久久久久久久久 | 国产综合精品久久亚洲| 久久精品www| 国产精品久久免费| 久久久久女人精品毛片| 久久精品久久久久观看99水蜜桃| 久久亚洲2019中文字幕| 久久国产V一级毛多内射| 国产—久久香蕉国产线看观看| 久久―日本道色综合久久| 久久免费小视频| 国产精自产拍久久久久久蜜| 精品久久久久久无码免费| 久久天天日天天操综合伊人av| 欧美色综合久久久久久|