共享內(nèi)存是進(jìn)程間通信中最簡單的方式之一。共享內(nèi)存允許兩個或更多進(jìn)程訪問同一塊內(nèi)存,就如同 malloc() 函數(shù)向不同進(jìn)程返回了指向同一個物理內(nèi)存區(qū)域的指針。當(dāng)一個進(jìn)程改變了這塊地址中的內(nèi)容的時候,其它進(jìn)程都會察覺到這個更改。
共享內(nèi)存的特點(diǎn):
1)共享內(nèi)存是進(jìn)程間共享數(shù)據(jù)的一種最快的方法。
一個進(jìn)程向共享的內(nèi)存區(qū)域?qū)懭肓藬?shù)據(jù),共享這個內(nèi)存區(qū)域的所有進(jìn)程就可以立刻看到其中的內(nèi)容。
2)使用共享內(nèi)存要注意的是多個進(jìn)程之間對一個給定存儲區(qū)訪問的互斥。
若一個進(jìn)程正在向共享內(nèi)存區(qū)寫數(shù)據(jù),則在它做完這一步操作前,別的進(jìn)程不應(yīng)當(dāng)去讀、寫這些數(shù)據(jù)。
常用函數(shù)
1)創(chuàng)建共享內(nèi)存
所需頭文件:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size,int shmflg);
功能:
創(chuàng)建或打開一塊共享內(nèi)存區(qū)。
參數(shù):
key:進(jìn)程間通信鍵值,ftok() 的返回值。
size:該共享存儲段的長度(字節(jié))。
shmflg:標(biāo)識函數(shù)的行為及共享內(nèi)存的權(quán)限,其取值如下:
IPC_CREAT:如果不存在就創(chuàng)建
IPC_EXCL: 如果已經(jīng)存在則返回失敗
位或權(quán)限位:共享內(nèi)存位或權(quán)限位后可以設(shè)置共享內(nèi)存的訪問權(quán)限,格式和 open() 函數(shù)的 mode_t 一樣(open() 的使用請點(diǎn)此鏈接),但可執(zhí)行權(quán)限未使用。
返回值:
成功:共享內(nèi)存標(biāo)識符。
失?。?1。
示例代碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define BUFSZ 1024
int main(int argc, char *argv[])
{
int shmid;
key_t key;
key = ftok("./", 2015);
if(key == -1)
{
perror("ftok");
}
//創(chuàng)建共享內(nèi)存
shmid = shmget(key, BUFSZ, IPC_CREAT|0666);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
return 0;
}
運(yùn)行結(jié)果如下:
2)共享內(nèi)存映射
所需頭文件:
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:
將一個共享內(nèi)存段映射到調(diào)用進(jìn)程的數(shù)據(jù)段中。簡單來理解,讓進(jìn)程和共享內(nèi)存建立一種聯(lián)系,讓進(jìn)程某個指針指向此共享內(nèi)存。
參數(shù):
shmid:共享內(nèi)存標(biāo)識符,shmget() 的返回值。
shmaddr:共享內(nèi)存映射地址(若為 NULL 則由系統(tǒng)自動指定),推薦使用 NULL。
shmflg:共享內(nèi)存段的訪問權(quán)限和映射條件( 通常為 0 ),具體取值如下:
0:共享內(nèi)存具有可讀可寫權(quán)限。
SHM_RDONLY:只讀。
SHM_RND:(shmaddr 非空時才有效)
返回值:
成功:共享內(nèi)存段映射地址( 相當(dāng)于這個指針就指向此共享內(nèi)存 )
失?。?1
3)解除共享內(nèi)存映射
所需頭文件:
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
功能:
將共享內(nèi)存和當(dāng)前進(jìn)程分離( 僅僅是斷開聯(lián)系并不刪除共享內(nèi)存,相當(dāng)于讓之前的指向此共享內(nèi)存的指針,不再指向)。
參數(shù):
shmaddr:共享內(nèi)存映射地址。
返回值:
成功:0
失?。?1
4)共享內(nèi)存控制
所需的頭文件:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:
共享內(nèi)存屬性的控制。
參數(shù):
shmid:共享內(nèi)存標(biāo)識符。
cmd:函數(shù)功能的控制,其取值如下:
IPC_RMID:刪除。(常用 )
IPC_SET:設(shè)置 shmid_ds 參數(shù),相當(dāng)于把共享內(nèi)存原來的屬性值替換為 buf 里的屬性值。
IPC_STAT:保存 shmid_ds 參數(shù),把共享內(nèi)存原來的屬性值備份到 buf 里。
SHM_LOCK:鎖定共享內(nèi)存段( 超級用戶 )。
SHM_UNLOCK:解鎖共享內(nèi)存段。
SHM_LOCK 用于鎖定內(nèi)存,禁止內(nèi)存交換。并不代表共享內(nèi)存被鎖定后禁止其它進(jìn)程訪問。其真正的意義是:被鎖定的內(nèi)存不允許被交換到虛擬內(nèi)存中。這樣做的優(yōu)勢在于讓共享內(nèi)存一直處于內(nèi)存中,從而提高程序性能。
buf:shmid_ds 數(shù)據(jù)類型的地址(具體類型請點(diǎn)此鏈接 ),用來存放或修改共享內(nèi)存的屬性。
返回值:
成功:0
失敗:-1
實(shí)戰(zhàn)示例
接下來我們做這么一個例子:創(chuàng)建兩個進(jìn)程,在 A 進(jìn)程中創(chuàng)建一個共享內(nèi)存,并向其寫入數(shù)據(jù),通過 B 進(jìn)程從共享內(nèi)存中讀取數(shù)據(jù)。
寫端代碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define BUFSZ 512
int main(int argc, char *argv[])
{
int shmid;
int ret;
key_t key;
char *shmadd;
//創(chuàng)建key值
key = ftok("../", 2015);
if(key == -1)
{
perror("ftok");
}
//創(chuàng)建共享內(nèi)存
shmid = shmget(key, BUFSZ, IPC_CREAT|0666);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
//映射
shmadd = shmat(shmid, NULL, 0);
if(shmadd < 0)
{
perror("shmat");
_exit(-1);
}
//拷貝數(shù)據(jù)至共享內(nèi)存區(qū)
printf("copy data to shared-memory\n");
bzero(shmadd, BUFSZ); // 共享內(nèi)存清空
strcpy(shmadd, "how are you, mike\n");
return 0;
}
讀端代碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define BUFSZ 512
int main(int argc, char *argv[])
{
int shmid;
int ret;
key_t key;
char *shmadd;
//創(chuàng)建key值
key = ftok("../", 2015);
if(key == -1)
{
perror("ftok");
}
system("ipcs -m"); //查看共享內(nèi)存
//打開共享內(nèi)存
shmid = shmget(key, BUFSZ, IPC_CREAT|0666);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
//映射
shmadd = shmat(shmid, NULL, 0);
if(shmadd < 0)
{
perror("shmat");
exit(-1);
}
//讀共享內(nèi)存區(qū)數(shù)據(jù)
printf("data = [%s]\n", shmadd);
//分離共享內(nèi)存和當(dāng)前進(jìn)程
ret = shmdt(shmadd);
if(ret < 0)
{
perror("shmdt");
exit(1);
}
else
{
printf("deleted shared-memory\n");
}
//刪除共享內(nèi)存
shmctl(shmid, IPC_RMID, NULL);
system("ipcs -m"); //查看共享內(nèi)存
return 0;
}
運(yùn)行結(jié)果如下: