linux进程间通信:system V消息队列
文章目錄
- 基本介紹
- 編程接口
- 代碼實例
- 消息隊列的發送和接收
- 消息隊列中的消息對象的屬性控制
基本介紹
- 支持不同進程之間以消息(messages)的形式進行數據交換,消息能夠擁有自己的標識,且內核使用鏈表方式進行消息管理。
- 進程之間的通信角色為:發送者和接受者
發送者:
a. 獲取消息隊列的ID(key或者msgid)
b. 將數據放入一個帶有標識的消息結構體,發送到消息隊列
接受者:
a. 獲取消息隊列的ID
b. 指定標識的消息從消息隊列中讀出,然后進一步后續處理 - 支持不同的進程標記不同的消息類型(1,2,3…),并由內核態維護對應消息類型的鏈表。
- 內核態的0號消息類型維護了一個鏈表,用來保存按照時間順序加入的消息
編程接口
-
生成ipc對象的唯一標識
key的接口
a. 頭文件<sys/ipc.h>
b. 函數聲明key_t ftok(const char *path, int id);
c. 參數描述
path需指定一個已經存在的可訪問的文件
id為用戶可自由指定的id -
創建或者打開一個消息隊列,并獲取system V 消息隊列中消息的身份標識
a. 頭文件<sys/types.h> <sys/ipc.h> <sys/msg.h>
b. 函數聲明int msgget (ket_t key, int msgflg)
c. 參數描述
key為ipc對象的唯一標識,生成的消息身份標識與該參數相關
msgflg當該函數沒有搜索到系統中與key值對應的消息隊列,則msgflg會指定IPC_CREAT,創建一個隊列,并返回消息標識
d. 返回值:成功返回消息身份標識,失敗返回-1 -
發送消息到消息隊列
a. 頭文件<sys/types.h> <sys/ipc.h> <sys/msg.h>
b. 函數聲明int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
c. 參數描述
msqid消息標識,類似于文件描述符fd
msgp消息內容指針
msgsz消息大小
msgflg當出現消息隊列沒有足夠的可用空間時,可以通過設置msgflg為IPC_NOWAIT來讓發送函數不產生阻塞,返回失敗
d. 返回值 失敗返回-1,以及對應失敗碼;成功則返回0 -
從消息隊列中接收消息
a. 頭文件<sys/types.h> <sys/ipc.h> <sys/msg.h>
b. 函數聲明ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
c. 參數描述
msqid消息標識,類似于文件描述符fd
msgp消息內容指針
msgsz消息大小,同時也是當前接收的消息最大能夠接收的大小
msgflg當出現實際的消息內容大于設定的msgsz,可以通過MSG_NOERROR來將消息裁剪為msgsz大小進行獲取,否則會返回失敗。而消息仍然存在于消息隊列。
msgtyp如果是0,則會讀取處于消息隊列的第一個消息;大于0,則會讀取對應type處于消息隊列中的第一個消息;如果小于0,則會讀取type絕對值或者小于絕對值的消息隊列的第一個消息。
d. 返回值 失敗返回-1,成功返回對應消息的大小 -
控制消息隊列的各個操作
a. 頭文件<sys/types.h> <sys/ipc.h> <sys/msg.h>
b. 函數聲明int msgctl(int msqid, int cmd, struct msqid_ds *buf);
c. 參數描述
msgqid消息標識
cmd針對消息標識的操作,合法的操作如下:IPC_STAT獲取msgqid的消息對象的信息,將各個屬性從內核拷貝到一個臨時的數據結構msgqid_ds類型的buf;調用者需要對消息隊列有讀權限IPC_SET自己可以通過臨時的msgqid_ds來設置內核中消息的對應msgqid_ds的屬性IPC_RMID立即移除消息隊列;當前調用者需要擁有 消息隊列的所有者權限,或者高于所有者的權限(root)IPC_INFO返回消息隊列的參數限制
其他標識可以通過
man msgctl來查看
代碼實例
消息隊列的發送和接收
發送端msg_snd.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>#define MSG_TYPE1 1
#define MSG_TYPE2 2 struct msgbuf
{long mtype;char mtext[100];
};int main()
{//當多用戶的時候通過指定文件以及設置id來獲取唯一的key標識//key_t key = ftok(".",100);key_t key = 12345; //個人使用的時候可以直接指定key//創建msg_qid的對象int msg_qid = msgget(key, IPC_CREAT | 0666);struct msgbuf msg;memset(&msg, 0 , sizeof(msg));//初始化消息類型以及消息內容msg.mtype = MSG_TYPE2;strncpy(msg.mtext, "hello world" , 80);//發送消息到消息標識的msg_qid IPC 對象中if( -1 == msgsnd(msg_qid,(void *)&msg,strlen(msg.mtext),0)) {printf("send msg failed\n");_exit(-1);}return 0;
}
編譯運行:
gcc msg_snd.c -o msg_snd
運行前查看系統消息隊列
ipcs -q
[root@node1 ~]# ipcs -q------ Message Queues --------
key msqid owner perms used-bytes messages
0x000004d2 0 root 666 0 0
運行后,可以看到我們發送了消息隊列的各個屬性信息。關于key值,它為我們設置的12345的16進制數值
[root@node1 ~]# ./msg_snd
[root@node1 ~]# ipcs -q------ Message Queues --------
key msqid owner perms used-bytes messages
0x000004d2 0 root 666 0 0
0x00003039 65538 root 666 11 1
接收端msg_rcv.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>#define MSG_TYPE1 1
#define MSG_TYPE2 2 struct msgbuf
{long mtype;char mtext[100];
};int main()
{key_t key = 12345;int msg_qid = msgget(key, IPC_CREAT | 0666);struct msgbuf msg;memset(&msg, 0 , sizeof(msg));if (-1 == msgrcv(msg_qid,(void *)&msg,sizeof(msg.mtext),MSG_TYPE2,0)) {printf("receive msg failed\n");_exit(-1);}printf("%s\n",msg.mtext);//當完成接收之后從消息隊列中刪除當前消息//msgctl(msg_id,IPC_RMID,NULL);return 0;
}
編譯運行,可以看到已經接手到了我們之前發送的內容:
[root@node1 ~]# gcc msg_rcv.c -o msg_rcv
[root@node1 ~]# ./msg_rcv
hello world
查看消息隊列情況,消息隊列中的數據已經被接收,所以在used-bytes和messages中看不到消息內容了,但是沒有刪除該消息隊列,所以消息標識仍然存在。我們可以在上述代碼中加入msgctl:
[root@node1 ~]# ipcs -q------ Message Queues --------
key msqid owner perms used-bytes messages
0x000004d2 0 root 666 0 0
0x00003039 65538 root 666 0 0
消息隊列中的消息對象的屬性控制
控制代碼msg_ctl.c
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
其中主要控制的是消息隊列一個數據結構,可以通過man msgctl查看 msqid_ds結構體
struct msqid_ds {struct ipc_perm msg_perm; /* Ownership and permissions */time_t msg_stime; /* Time of last msgsnd(2) */time_t msg_rtime; /* Time of last msgrcv(2) */time_t msg_ctime; /* Time of last change */unsigned long __msg_cbytes; /* Current number of bytes inqueue (nonstandard) */msgqnum_t msg_qnum; /* Current number of messagesin queue */msglen_t msg_qbytes; /* Maximum number of bytesallowed in queue */pid_t msg_lspid; /* PID of last msgsnd(2) */pid_t msg_lrpid; /* PID of last msgrcv(2) */};
以下代碼為主要控制參數的代碼,通過msgctl的cmd參數進程控制
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int main()
{key_t key = 12345;int msg_id = msgget(key,IPC_CREAT|0666);struct msqid_ds info;//第一次先從已存在的12345的消息中獲取隊列狀態if (-1 == msgctl(msg_id,IPC_STAT,&info)) {printf("control msg failed\n");_exit(-1);}//打印各個狀態信息printf("uid :%d,gid:%d,cuid:%d,cgid:%d\n",\info.msg_perm.uid,info.msg_perm.gid,info.msg_perm.cuid,info.msg_perm.cgid);printf("mode:%o3o,cbytes:%lu,qnum:%lu,qbytes:%lu\n",\info.msg_perm.mode & 0777,info.__msg_cbytes,info.msg_qnum,info.msg_qbytes);//嘗試設置消息隊列允許的最大字節內容info.msg_qbytes = 16000;//通過cmd為IPS_SET的標記進行設置if (-1 == msgctl(msg_id, IPC_SET, &info)) {printf("ipc_set failed\n");_exit(-1);}if (-1 == msgctl(msg_id, IPC_STAT, &info)) {printf("ipc_stat failed\n");_exit(-1);}printf("mode:%o3o,cbytes:%lu,qnum:%lu,qbytes:%lu\n",\info.msg_perm.mode & 0777,info.__msg_cbytes,info.msg_qnum,info.msg_qbytes);return 0;
}
輸出如下,可以看到我們控制的隊列允許的最大字節內容msg_qbytes已經設置進去:
[root@node1 ~]# ./msg_ctl
uid :0,gid:0,cuid:0,cgid:0
mode:6663o,cbytes:11,qnum:1,qbytes:16000
mode:6663o,cbytes:11,qnum:1,qbytes:16000
總結
以上是生活随笔為你收集整理的linux进程间通信:system V消息队列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux进程间通信:FIFO实现进程间
- 下一篇: 一般封阳台窗户多少钱