標籤:c ext int get a 檔案
進程間的通訊(2)
有三種IPC我們稱為XSI IPC,即訊息佇列、訊號量以及共用儲存空間,它們之間有很多相似之處。
標識符和鍵
每個核心的IPC結構(訊息佇列、訊號量或共用儲存段)都用一個非負整數的標識符加以引用。例如,為了對一個訊息佇列發送或取訊息,只需要知道其隊列標識符。與檔案描述符不同,IPC標識符不是小的整數。當一個IPC結構被建立,以後被刪除時,與這種結果相關的標識符連續加1,知道達到一個整型數的最大值,然後又回到0。
標識符是IPC對象的內部名。為使多個合作進程能夠在同一IPC對象上會和,需要提供一個外部名方案。為此使用了鍵,每個IPC對象都與一個鍵相關聯,於是鍵就用作該對象的外部名,無論何時建立IPC結構,都應該指定一個鍵,鍵的資料類型是基本系統資料類型key_t,通常在標頭檔<sys/types.h>中被定義為長整型。鍵由核心轉換成標識符。
訊息佇列
訊息佇列是訊息的串連表,存放在核心中並由訊息佇列標識符標識。我們把訊息佇列成為隊列,其標識符為隊列ID。
訊息佇列提供了一種子兩個不相關的進程之間傳遞資料相當簡單而且有效方法。與具名管道相比,訊息佇列的優勢在於,它獨立於發送和接受進程而存在,這消除了在同步命名管的開啟和關閉時可能產生的一些困難。
訊息佇列函數的定義如下:
#include <sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
int msgctl(intmsqid, int cmd, struct msqid_ds *buf);
int msgget(key_tkey, int msgflg);
int msgsnd(intmsqid, const void *msgp, size_t msgsz, int msgflg);
ssize_tmsgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msgget用於建立一個新的隊列或開啟一個現存的隊列,程式提供一個兼職來命名某個特定的訊息佇列,第二個參數msgflg由九個標誌組成,返回隊列標識符。
msgsnd將訊息添加到隊列尾端。每個訊息包含一個正長整數型別欄位,一個非負長度以及實際資料位元組,所有這些都在將訊息添加到隊列時,傳送給msgsnd。msgp參數指向一個長整型,它包含了整型訊息類型,再其後緊跟資料。若發送的最長訊息是512位元組,可定義下列結構,於是msgp就是指向msgbuf結構的指標。msgflag可以設定為IPC_NOWAIT,類似於I/O的非阻塞I/O標誌。
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[512]; /* message data */
};
msgrcv用於從隊列中取訊息。我們並不一定要以先進先出次序取訊息,也可以按訊息的類型欄位取訊息。第二個參數是指向準備接收訊息的指標;第四個參數指定接收優先順序,若值為0,就擷取隊列中第一個訊息,若大於0擷取具有相同訊息類型的第一個訊息,若小於0擷取訊息類型小於或等於msgtyp的絕對值的第一個訊息。
msgctl函數可以對隊列執行多種操作。
訊號量
訊號量是一個計數器,用於多進程對共用資料對象的訪問。為了獲得共用資源,進程需要執行下列操作:
1. 測試控制該資源的訊號量
2. 若此訊號量的值為正,則進程可以使用該資源。進程將訊號量值減1,表示它使用了一個資源單位
3. 若此訊號量的值為0,則進程進入休眠狀態,直至訊號量值大於0。進程被喚醒後,它返回第1步。
當進程不在使用由一個訊號量控制的共用資源時,該訊號量值增1。如果有進程正在休眠等待此訊號量,則喚醒它們。
為了正確實現訊號量,訊號量值的測試和減1操作應當是原子操作。為此,訊號量通常在核心中實現。
最簡單的訊號量是只能取值為0和1的變數,即二進位訊號量(二元訊號量)。
PV操作的定義非常簡單。假設有一個訊號量變數sv,則這兩個操作定義如下:
P(sv):如果sv的值大於零,就給它減去1;如果它的值等於0,就掛起該進程的運行。
V(sv):如果有其他進程因等待sv而被掛起,就讓他恢複運行;如果沒有進程因等待sv而被掛起,就給它加1。
但訊號量集與普通訊號量相比要複雜得多:
1. 訊號量集並非單個非負值,而必須將其定義為含有一個或多個訊號量的集合。當建立一個訊號量集時,要指定該集合中訊號量的數目
2. 建立訊號量集(semget)與對其賦初值(semctl)分開。這是一個致命弱點,因為不能原子地建立一個訊號集,並對該集合中的各個訊號量賦初值
3. 即使沒有使用訊號量集,他們仍然存在。有些程式在終止時並沒有回訪已經分配給他們的訊號量集,所以我們不得不為此次擔心。
#include <sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
int semget(key_t key, int nsems, intsemflg);
semget函數的作用是建立一個新訊號量或取得一個已有訊號量的健。
#include <sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
int semop(int semid, struct sembuf*sops, unsigned nsops);
semop函數用於改變訊號量的值。它的第一個參數sem_id是由semget返回的訊號量標識符。第二個參數是一個指向饑餓數組的指標,每個素群組成員至少包含以下成員:
struct sembuf{
unsigned short sem_num; /*semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
}
第一個成員sem_num是訊號量編號,除非你需要使用一組訊號量,否則它的取值一般為0。第二個成員sem_op成員的值是訊號量在一次操作中需要改變的數值。通常只會用到兩個值,一個是-1,也就是P操作,它等待訊號量變為可用;一個是+1,也就是V操作,它發送訊號表示訊號量現在可用。最後一個成員sem_flag通常被設定為SEM_UNDO。它將使得作業系統跟蹤當前進程對這個訊號量的修改情況,如果該進程在沒有釋放該訊號量的情況下終止,作業系統將自動釋放該進程持有的訊號量。
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
int semctl(int semid, int semnum, intcmd, ...);
semctl函數允許我們直接控制訊號量資訊,sem_num參數是訊號量編號,一般為0。cmd參數是即將採取的動作。
共用記憶體
共用記憶體允許兩個不相關的進程訪問同一個邏輯記憶體。共用記憶體為在多個進程之間共用和傳遞資料提供了一種有效方式。由於它並未提供同步機制,所以我們通常需要其他的機制來同步對共用記憶體的訪問。共用記憶體是由IPC為進程建立的一個特殊的位址範圍,它將出現在該進程的地址空間中。其他進程可以同一段共用記憶體串連到它們自己的地址空間中。所有進程都可以訪問共用記憶體中的地址,就好像它們是由malloc分配的一樣。如果一個進程向共用記憶體寫入資料,所做的改動將立刻可以訪問同一段共用記憶體的任何其他進程看到。
共用記憶體使用量的函數如下:
#include<sys/types.h>
#include<sys/shm.h>
void *shmat(intshmid, const void *shmaddr, int shmflg);
int shmdt(constvoid *shmaddr);
#include<sys/ipc.h>
#include<sys/shm.h>
int shmctl(intshmid, int cmd, struct shmid_ds *buf);
int shmget(key_tkey, size_t size, int shmflg);
我們需要用shmget函數建立共用記憶體:第一個參數作為鍵值;第二個參數是以位元組為單位指定需要共用的記憶體容量;第三個參數shmflag包含九個位元的許可權標誌,許可權標誌允許一個進程建立的共用記憶體可以被共用記憶體的建立者擁有的進程寫入,同時其他使用者建立的進程只能讀取該共用記憶體。
第一次建立共用記憶體時,它不能被任何進程訪問,要想啟用對該共用記憶體的訪問,必須將其串連到一個進程的地址空間中。這項工作由shmat函數完成。第一個參數是共用記憶體標識符;第二個參數shmaddr指定的是共用記憶體串連到當前進程中的地址位置。通常是一個null 指標,表示讓系統選擇共用記憶體出現的地址。第三個參數shmflg是一組位標誌。它的兩個取值可能是SHM_RND(這個標誌與shm_addr聯合使用,用來控制共用記憶體連接的地址)和SHM_RDONLY(它使得串連的記憶體唯讀)。
shmdt函數作用是將共用記憶體從當前進程中分離,參數是shmat返回的指標。
shmctl的第二個參數是要採取的動作;第三個參數是一個指標,指向共用記憶體模式和存取權限的結構。