Linux 進程間的通信(二)—消息隊列
消息隊列提供了一種從一個進程向另一個進程發送一個數據塊的方法。 每個數據塊都被認爲含有一個類型,接收進程可以獨立地接收含有不同類型的數據結構。消息隊列與命名管道一樣,每個數據塊都有一個最大長度的限制
消息緩衝區結構
常用的結構是msgbuf結構。程序員可以以這個結構爲模板定義自己的消息結構。在頭文件<linux/msg.h>中,它的定義如下所示:
struct msgbuf {
long mtype;
char mtext[1];
};
獲得消息函數msgget()
創建一個新的消息隊列,或者訪問一個現有的隊列,可以使用函數msgget(),其原型如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
msgget()的第一個參數是鍵值,可以用ftok()函數生成,這個關鍵字的值將被拿來與內核中其他消息隊列的現有關鍵字值相比較。
msgflg是一個權限標誌,表示消息隊列的訪問權限,它與文件的訪問權限一樣。msgflg可以與IPC_CREAT做或操作,表示當key所命名的消息隊列不存在時創建一個消息隊列,如果key所命名的消息隊列存在時,IPC_CREAT標誌會被忽略,而只返回一個標識符。
發送消息函數msgsnd()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
成功返回0,失敗返回-1
函數msgsnd()的第一個參數是隊列標識符,它是前面調用msgget()獲得的返回值。第二個參數是msgp,它是一個void類型的指針,指向一個消息緩衝區。msgsz參數則包含着消息的大小,它是以字節爲單位的,其中不包括消息類型的長度(四個字節長)。 msgflg參數可以設置爲0(表示忽略),也可以設置爲IPC_NOWAIT。如果消息隊列已滿,則消息將不會被寫入到隊列中。如果沒有指定IPC_NOWAIT,則調用進程將被中斷(阻塞),直到可以寫消息爲止
接收消息函數msgrcv()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long sgtyp, int msgflg);
函數msgrcv()的第一個參數msqid是用來指定在消息獲取過程中所使用的隊列(該值是由前面調用msgget()得到的返回值)。
第二個參數msgp代表消息緩衝區變量的地址,獲取的消息將存放在這裏。
第三個參數msgsz代表消息緩衝區結構的大小,不包括mtype成員的長度。
第四個參數mtype指定要從隊列中獲取的消息的類型。內核將查找隊列中具有匹配類型的第一個到達的消息,並把它的一個複製返回到由msgp參數所指定的地址中。如果mtype參數傳送一個爲零的值,則將返回隊列中最新的消息,不管該消息的類型是什麼。
消息控制函數msgctl()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msgclt()向內核發送一個cmd命令,內核根據此來判斷進行何種操作,buf爲應用層和內核空間進行數據交換的指針。其中的cmd可以爲如下值:
IPC_STAT:獲取隊列的msqid_ds結構,並把它存放在buf變量所指定的地址中,通過這種方式,應用層可以獲得當前消息隊列的設置情況,例如是否有消息到來,消息隊列的緩衝區設置等等。
IPC_SET:設置隊列的msqid_ds結構的ipc_perm成員的值,它是從buf中取得該值的。通過IPC_SET命令,應用層可以設置消息隊列的狀態,例如修改消息隊列的權限,使其他用戶可以訪問或者不能訪問當前的隊列;甚至可以設置消息隊列的某些當前值來僞裝。
IPC_RMID:內核刪除隊列。使用此命令執行後,內核會把此消息隊列從系統中刪除。
struct msqid_ds {
struct ipc_perm msg_perm;
time_t msg_stime;/*發送到隊列的最後一個消息的時間戳*/
time_t msg_rtime;/*從隊列中獲取的最後一個消息的時間戳*/
time_t msg_ctime; /*對隊列進行最後一次變動的時間戳*/
unsigned long __msg_cbytes; /*在隊列上所駐留的字節的總數*/
msgqnum_t msg_qnum; /*當前處於隊列中的消息數目*
msglen_t msg_qbytes; /*隊列中能容納的字節的最大數目*/
pid_t msg_lspid; /*發送最後一個消息的進程的PID *
pid_t msg_lrpid; /*接收最後一個消息的進程的PID */
};
案例一、創建消息隊列,一個程序往裏面發送消息,另一個讀出消息,這是值寫入一種類型的消息。消息寫入程序結束後,發送的消息任在消息隊列中。
msgsend.c
/* ************************************************************************
* Filename: msgsend.c
* Description:
* Version: 1.0
* Created: 05/11/2020 12:30:05 AM
* Revision: none
* Compiler: gcc
* Author: YOUR NAME (WCT),
* Company:
* ************************************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>
#define MAX_TEXT 512
struct msg_st{
long int msg_type;
char text[MAX_TEXT];
};
int main(){
int running = 1;
struct msg_st data;
char buffer[BUFSIZ];
int msgid = -1;
// 創建消息隊列
msgid = msgget( (key_t)1234, 0666 | IPC_CREAT);
if( msgid == -1 ){
fprintf(stderr, "mesage failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
while( running ){
printf("Enter some string: ");
fgets(buffer, BUFSIZ, stdin);
data.msg_type = 1;
strcpy(data.text, buffer);
// 向消息隊列發送數據
if( msgsnd( msgid, (void *)&data, MAX_TEXT, 0) == -1){
fprintf(stderr, "msgsnd failed\n");
exit( EXIT_FAILURE );
}
if( strncmp( buffer, "end", 3) == 0 ){
running = 0;
}
sleep(1);
}
exit( EXIT_SUCCESS );
}
msgrcv.c
/* ************************************************************************
* Filename: msgrcv.c
* Description:
* Version: 1.0
* Created: 05/11/2020 12:30:05 AM
* Revision: none
* Compiler: gcc
* Author: YOUR NAME (WCT),
* Company:
* ************************************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>
#define MAX_TEXT 512
struct msg_st{
long int msg_type;
char text[MAX_TEXT];
};
int main(){
int running = 1;
struct msg_st data;
int msgid = -1;
int msgtype = 1;
// 創建消息隊列
msgid = msgget( (key_t)1234, 0666 | IPC_CREAT);
if( msgid == -1 ){
fprintf(stderr, "mesage failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
while( running ){
// 向消息隊列接受數據
if( msgrcv( msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1){
fprintf(stderr, "msgrcv failed with erron: %d\n", errno);
exit( EXIT_FAILURE );
}
printf("Rcv Msgage: %s", data.text);
if( strncmp( data.text, "end", 3) == 0 ){
running = 0;
}
}
if( msgctl(msgid, IPC_RMID, 0) == -1 ){
fprintf( stderr, "msgctl(IPC_RMID) failed\n");
exit( EXIT_FAILURE );
}
exit( EXIT_SUCCESS );
}
BUFSIZ 在 /usr/include/stdio.h
裏定義
編譯運行結果
案例一、寫入不同類型的消息,按要求類型讀取消息
msgsend1.c
/* ************************************************************************
* Filename: msgsend1.c
* Description:
* Version: 1.0
* Created: 05/11/2020 12:30:05 AM
* Revision: none
* Compiler: gcc
* Author: YOUR NAME (WCT),
* Company:
* ************************************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>
#define MAX_TEXT 512
struct msg_st{
long int msg_type;
char text[MAX_TEXT];
};
int main(){
int running = 1;
struct msg_st data;
char buffer[BUFSIZ];
int msgid = -1;
// 創建消息隊列
msgid = msgget( (key_t)1234, 0666 | IPC_CREAT);
if( msgid == -1 ){
fprintf(stderr, "mesage failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
while( running ){
printf("Enter some string1: ");
fgets(buffer, BUFSIZ, stdin);
data.msg_type = 1;
strcpy(data.text, buffer);
// 向消息隊列發送數據
if( msgsnd( msgid, (void *)&data, MAX_TEXT, 0) == -1){
fprintf(stderr, "msgsnd failed\n");
exit( EXIT_FAILURE );
}
if( strncmp( buffer, "end", 3) == 0 ){
data.msg_type = 2;
if( msgsnd( msgid, (void *)&data, MAX_TEXT, 0) == -1){
fprintf(stderr, "msgsnd failed\n");
exit( EXIT_FAILURE );
}
break;
}
sleep(1);
printf("Enter some string2: ");
fgets(buffer, BUFSIZ, stdin);
data.msg_type = 2;
strcpy(data.text, buffer);
// 向消息隊列發送數據
if( msgsnd( msgid, (void *)&data, MAX_TEXT, 0) == -1){
fprintf(stderr, "msgsnd failed\n");
exit( EXIT_FAILURE );
}
if( strncmp( buffer, "end", 3) == 0 ){
data.msg_type = 1;
if( msgsnd( msgid, (void *)&data, MAX_TEXT, 0) == -1){
fprintf(stderr, "msgsnd failed\n");
exit( EXIT_FAILURE );
}
break;
}
sleep(1);
}
exit( EXIT_SUCCESS );
}
msgrcv.c
/* ************************************************************************
* Filename: msgrcv.c
.c
* Description:
* Version: 1.0
* Created: 05/11/2020 12:30:05 AM
* Revision: none
* Compiler: gcc
* Author: YOUR NAME (WCT),
* Company:
* ************************************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>
#define MAX_TEXT 512
struct msg_st{
long int msg_type;
char text[MAX_TEXT];
};
int main(){
int running = 1;
struct msg_st data;
int msgid = -1;
int msgtype = 1;
// 創建消息隊列
msgid = msgget( (key_t)1234, 0666 | IPC_CREAT);
if( msgid == -1 ){
fprintf(stderr, "mesage failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
while( running ){
// 向消息隊列接受數據
if( msgrcv( msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1){
fprintf(stderr, "msgrcv failed with erron: %d\n", errno);
exit( EXIT_FAILURE );
}
printf("Rcv Msgage: %s", data.text);
if( strncmp( data.text, "end", 3) == 0 ){
running = 0;
}
}
if( strncmp( data.text, "end", 3) == 0 ){
if ( msgget( (key_t)1234, 0666) != -1 ){
if( ( msgctl(msgid, IPC_RMID, 0) == -1 ) ){
fprintf( stderr, "msgctl(IPC_RMID) failed\n");
exit( EXIT_FAILURE );
}
}
}
exit( EXIT_SUCCESS );
}
msgrcv1.c
/* ************************************************************************
* Filename: msgrcv1.c
* Description:
* Version: 1.0
* Created: 05/11/2020 12:30:05 AM
* Revision: none
* Compiler: gcc
* Author: YOUR NAME (WCT),
* Company:
* ************************************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>
#define MAX_TEXT 512
struct msg_st{
long int msg_type;
char text[MAX_TEXT];
};
int main(){
int running = 1;
struct msg_st data;
int msgid = -1;
int msgtype = 2;
// 創建消息隊列
msgid = msgget( (key_t)1234, 0666 | IPC_CREAT);
if( msgid == -1 ){
fprintf(stderr, "mesage failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
while( running ){
// 向消息隊列接受數據
if( msgrcv( msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1){
fprintf(stderr, "msgrcv failed with erron: %d\n", errno);
exit( EXIT_FAILURE );
}
printf("Rcv2 Msgage: %s", data.text);
if( strncmp( data.text, "end", 3) == 0 ){
running = 0;
}
}
if( ( msgget( (key_t)1234, 0666) != -1 ) && ( msgctl(msgid, IPC_RMID, 0) == -1 ) ){
fprintf( stderr, "msgctl(IPC_RMID) failed\n");
exit( EXIT_FAILURE );
}
exit( EXIT_SUCCESS );
}