Lixnux的消息創建、發送與接收
什么是消息?
消息(message)是一個格式化的可變長的信息單元。消息機制允許由一個進程給其它任意的進程發送一個消息。當一個進程收到多個消息時,可將它們排成一個消息隊列。消息使用二種重要的數據結構:一是消息首部,其中記錄了一些與消息有關的信息,如消息數據的字節數;二個消息隊列頭表,其每一表項是作為一個消息隊列的消息頭,記錄了消息隊列的有關信息。
消息的系統函數用法
1. msgget( )
創建一個消息,獲得一個消息的描述符。核心將搜索消息隊列頭表,確定是否有指定名字的消息隊列。若無,核心將分配一新的消息隊列頭,並對它進行初始化,然后給用戶返回一個消息隊列描述符,否則它只是檢查消息隊列的許可權便返回。
msgqid=msgget(key,flag)
key
是用戶指定的消息隊列的名字;flag
是用戶設置的標志和訪問方式。
如 IPC_CREAT |0400 是否該隊列已被創建。無則創建,是則打開;
IPC_EXCL |0400 是否該隊列的創建應是互斥的。
msgqid 是該系統調用返回的描述符,失敗則返回-1。
2. msgsnd()
發送一消息。向指定的消息隊列發送一個消息,並將該消息鏈接到該消息隊列的尾部。
msgsnd(msgqid,msgp,size,flag)
msgqid是返回消息隊列的描述符;
msgp是指向用戶消息緩沖區的一個結構體指針。緩沖區中包括消息類型和消息正文。
即
{
long mtype; /消息類型/
char mtext[ ]; /消息的文本/
}
size指示由msgp指向的數據結構中字符數組的長度;即消息的長度。
flag規定當核心用盡內部緩沖空間時應執行的動作:進程是等待,還是立即返回。設置為0表示阻塞方式,設置IPC_NOWAIT 表示非阻塞方式
3. msgrcv( )
msgrcv(msgqid,msgp,size,type,flag)
除了type參數,其他同上。
type:分成三種情況處理:
type=0,接收該隊列的第一個消息,並將它返回給調用者
type為正整數,接收類型type的第一個消息
type為負整數,接收小於等於type絕對值的最低類型的第一個消息
Linux下查看消息的有關命令
查看消息隊列等等相關信息
ipcs
只查看消息隊列
ipcs -p
根據消息隊列的msqid刪除消息隊列
ipcrm -q 163840
調試例子
1、通過消息機制實現進程間的數據通信,client向server發送“I am client!”
client.c
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <stdlib.h>
#define MSGKEY 75
//1.新建一個結構體,用於存放將要發送的消息
struct msgform
{
long mtype;
char mtext[1000];
} msg;
int msgqid = -1;
void client()
{
int i;
//2.創建一個名為為75可讀可寫的消息隊列
msgqid = msgget(MSGKEY, 0777); /*打開75#消息隊列*/
if(msgqid == -1){
printf("client的消息隊列創建失敗\n");
}
//3.向消息隊列發送消息
strcpy(msg.mtext,"I am client!");
for (i = 3; i >= 1; i--)
{
msg.mtype = i;//i為消息的類型
printf("(server)send: %s\n",msg.mtext);
msgsnd(msgqid, &msg, 1024, 0); /*發送消息*/
}
exit(0);
}
main()
{
client();
}
server.c
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#define MSGKEY 75
//1.新建一個結構體,用於存放將要接受的消息
struct msgform
{
long mtype;
char mtext[1000];
} msg;
int msgqid;
void server()
{
//2.創建一個名為為75可讀可寫的消息隊列
msgqid = msgget(MSGKEY, 0777 | IPC_CREAT); /*創建75#消息隊列*/
//3.監聽消息隊列中的消息
do
{
msgrcv(msgqid, &msg, 1030, 0, 0); /*接收消息:倒數第二個0代表接受任何消息*/
printf("(server)received: %s\n",msg.mtext);
} while (msg.mtype != 1);//當接受到消息時,返回值為1,跳出循環
msgctl(msgqid, IPC_RMID, 0); /*刪除消息隊列,歸還資源*/
exit(0);
}
main()
{
server();
}
運行結果:
Linux的共享存儲區通信
什么是共享存儲?
共享存儲如同其名字所隱喻的,是物理存儲器中一段可由兩個以上的進程共享的存儲空間。共享存儲段具有大小和物理存儲地址。想要訪問共享存儲段的進程可以連接這段存儲區域到自己的地址空間中任何適合的地方,其他進程也一樣。這樣,多個進程便可以訪問相同的物理存儲。其中箭頭表示進程的邏輯存儲與物理存儲的映射關系。
共享儲存區通信的函數用法
1、shmget( )
創建、獲得一個共享存儲區。
shmid=shmget(key,size,flag)
key是共享存儲區的名字
size是其大小(以字節計)
flag是用戶設置的標志
2、shmat( )
共享存儲區的附接。從邏輯上將一個共享存儲區附接到進程的虛擬地址空間上。
virtaddr=shmat(shmid,addr,flag)
shmid是共享存儲區的標識符
addr是用戶給定的,將共享存儲區附接到進程的虛地址空間
flag規定共享存儲區的讀、寫權限,以及系統是否應對用戶規定的地址做舍入操作,該系統調用的返回值是共享存儲區所附接到的進程虛地址viraddr。
3、shmdt( )
把一個共享存儲區從指定進程的虛地址空間斷開。
shmdt(addr)
addr是要斷開連接的虛地址,亦即以前由連接的系統調用shmat( )所返回的虛地址。調用成功時,返回0值,調用不成功,返回-1。
4、shmctl( )
共享存儲區的控制,對其狀態信息進行讀取和修改。
shmctl(shmid,cmd,buf)
shmid是上邊那個id
buf是用戶緩沖區地址
cmd是操作命令
2、通過共享內存機制實現進程間的數據通信,client向server發送“I am client!”
sharem.c
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define SHMKEY 75
int shmid, i;
char *addr;
void client()
{
int i;
//1.取得共享存儲區id
shmid = shmget(SHMKEY, 1024, 0777);
//2.使當前的子進程的虛擬地址空間附接到共享儲存區,shmat函數中shimid為共享存儲區id,第二個0為地址,第三個0表示可讀可寫,shmat的返回值是共享區所附接到的虛地址addr
printf("client進程獲取存儲區id成功\n");
addr = shmat(shmid, 0, 0); /*獲得共享存儲區首地址:這個addr其實就是個字符串指針指向的一個區域,因為父進程和子進程都能訪問到這個地址,因此子進程向地址中寫入數據,父進程取出數據,這樣就完成了共享存儲區的信息通信*/
printf("client進程附接存儲區地址成功\n");
//3.向共享存儲區寫信息
for (i = 3; i >= 0; i--)
{
strcpy(addr, "I am client!");
while (strcmp(addr, "waiting") != 0)
;
printf("(client) reading:%s\n", addr);
}
printf("client進程發送信息成功\n");
//4.發送信息完畢,發送一個結束的標志信息
strcpy(addr, "endding");
exit(0);
}
void server()
{
//1.取得共享存儲區id
shmid = shmget(SHMKEY, 1024, 0777 | IPC_CREAT); /*創建共享存儲區*/
printf("server進程獲取存儲區id成功\n");
//2.使當前的子進程的虛擬地址空間附接到共享儲存區
addr = shmat(shmid, 0, 0); /*獲取首地址*/
printf("server進程附接存儲區地址成功\n");
//3.不斷向共享存儲區讀消息
do
{
strcpy(addr, "waiting");
while (strcmp(addr, "waiting") == 0)
;
printf("(server) received:%s\n", addr);
} while (strcmp(addr, "endding") != 0);
printf("server進程發送信息成功\n");
shmctl(shmid, IPC_RMID, 0); /*撤消共享存儲區,歸還資源*/
exit(0);
}
main()
{
while ((i = fork()) == -1)
;
if (!i) //令一個子進程執行server
server();
system("ipcs - m"); //終端執行ipcs - m
while ((i = fork()) == -1)
;
if (!i) //另一個子進程執行client
client();
wait(0);
wait(0);
}
運行結果: