UNIX高級編程總結-----進程間通信(XSI IPC 總述)

        在 XSI IPC 中,有三種IPC:消息隊列、信號量 以及 共享內存,他們之間有許多相似之處。

        每個進程各自有不同的用戶地址空間,任何一個進程的全局變量在另一個進程中都看不到,所以進程之間要交換數據必須通過內核,在內核中開闢一塊緩衝區,進程A把數據從用戶空間拷到內核緩衝區,進程B再從內核緩衝區把數據讀走,內核提供的這種機制稱爲進程間通信。

1、標識符 和 鍵

(1)key  鍵

        每個內核中的IPC結構(消息隊列、信號量和共享內存)都用了一個非負數的標識符。例如,如果要向一個消息隊列發送消息或者從一個消息隊列取消息,只需要知道其隊列標識符。與文件描述符不同,IPC標識符不是最小整數,當一個IPC結構被創建,然後又被刪除,與這種結構相關的標識符連續加1,直至達到一整數的最大值,然後又迴轉到0。

        標識符是IPC對象的內部名,爲使多個合作進程能夠在同一IPC對象上匯聚,需要提供一個外部命名方案,爲此,每個IPC對象都與一個key相關聯,這個key就是該對象的外部名。

        對於key值,應用程序有如下三種選擇:
①    調用ftok,給它傳遞pathname和proj_id,操作系統根據兩者合成key值。
②    指定key爲IPC_PRIVATE,內核保證創建一個新的、唯一的IPC對象,IPC標識符與內存中的標識符不會衝突。IPC_PRIVATE爲宏定義,其值等於0。
③    指定key爲大於0的常數,這需要用戶自行保證生成的IPC key值不與系統中存在的衝突,而前兩種是操作系統保證的。

        

ftok的典型實現是調用stat函數,然後組合以下三個值:
①    path 所在的文件系統的信息(stat結構的st_dev成員)。
②    該文件在本文件系統內的索引節點號(stat結構的st_ino成員)。
③    id的低序8位(不能爲0)。

以下程序可以說明這一點:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char **argv)
{
    struct stat stat1 ;
    if ( argc != 2 )
    {
        printf("usage: ftok < pathname >\n" ) ;
        exit(1) ;
    }
    stat( argv[1], &stat1 ) ;
    printf("st_dev:%lx, st_ino:%lx, key:%x\n",  \
(unsigned long)stat1.st_dev, (unsigned long)stat1.st_ino , ftok(argv[1],0x579 )) ;
    printf("st_dev:%lx, st_ino:%lx, key:%x\n",  \
(unsigned long)stat1.st_dev, (unsigned long)stat1.st_ino , ftok(argv[1],0x122 )) ;
    printf("st_dev:%lx, st_ino:%lx, key:%x\n",  \
(unsigned long)stat1.st_dev, (unsigned long)stat1.st_ino , ftok(argv[1],0x22 )) ;
    
    exit(0) ;
}

運行結果:

st_dev:801, st_ino:a0001, key:79010001
st_dev:801, st_ino:a0001, key:22010001
st_dev:801, st_ino:a0001, key:22010001

        根據上述結果可以看出,通過ftok返回的是根據文件(pathname)信息和計劃編號(proj_id)合成的IPC key鍵值,從而避免用戶使用key值的衝突。proj_id值的意義讓一個文件也能生成多個IPC key鍵值。ftok利用同一文件最多可得到IPC key鍵值0xff(即256)個,因爲ftok只取proj_id值二進制的後8位,即16進制的後兩位與文件信息合成IPC key鍵值。

        需要注意的是:如果兩個路徑名引用的是兩個不同的文件,那麼ftok會返回不同的key;如果兩個路徑相同,試用同一個id值,那麼有可能會返回相同的key。

        在 XSI IPC 三種get函數中(msgget、semget、shmget)都會有兩個相似的參數:key  和 flag

        在創建新的IPC結構時(通常是由服務器創建),如果key是IPC_PRIVATE 或者 和當前某種類型的IPC結構無關,則flag需要用 IPC_CREAT 標誌位。

      在引用現有的IPC結構時(通常是客戶端),key 必須等於 服務器創建的 key。 IPC_CREAT 不必可以聲明。注意:IPC_PRIVATE 不能作爲客戶端引用的key,IPC_PRIVATE只能創建,如果要引用 IPC_PRIVATE 需要用其相關的標識符(也就是前面講的內部名,下面詳細說明),然後利用msgsnd、msgrev等函數使用標識符。這樣繞過get。

      flag的使用:

(2)標識符

        給semget、msgget、shmget傳入key值,它們返回的都是相應的IPC對象標識符。注意IPC鍵值和IPC標識符是兩個概念,後者是建立在前者之上。下圖畫出了從IPC鍵值生成IPC標識符圖,其中key爲IPC鍵值,由ftok函數生成;ipc_id爲IPC標識符,由semget、msgget、shmget函數生成。ipc_id在信號量函數中稱爲semid,在消息隊列函數中稱爲msgid,在共享內存函數中稱爲shmid,它們表示的是各自IPC對象標識符。

 

2、創建 或 打開 IPC對象

semget、msgget、shmget函數的作用是創建一個新的IPC對象或者訪問一個已存在的IPC對象。其創建或訪問的規則如下:
①    指定key爲IPC_PRIVATE操作系統保證創建一個唯一的IPC對象。
②    設置flag參數的IPC_CREAT位但不設置它的IPC_EXCL位時,如果所指定key鍵的IPC對象不存在,那就是創建一個新的對象;否則返回該對象。
③    同時設置flag的IPC_CREAT和IPC_EXCL位時,如果所指定key鍵的IPC對象不存在,那就創建一個新的對象;否則返回一個EEXIST錯誤,因爲該對象已存在。
綜上所述,flag創建模式標誌的作用如下表15-3所示。
表15-3 三種xxxget函數flag的創建模式標誌作用表

flag創建模式標誌

      不存在

       已存在

無特殊標誌

出錯,errno=ENOENT

成功,引用已存在對象

IPC_CREAT

成功,創建新對象

成功,引用已存在對象

IPC_CREAT|IPC_EXCL

成功,創建新對象

出錯,errno=EEXIST

下圖15-2畫出了semget、msgget、shmget創建或打開一個IPC對象的邏輯流程圖,它說明了內核創建和訪問IPC對象的流程。

圖15-2 semget、msgget、shmget創建或打開一個IPC對象的邏輯流程圖
     使用semget、msgget、shmget創建一個IPC對象時,需要指定flag標誌,在key不等於IPC_PRIVATE情況下,flag標誌決定了創建方式和創建後IPC對象的存取權限。在key等於IPC_PRIVATE情況下,flag標誌決定了創建後IPC對象的存取權限。如果只是引用一個已經存在的IPC對象只需把flag標誌設爲0即可。

 

3、有關該函數的三個常見問題:

1.pathname是目錄還是文件的具體路徑,是否可以隨便設置
2.pathname指定的目錄或文件的權限是否有要求
3.proj_id是否可以隨便設定,有什麼限制條件

解答:

    1、ftok根據路徑名,提取文件信息,再根據這些文件信息及project ID合成key,該路徑可以隨便設置。
    2、該路徑是必須存在的,ftok只是根據文件inode在系統內的唯一性來取一個數值,和文件的權限無關。
    3、proj_id是可以根據自己的約定,隨意設置。這個數字,有的稱之爲project ID; 在UNIX系統上,它的取值是1到255;

 

4、關於ftok()函數的一個陷阱

     在使用ftok()函數時,裏面有兩個參數,即fname和id,fname爲指定的文件名,而id爲子序列號,這個函數的返回值就是key,它與指定的文件的索引節點號和子序列號id有關,這樣就會給我們一個誤解,即只要文件的路徑,名稱和子序列號不變,那麼得到的key值永遠就不會變。
     事實上,這種認識是錯誤的,想想一下,假如存在這樣一種情況:在訪問同一共享內存的多個進程先後調用ftok()時間段中,如果fname指向的文件或者目錄被刪除而且又重新創建,那麼文件系統會賦予這個同名文件新的i節點信息,於是這些進程調用的ftok()都能正常返回,但鍵值key卻不一定相同了。由此可能造成的後果是,原本這些進程意圖訪問一個相同的共享內存對象,然而由於它們各自得到的鍵值不同,實際上進程指向的共享內存不再一致;如果這些共享內存都得到創建,則在整個應用運行的過程中表面上不會報出任何錯誤,然而通過一個共享內存對象進行數據傳輸的目 的將無法實現。
      這是一個很重要的問題,希望能謹記!!!
     所以要確保key值不變,要麼確保ftok()的文件不被刪除,要麼不用ftok(),指定一個固定的key值。

 

UNIX高級編程總結-----進程間通信(消息隊列)

文章引用:linux進程間通信--消息隊列相關函數(ftok)詳解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章