作為最快的IPC方式,共享內存當然得好好學一下咯。
System V進程間通信方式:信號量、消息隊列、共享內存。他們都是由AT&T System V2版本的UNIX引進的,所以統稱為System V IPC.
除了下面講的System V IPC,還有mmap也可以將文件進行內存映射,從而實現共享內存的效果。對比可以參考 Link
參考 http://blog.csdn.net/ljianhui/article/details/10253345
它們聲明在頭文件 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函數
該函數用於將共享內存從當前進程中分離。注意,將共享內存分離並不是刪除它,只是使該共享內存對當前進程不再可用。它的原型如下:
int shmdt(const void *shmaddr);
參數shmaddr是shmat函數返回的地址指針,調用成功時返回0,失敗時返回-1.
4、shmctl函數
與信號量的semctl函數一樣,用來控制共享內存,它的原型如下:
int shmctl(int shm_id, int command, struct shmid_ds *buf);
第一個參數,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.h
#ifndef _SHMDATA_H_HEADER #define _SHMDATA_H_HEADER #define TEXT_SZ 2048 struct shared_use_st { int written; 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; 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, 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; shared->written = 0; while (running) { if (shared->written != 0) { printf("You wrote: %s\n", shared->text); sleep(rand() % 5 + 1); if (strncmp(shared->text, "end", 3) == 0) { running = 0; } shared->written = 0; } else { sleep(rand() % 5 + 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); shared->written = 1; 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); }
編譯命令:
gcc -o shmwrite shmwrite.c -lm gcc -o shmread shmread.c -lm # actually no -lm also works
運行效果:
$ ./shmread & $ Memory attached at B76A8000 $ ./shmwrite Memory attached at 5f886000 Enter some text:a waiting... You wrote: a waiting... waiting... Enter some text:b waiting... waiting... You wrote: b waiting... waiting... Enter some text:end You wrote: end $ [1]+ Done ./shmread
分析:
1、程序shmread創建共享內存,然后將它連接到自己的地址空間。在共享內存的開始處使用了一個結構struct_use_st。該結構中有個標志written,當共享內存中有其他進程向它寫入數據時,共享內存中的written被設置為0,程序等待。當它不為0時,表示沒有進程對共享內存寫入數據,程序就從共享內存中讀取數據並輸出,然后重置設置共享內存中的written為0,即讓其可被shmwrite進程寫入數據。 2、程序shmwrite取得共享內存並連接到自己的地址空間中。檢查共享內存中的written,是否為0,若不是,表示共享內存中的數據還沒有被完,則等待其他進程讀取完成,並提示用戶等待。若共享內存的written為0,表示沒有其他進程對共享內存進行讀取,則提示用戶輸入文本,並再次設置共享內存中的written為1,表示寫完成,其他進程可對共享內存進行讀操作。
四、關於前面的例子的安全性討論
這個程序是不安全的。
要想讓程序安全地執行,就要有一種進程同步的進制,保證在進入臨界區的操作是原子操作。例如,可以使用前面所講的信號量來進行進程的同步。因為信號量的操作都是原子性的。
五、使用共享內存的優缺點
1、優點:我們可以看到使用共享內存進行進程間的通信真的是非常方便,而且函數的接口也簡單,數據的共享還使
進程間的數據不用傳送,而是直接訪問內存,也加快了程序的效率。同時,它也
不像匿名管道那樣要求通信的進程有一定的父子關系。
2、缺點:
共享內存沒有提供同步的機制,這使得我們在使用共享內存進行進程間通信時,往往要借助其他的手段來進行進程間的同步工作。
實現
大多數的共享內存的實現,
都把由不同進程之間共享的內存安排為同一段物理內存。
它
並未提供同步機制,
我們通常是用共享內存來提供對大塊內存區域的有效訪問,
同時通過傳遞小消息來同步對該內存的訪問。
實際編程中,
應該使用信號量,
或通過傳遞消息(使用管道或IPC消息),
或生成信號
的方法來提供讀寫之間的更有效的同步機制。