Linux進程間通信(消息隊列

寫在前面

不得不說,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.

信號量

  • 什麼是信號量
  • 為了防止出現因多個程序同時訪問一個共享資源而引發的一系列問題,我們需要一種方法,它可以通過生成並使用令牌來授權,在任一時刻只能有一個執行線程訪問代碼的臨界區域。臨界區域是指執行數據更新的代碼需要獨佔式地執行。而信號量就可以提供這樣的一種訪問機制,讓一個臨界區同一時間只有一個線程在訪問它,也就是說信號量是用來調協進程對共享資源的訪問的。
  • 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, ...);

共享內存

  • 什麼是共享內存
  • 顧名思義,共享內存就是允許兩個不相關的進程訪問同一個邏輯內存。共享內存是在兩個正在運行的進程之間共享和傳遞數據的一種非常有效的方式。不同進程之間共享的內存通常安排為同一段物理內存。進程可以將同一段共享內存連接到它們自己的地址空間中,所有進程都可以訪問共享內存中的地址,就好像它們是由用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);
  • Linux進程間通信(五):信號量 semget()、semop()、semctl()
  • Linux進程間通信(六):共享內存 shmget()、shmat()、shmdt()、shmctl()
  • Linux進程間通信(七):消息隊列 msgget()、msgsend()、msgrcv()、msgctl()

實驗結果

  • 消息隊列
Linux進程間通信(消息隊列/信號量+共享內存)


  • 信號量+共享內存
Linux進程間通信(消息隊列/信號量+共享內存)


完整代碼

Linux-interProcessCommunication

總結

  • 不足
  • 沒有圖形化界面
  • 用信號量和共享內存實現的進程通信只能發送數字消息
  • 創建共享內存空間時,設置權限為了省事設置為0666( 每個進程可讀和可寫),應該要設置user只能讀而不能寫,other只能寫而不能讀
  • 消息隊列沒有測試msgrcv()函數通過改變msgtype參數來改變接收優先級。
  • > msgtype 可以實現一種簡單的接收優先級。如果msgtype為0,就獲取隊列中的第一個消息。如果它的值大於零,將獲取具有相同消息類型的第一個信息。如果它小於零,就獲取類型等於或小於msgtype的絕對值的第一個消息。
  • ...
  • 兩種方式實現進程間通信的異同
  • 異:消息隊列不需要信號量來控制同步和互斥問題,並且可以很方便的改變接收優先級,而共享內存則只能簡單的接收按時間排序的消息。
  • 同:一個進程接收到的消息都和另一個進程發送的相同。
  • 心得
  • 由於Linux提供的信號量接口函數都是針對一組信號量進行操作的,因此參數中大部分都需要指定對一組中的哪一個信號量進行操作
  • 共享內存只存放消息緩存區,至於信箱頭的那些值仍然存放在各自進程中。


分享到:


相關文章: