Linux c 基於記憶體的程序通訊—共享記憶體、共享佇列(訊息佇列)
基於記憶體的程序通訊:
1. 核心共享記憶體
程式設計模型:
1.1.建立共享記憶體,得到一個ID shmget
1.2.把ID影射成虛擬地址(掛載) shmat
1.3.使用虛擬地址訪問核心共享記憶體使用任何記憶體函式與運算子號 1.4.解除安裝虛擬地址 shmdt
1.5.刪除共享記憶體 shctl(修改/獲取共享記憶體的屬性)
案例:
A.建立共享記憶體,並且修改記憶體資料
1. 建立共享記憶體
#include<sys/shm.h>
intshmget(key_t key,//為什麼需要key
int size,//共享記憶體大小
int flags//共享記憶體的屬性與許可權
)
為什麼要key_t:
約定建立與訪問的是同一個共享記憶體。Key為兩個程序之間訪問同一塊共享記憶體的約定
注:key需要唯一性,因為我們不能保證我們自己定義的key的唯一性,所以為了保證kay的唯一性,我們需要用某個檔案對應的整數來充當kay值,可以用ftok函式來將一個檔案轉化為一個kay值。(一般我們用兩個程序的共同的工程目錄檔案來確定kay值)
ftok函式:
#include<sys/ipc.h>
key_t ftok( const char * pathname,int proj_id);
引數二:一個控制因子。建議在0—255之間
第三個引數:
方式|許可權
方式:建立 IPC_CREAT IPC_EXCL(如果記憶體已經建立,直接錯誤返回)
開啟:0
常見的兩種方式:
建立:IPC_CREAT|IPC_EXCL | 0666;
開啟:0
返回:
成功返回共享記憶體ID
失敗返回-1
失敗返回-1
B.根據ID得到共享記憶體,並且訪問記憶體資料。
掛載共享記憶體
void* shmat(int id,
void*startaddr,//0:系統指定首地址
intflags)//掛載方式,建議0預設讀寫,可以使用IPC_RDONLY
返回值:合法地址成功,-1失敗
C.刪除
intshmctl(int id,//被操作的共享記憶體ID
inthow,//操作方式:一共三種操作
structshmid_ds*ds)//共享記憶體屬性
how:
IPC_STAT
IPC_SET //修改屬性
IPC_RMID //刪除 引數三無用
案例程式碼:
ShmA.c
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/shm.h>
#include<sys/ipc.h>
void main()
{
key_t key;
int shmid;
int * p;
//1.建立共享記憶體
key=ftok( “.” , 255 ); //用當前路徑的目錄來確定kay值
if(key == -1) printf(“ftok error%m\n”) , exit( - 1 );
shmid=shmget( key , 4 , IPC_CREAT|IPC_EXCL | 0666 );
if(shmid == -1) printf(“shmget error %m\n”) , exit( -1 );
//2.掛載共享記憶體
p=shmat( shmid , 0 , 0);
if(p==(int *) - 1) printf(“at error %m\n”) , exit( -1 );
//3.訪問共享記憶體
*p=999;
//4.解除安裝共享記憶體
shmdt(shmid);
//刪除共享記憶體
shctl( shmid , IPC_RMID , 0);
}
不建立共享記憶體,只訪問已有的
shmB.c
ShmA.c
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/shm.h>
#include<sys/ipc.h>
void main()
{
key_t key;
int shmid;
int * p;
//1.得到共享記憶體
key=ftok( “.” , 255 ); //用當前路徑的目錄來確定kay值
if(key == -1) printf(“ftok error%m\n”) , exit( - 1 );
shmid=shmget( key , 4 ,0 );
if(shmid == -1) printf(“shmget error %m\n”) , exit( -1 );
//2.掛載共享記憶體
p=shmat( shmid , 0 , 0);
if(p==(int *) - 1) printf(“at error %m\n”) , exit( -1 );
//3.訪問共享記憶體
printf(“%d\n”,*p);
//4.解除安裝共享記憶體
shmdt(shmid);
}
2. 核心共享佇列(有序)
程式設計模型:
2.1.建立共享佇列/得到佇列msgget
2.2.使用佇列(傳送訊息msgsnd/接收訊息msgrcv)
2.3.刪除佇列msgctl
案例:
建立共享佇列
#include<sys/msg.h>
intmsgget(key_t,int); 除了不用指定大小,和shmget函式的引數一樣
傳送訊息
intmsgsnd(
intid,//訊息佇列ID
constvoid *msg,//要傳送訊息
size_tlen,//訊息的長度
int flags//傳送訊息的方式,建議為0
);
返回:
-1:失敗
0:成功
第二個引數的訊息有固定的格式
4位元組:表示訊息的型別
若干位元組:訊息內容。
訊息格式:(該結構體需要我們自己定義)
struct msgbuf{
long mtype; //訊息型別
char mtext[1]; //訊息內容
}
第三個引數:
訊息的大小,不包含型別的4個位元組
接收訊息:
size_t msgrcv(int id ,
void * msgp ,
size_t msgsz, ,
long msgtype , //那種型別的訊息
int msgflg);
返回:
-1:失敗
大小:成功
刪除佇列:msgctl 引數和shctl一樣
案例程式碼:
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct msgbuf
{
long type;
char data[32];
}
void main()
{
key_t key;
int msgid;
//1建立訊息佇列
key= ftok(“ . ” , 254);
if(key == -1) printf(“ftok error:%m\n”) , exit(-1);
msgid= msgget(key,IPC_CREAT | IPC_EXCL|0666);
if(msgid==-1) printf(“get error : %m\n”), exit(-1);
//2構造訊息
struct msgbuf msg;
//3傳送訊息
for(i=1;i<=10;i++)
{
msg.type=1; //訊息型別自己定義一個long型別
sprintf(msg.data , “Message:%d”,i);
msgsnd(msgid ,&msg , strlen(msg.data) , 0);
}
//4刪除佇列
//msgctl(msgid, IPC_RMID,0);
}
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct msgbuf
{
long type;
char data[32];
}
void main()
{
key_t key;
int msgid;
//1得到訊息佇列
key = ftok(“ . ” , 254);
if(key == -1) printf(“ftok error:%m\n”) , exit(-1);
msgid= msgget(key,0);
if(msgid== -1) printf(“get error : %m\n”), exit(-1);
//2構造訊息
struct msgbuf msg;
//3接收訊息
while(1)
{
bzero(&msg,sizeof(msg));
msgrcv(msgid , & msg sizeof(msg.data) , 1 , 0);
printf(“%s\n”,msg.data);
}
}
說明:如果訊息佇列中沒有訊息了讀取訊息的程式會阻塞等待
當程式傳送訊息到佇列中,一個程式讀取了所以訊息,佇列中就沒有訊息,就無法再讀取了,只能等待在傳送訊息後在讀取訊息。