由於操作系統實驗的需要,做了這些內容。代碼基於Ubuntu操作系統環境下運行和測試。 ...
寫在前面
不得不說,Deadline果真是第一生產力。不過做出來的東西真的是不堪入目,於是又花了一早上重寫代碼。
實驗內容
進程通信的郵箱方式由操作系統提供形如 send()和 receive()的系統調用來支持,本實驗要求學生首先查找資料瞭解所選用操作系統平臺上用於進程通信的系統調用具體形式,然後使用該系統調用編寫程式進行進程間的通信,要求程式運行結果可以直觀地體現在界面上。在此基礎上查找所選用操作系統平臺上支持信號量機制的系統調用具體形式,運用生產者與消費者模型設計實現一個簡單的信箱,該信箱需要有創建、發信、收信、撤銷等函數,至少能夠支持兩個進程互相交換信息,比較自己實現的信箱與操作系統本身提供的信箱,分析兩者之間存在的異同。
背景知識
消息隊列
什麼是消息隊列
消息隊列提供了一種從一個進程向另一個進程發送一個數據塊的方法。 每個數據塊都被認為含有一個類型,接收進程可以獨立地接收含有不同類型的數據結構。我們可以通過發送消息來避免命名管道的同步和阻塞問題。但是消息隊列與命名管道一樣,每個數據塊都有一個最大長度的限制。
Linux用巨集MSGMAX和MSGMNB來限制一條消息的最大長度和一個隊列的最大長度。- Linux中如何使用消息隊列
Linux提供了一系列消息隊列的函數介面來讓我們方便地使用它來實現進程間的通信。它的用法與其他兩個System V PIC機制,即信號量和共用記憶體相似。- msgget()函數
該函數用來創建和訪問一個消息隊列。它的原型為:
int msgget(key_t key, int msgflg);
它返回一個以key命名的消息隊列的標識符(非零整數),失敗時返回-1. - msgsnd()函數
該函數用來把消息添加到消息隊列中。它的原型為:
int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);
如果調用成功,消息數據的一份副本將被放到消息隊列中,並返回0,失敗時返回-1. - msgrcv()函數
該函數用來從一個消息隊列獲取消息,它的原型為:
int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);
調用成功時,該函數返回放到接收緩存區中的位元組數,消息被覆制到由msg_ptr指向的用戶分配的緩存區中,然後刪除消息隊列中的對應消息。失敗時返回-1。 - msgctl()函數
該函數用來控制消息隊列,它與共用記憶體的shmctl函數相似,它的原型為:
int msgctl(int msgid, int command, struct msgid_ds *buf);
成功時返回0,失敗時返回-1.
- msgget()函數
信號量
- 什麼是信號量
為了防止出現因多個程式同時訪問一個共用資源而引發的一系列問題,我們需要一種方法,它可以通過生成並使用令牌來授權,在任一時刻只能有一個執行線程訪問代碼的臨界區域。臨界區域是指執行數據更新的代碼需要獨占式地執行。而信號量就可以提供這樣的一種訪問機制,讓一個臨界區同一時間只有一個線程在訪問它,也就是說信號量是用來調協進程對共用資源的訪問的。 - Linux的信號量機制
Linux提供了一組精心設計的信號量介面來對信號量進行操作,它們不只是針對二進位信號量,下麵將會對這些函數進行介紹,但請註意,這些函數都是用來對成組的信號量值進行操作的。它們聲明在頭文件sys/sem.h中。- semget()函數
它的作用是創建一個新信號量或取得一個已有信號量,原型為:
int semget(key_t key, int num_sems, int sem_flags);
成功返回一個相應信號標識符(非零),失敗返回-1. - semop()函數
它的作用是改變信號量的值,原型為:
int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);
- semctl()函數
該函數用來直接控制信號量信息,它的原型為:
int semctl(int sem_id, int sem_num, int command, ...);
- semget()函數
共用記憶體
- 什麼是共用記憶體
顧名思義,共用記憶體就是允許兩個不相關的進程訪問同一個邏輯記憶體。共用記憶體是在兩個正在運行的進程之間共用和傳遞數據的一種非常有效的方式。不同進程之間共用的記憶體通常安排為同一段物理記憶體。進程可以將同一段共用記憶體連接到它們自己的地址空間中,所有進程都可以訪問共用記憶體中的地址,就好像它們是由用C語言函數malloc()分配的記憶體一樣。而如果某個進程向共用記憶體寫入數據,所做的改動將立即影響到可以訪問同一段共用記憶體的任何其他進程。
特別提醒:共用記憶體並未提供同步機制,也就是說,在第一個進程結束對共用記憶體的寫操作之前,並無自動機制可以阻止第二個進程開始對它進行讀取。所以我們通常需要用其他的機制來同步對共用記憶體的訪問,例如前面說到的信號量。 - 共用記憶體的使用
與信號量一樣,在Linux中也提供了一組函數介面用於使用共用記憶體,而且使用共用共存的介面還與信號量的非常相似,而且比使用信號量的介面來得簡單。它們聲明在頭文件 sys/shm.h 中。- shmget()函數
該函數用來創建共用記憶體,它的原型為:
int shmget(key_t key, size_t size, int shmflg);
成功時返回一個與key相關的共用記憶體標識符(非負整數),用於後續的共用記憶體函數。調用失敗返回-1. - shmat()函數
第一次創建完共用記憶體時,它還不能被任何進程訪問,shmat()函數的作用就是用來啟動對該共用記憶體的訪問,並把共用記憶體連接到當前進程的地址空間。它的原型如下:
void *shmat(int shm_id, const void *shm_addr, int shmflg);
成功時返回一個指向共用記憶體第一個位元組的指針,如果調用失敗返回-1. - shmdt()函數
該函數用於將共用記憶體從當前進程中分離。註意,將共用記憶體分離並不是刪除它,只是使該共用記憶體對當前進程不再可用。它的原型如下:
int shmdt(const void *shmaddr);
調用成功時返回0,失敗時返回-1. - shmctl()函數
與信號量的semctl()函數一樣,用來控制共用記憶體,它的原型如下:
int shmctl(int shm_id, int command, struct shmid_ds *buf);
- shmget()函數
參考資料
以上資料全部來源於以下網站:
- Linux進程間通信(五):信號量 semget()、semop()、semctl()
- Linux進程間通信(六):共用記憶體 shmget()、shmat()、shmdt()、shmctl()
- Linux進程間通信(七):消息隊列 msgget()、msgsend()、msgrcv()、msgctl()
實驗結果
- 消息隊列
- 信號量+共用記憶體
完整代碼
Linux-interProcessCommunication
如果對你有幫助點個star吧(●'◡'●)
總結
- 不足
- 沒有圖形化界面
- 用信號量和共用記憶體實現的進程通信只能發送數字消息
- 創建共用記憶體空間時,設置許可權為了省事設置為0666( 每個進程可讀和可寫),應該要設置user只能讀而不能寫,other只能寫而不能讀
消息隊列沒有測試msgrcv()函數通過改變msgtype參數來改變接收優先順序。
> msgtype 可以實現一種簡單的接收優先順序。如果msgtype為0,就獲取隊列中的第一個消息。如果它的值大於零,將獲取具有相同消息類型的第一個信息。如果它小於零,就獲取類型等於或小於msgtype的絕對值的第一個消息。- ...
- 兩種方式實現進程間通信的異同
- 異:消息隊列不需要信號量來控制同步和互斥問題,並且可以很方便的改變接收優先順序,而共用記憶體則只能簡單的接收按時間排序的消息。
- 同:一個進程接收到的消息都和另一個進程發送的相同。
- 心得
- 由於Linux提供的信號量介面函數都是針對一組信號量進行操作的,因此參數中大部分都需要指定對一組中的哪一個信號量進行操作
- 共用記憶體只存放消息緩存區,至於信箱頭的那些值仍然存放在各自進程中。
如有不足,歡迎指正!