Linux處理序間通訊——使用共用記憶體

來源:互聯網
上載者:User
下面將講解處理序間通訊的另一種方式,使用共用記憶體。一、什麼是共用記憶體顧名思義,共用記憶體就是允許兩個不相關的進程訪問同一個邏輯記憶體。共用記憶體是在兩個正在啟動並執行進程之間共用和傳遞資料的一種非常有效方式。不同進程之間共用的記憶體通常安排為同一段實體記憶體。進程可以將同一段共用記憶體串連到它們自己的地址空間中,所有進程都可以訪問共用記憶體中的地址,就好像它們是由用C語言函數malloc分配的記憶體一樣。而如果某個進程向共用記憶體寫入資料,所做的改動將立即影響到可以訪問同一段共用記憶體的任何其他進程。特別提醒:共用記憶體並未提供同步機制,也就是說,在第一個進程結束對共用記憶體的寫操作之前,並無自動機制可以阻止第二個進程開始對它進行讀取。所以我們通常需要用其他的機制來同步對共用記憶體的訪問,例如前面說到的訊號量。有關訊號量的更多內容,可以查閱我的另一篇文章:Linux處理序間通訊——使用訊號量二、共用記憶體的使得與訊號量一樣,在Linux中也提供了一組函數介面用於使用共用記憶體,而且使用共用共存的介面還與訊號量的非常相似,而且比使用訊號量的介面來得簡單。它們聲明在標頭檔 sys/shm.h中。1、shmget函數該函數用來建立共用記憶體,它的原型為:
int shmget(key_t key, size_t size, int shmflg);
第一個參數,與訊號量的semget函數一樣,程式需要提供一個參數key(非0整數),它有效地為共用記憶體段命名,shmget函數成功時返回一個與key相關的共用記憶體標識符(非負整數),用於後續的共用記憶體函數。調用失敗返回-1.不相關的進程可以通過該函數的傳回值訪問同一共用記憶體,它代表程式可能要使用的某個資源,程式對所有共用記憶體的訪問都是間接的,程式先通過調用shmget函數並提供一個鍵,再由系統產生一個相應的共用記憶體標識符(shmget函數的傳回值),只有shmget函數才直接使用訊號量鍵,所有其他的訊號量函數使用由semget函數返回的訊號量標識符。第二個參數,size以位元組為單位指定需要共用的記憶體容量第三個參數,shmflg是許可權標誌,它的作用與open函數的mode參數一樣,如果要想在key標識的共用記憶體不存在時,建立它的話,可以與IPC_CREAT做或操作。共用記憶體的許可權標誌與檔案的讀寫權限一樣,舉例來說,0644,它表示允許一個進程建立的共用記憶體被記憶體建立者所擁有的進程向共用記憶體讀取和寫入資料,同時其他使用者建立的進程只能讀取共用記憶體。2、shmat函數第一次建立完共用記憶體時,它還不能被任何進程訪問,shmat函數的作用就是用來啟動對該共用記憶體的訪問,並把共用記憶體串連到當前進程的地址空間。它的原型如下:
void *shmat(int shm_id, const void *shm_addr, int shmflg);
第一個參數,shm_id是由shmget函數返回的共用記憶體標識。第二個參數,shm_addr指定共用記憶體串連到當前進程中的地址位置,通常為空白,表示讓系統來選擇共用記憶體的地址。第三個參數,shm_flg是一組標誌位,通常為0。調用成功時返回一個指向共用記憶體第一個位元組的指標,如果調用失敗返回-1.3、shmdt函數該函數用於將共用記憶體從當前進程中分離。注意,將共用記憶體分離並不是刪除它,只是使該共用記憶體對當前進程不再可用。它的原型如下:
void *shmat(int shm_id, const void *shm_addr, int shmflg);
參數shmaddr是shmat函數返回的地址指標,調用成功時返回0,失敗時返回-1.4、shmctl函數與訊號量的semctl函數一樣,用來控制共用記憶體,它的原型如下:
void *shmat(int shm_id, const void *shm_addr, int shmflg);
第一個參數,shm_id是shmget函數返回的共用記憶體標識符。第二個參數,command是要採取的操作,它可以取下面的三個值 :    IPC_STAT:把shmid_ds結構中的資料設定為共用記憶體的當前關聯值,即用共用記憶體的當前關聯值覆蓋shmid_ds的值。    IPC_SET:如果進程有足夠的許可權,就把共用記憶體的當前關聯值設定為shmid_ds結構中給出的值    IPC_RMID:刪除共用記憶體段第三個參數,buf是一個結構指標,它指向共用記憶體模式和存取權限的結構。shmid_ds結構至少包括以下成員:
struct shmid_ds{    uid_t shm_perm.uid;    uid_t shm_perm.gid;    mode_t shm_perm.mode;};

三、使用共用記憶體進行處理序間通訊說了這麼多,又到了實戰的時候了。下面就以兩個不相關的進程來說明進程間如何通過共用記憶體來進行通訊。其中一個檔案shmread.c建立共用記憶體,並讀取其中的資訊,另一個檔案shmwrite.c向共用記憶體中寫入資料。為了方便操作和資料結構的統一,為這兩個檔案定義了相同的資料結構,定義在檔案shmdata.c中。結構shared_use_st中的written作為一個可讀或可寫的標誌,非0:表示可讀,0表示可寫,text則是記憶體中的檔案。shmdata.c的原始碼如下:

#ifndef _SHMDATA_H_HEADER#define _SHMDATA_H_HEADER#define TEXT_SZ 2048struct shared_use_st{int written;//作為一個標誌,非0:表示可讀,0表示可寫char text[TEXT_SZ];//記錄寫入和讀取的文本};#endif
源檔案shmread.c的原始碼如下:
#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <sys/shm.h>#include "shmdata.h"int main(){int running = 1;//程式是否繼續啟動並執行標誌void *shm = NULL;//分配的共用記憶體的原始首地址struct shared_use_st *shared;//指向shmint shmid;//共用記憶體標識符//建立共用記憶體shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);if(shmid == -1){fprintf(stderr, "shmget failed\n");exit(EXIT_FAILURE);}//將共用記憶體串連到當前進程的地址空間shm = shmat(shmid, 0, 0);if(shm == (void*)-1){fprintf(stderr, "shmat failed\n");exit(EXIT_FAILURE);}printf("\nMemory attached at %X\n", (int)shm);//設定共用記憶體shared = (struct shared_use_st*)shm;shared->written = 0;while(running)//讀取共用記憶體中的資料{//沒有進程向共用記憶體定資料有資料可讀取if(shared->written != 0){printf("You wrote: %s", shared->text);sleep(rand() % 3);//讀取完資料,設定written使共用記憶體段可寫shared->written = 0;//輸入了end,退出迴圈(程式)if(strncmp(shared->text, "end", 3) == 0)running = 0;}else//有其他進程在寫資料,不能讀取資料sleep(1);}//把共用記憶體從當前進程中分離if(shmdt(shm) == -1){fprintf(stderr, "shmdt failed\n");exit(EXIT_FAILURE);}//刪除共用記憶體if(shmctl(shmid, IPC_RMID, 0) == -1){fprintf(stderr, "shmctl(IPC_RMID) failed\n");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);}
源檔案shmwrite.c的原始碼如下:
#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <sys/shm.h>#include "shmdata.h"int main(){int running = 1;void *shm = NULL;struct shared_use_st *shared = NULL;char buffer[BUFSIZ + 1];//用於儲存輸入的文本int shmid;//建立共用記憶體shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);if(shmid == -1){fprintf(stderr, "shmget failed\n");exit(EXIT_FAILURE);}//將共用記憶體串連到當前進程的地址空間shm = shmat(shmid, (void*)0, 0);if(shm == (void*)-1){fprintf(stderr, "shmat failed\n");exit(EXIT_FAILURE);}printf("Memory attached at %X\n", (int)shm);//設定共用記憶體shared = (struct shared_use_st*)shm;while(running)//向共用記憶體中寫資料{//資料還沒有被讀取,則等待資料被讀取,不能向共用記憶體中寫入文本while(shared->written == 1){sleep(1);printf("Waiting...\n");}//向共用記憶體中寫入資料printf("Enter some text: ");fgets(buffer, BUFSIZ, stdin);strncpy(shared->text, buffer, TEXT_SZ);//寫完資料,設定written使共用記憶體段可讀shared->written = 1;//輸入了end,退出迴圈(程式)if(strncmp(buffer, "end", 3) == 0)running = 0;}//把共用記憶體從當前進程中分離if(shmdt(shm) == -1){fprintf(stderr, "shmdt failed\n");exit(EXIT_FAILURE);}sleep(2);exit(EXIT_SUCCESS);}
再來看看啟動並執行結果:分析:1、程式shmread建立共用記憶體,然後將它串連到自己的地址空間。在共用記憶體的開始處使用了一個結構struct_use_st。該結構中有個標誌written,當共用記憶體中有其他進程向它寫入資料時,共用記憶體中的written被設定為0,程式等待。當它不為0時,表示沒有進程對共用記憶體寫入資料,程式就從共用記憶體中讀取資料並輸出,然後重設設定共用記憶體中的written為0,即讓其可被shmwrite進程寫入資料。2、程式shmwrite取得共用記憶體並串連到自己的地址空間中。檢查共用記憶體中的written,是否為0,若不是,表示共用記憶體中的資料還沒有被完,則等待其他進程讀取完成,並提示使用者等待。若共用記憶體的written為0,表示沒有其他進程對共用記憶體進行讀取,則提示使用者輸入文本,並再次設定共用記憶體中的written為1,表示寫完成,其他進程可對共用記憶體進行讀操作。四、關於前面的例子的安全性討論這個程式是不安全的,當有多個程式同時向共用記憶體中讀寫資料時,問題就會出現。可能你會認為,可以改變一下written的使用方式,例如,只有當written為0時進程才可以向共用記憶體寫入資料,而當一個進程只有在written不為0時才能對其進行讀取,同時把written進行加1操作,讀取完後進行減1操作。這就有點像檔案鎖中的讀寫鎖的功能。咋看之下,它似乎能行得通。但是這都不是原子操作,所以這種做法是行不能的。試想當written為0時,如果有兩個進程同時訪問共用記憶體,它們就會發現written為0,於是兩個進程都對其進行寫操作,顯然不行。當written為1時,有兩個進程同時對共用記憶體進行讀操作時也是如些,當這兩個進程都讀取完是,written就變成了-1.要想讓程式安全地執行,就要有一種進程同步的進位,保證在進入臨界區的操作是原子操作。例如,可以使用前面所講的訊號量來進行進程的同步。因為訊號量的操作都是原子性的。五、使用共用記憶體的優缺點1、優點:我們可以看到使用共用記憶體進行進程間的通訊真的是非常方便,而且函數的介面也簡單,資料的共用還使進程間的資料不用傳送,而是直接存取記憶體,也加快了程式的效率。同時,它也不像匿名管道那樣要求通訊的進程有一定的父子關係。2、缺點:共用記憶體沒有提供同步的機制,這使得我們在使用共用記憶體進行處理序間通訊時,往往要藉助其他的手段來進行進程間的同步工作。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.