System V IPC 機制:消息隊列
消息隊列
消息隊列與 FIFO 很相似,都是一個隊列結構,都可以有多個進程往隊列裏面寫信息,多個進程從隊列中讀取信息。但 FIFO 需要讀、寫的兩端事先都打開,才能夠開始信息傳遞工作。而消息隊列可以事先往隊列中寫信息,需要時再打開讀取信息。但是,消息隊列先打開讀,仍然會阻塞,因爲此時沒有消息可讀。
函數原型:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg);
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
函數 msgget()
int msgget(key_t key, int msgflg);
創建和訪問一個消息隊列。該函數成功則返回一個唯一的消息隊列標識符(類似於進程 ID 一樣),失敗則返回-1。
參數 key 是唯一標識一個消息隊列的關鍵字,如果爲IPC_PRIVATE(值爲 0),用創建一個只有創建者進程纔可以訪問的消息隊列, 可以用於父子間通信; 非0值的 key值(可以通過 ftok 函數獲得)表示創建一個可以被多個進程共享的消息隊列;
參數 msgflg 指明消息隊列的訪問權限和創建標誌,創建標誌的可選值爲IPC_CREAT和IPC_EXCL 。如果單獨指定 IPC_CREAT,msgget 要麼返回新創建的消息隊列 id,要麼返回具有相同 key 值的消息隊列 id。 如果 IPC_EXCL 和 IPC_CREAT 同時設定, 則要麼創建新的消息隊列,要麼當隊列存在時,調用失敗並返回-1。
msgget.c
#include <func.h>
typedef struct msgbuf{
long mtype;
char mtext[64];
}MSG_T;
int main(int argc, char* argv[])
{
int msgid=msgget(1000,IPC_CREAT|0600);
ERROR_CHECK(msgid,-1,"msgget");
struct msgbuf msgInfo;
msgInfo.mtype = 1;
strcpy(msgInfo.mtext,"hello");
int ret=msgsnd(msgid,&msgInfo,strlen(msgInfo.mtext),0);
ERROR_CHECK(ret,-1,"msgsnd");
return 0;
}
函數msgsnd()和函數msgrcv()
int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg);
函數 msgsnd 和 msgrcv 用來將消息添加到消息隊列中和從一個消息隊列中獲取信息。
參數 msgid 指明消息隊列的 ID; 通常是 msgget 函數成功的返回值。
參數 msgbuf 是消息結構體,它的長度必須小於系統規定的上限,必須以一個長整型成員變量開始, 接收函數將用這個成員變量來確定消息的類型。 必須重寫這個結構體,其中第一個參數不能改,其他自定義。如下:
struct msgbuf {
long mtype; /* type of message */
char mtext[1]; /* message text */
};
字段 mtype 是用戶自己指定的消息類型(必須是正整數),該結構體第 2 個成員僅僅是一種說明性的結構,實際上用戶可以使用任何類型的數據,指消息內容。
參數 msgsz 是消息體的大小,每個消息體最大不要超過 4K。
參數 msgflg 可以爲 0(通常爲 0)或 IPC_NOWAIT,如果設置 IPC_NOWAIT,則msgsnd 和 msgrcv 都不會阻塞,此時如果隊列滿並調用 msgsnd 或隊列空時調用 msgrcv將返回錯誤。
參數 msgtyp 有 3 種選項:
msgtyp == 0 接收隊列中的第 1 個消息(通常爲 0)
msgtyp > 0 接收對列中的第 1 個類型等於 msgtyp 的消息
msgtyp < 0 接收其類型小於或等於 msgtyp 絕對值的第 1 個最低類型消息
msgget_msgsnd.c
#include <func.h>
typedef struct msgbuf{
long mtype;
char mtext[64];
}MSG_T;
int main(int argc, char* argv[])
{
ARGS_CHECK(argc,3);
int msgid=msgget(1000,IPC_CREAT|0600);
ERROR_CHECK(msgid,-1,"msgget");
struct msgbuf msgInfo;
msgInfo.mtype = atoi(argv[1]);
strcpy(msgInfo.mtext,argv[2]);
int ret=msgsnd(msgid,&msgInfo,strlen(msgInfo.mtext),0);
ERROR_CHECK(ret,-1,"msgsnd");
return 0;
}
msgget_msgrcv.c
#include <func.h>
typedef struct msgbuf{
long mtype;
char mtext[64];
}MSG_T;
int main(int argc, char* argv[])
{
int msgid=msgget(1000,IPC_CREAT|0600);
ERROR_CHECK(msgid,-1,"msgget");
struct msgbuf msgInfo;
msgInfo.mtype = 1;
strcpy(msgInfo.mtext,"hello");
int ret=msgsnd(msgid,&msgInfo,strlen(msgInfo.mtext),0);
ERROR_CHECK(ret,-1,"msgsnd");
return 0;
}
msgget_msgrcv_nowait.c
#include <func.h>
typedef struct msgbuf{
long mtype;
char mtext[64];
}MSG_T;
int main(int argc, char* argv[])
{
ARGS_CHECK(argc,2);
int msgid=msgget(1000,IPC_CREAT|0600);
ERROR_CHECK(msgid,-1,"msgget");
struct msgbuf msgInfo;
bzero(&msgInfo,sizeof(msgInfo));
int ret=msgrcv(msgid,&msgInfo,sizeof(msgInfo.mtext),atoi(argv[1]),IPC_NOWAIT);
printf("errno=%d\n",errno);
ERROR_CHECK(ret,-1,"msgrcv");
printf("mtype=%ld,mtext=%s\n",msgInfo.mtype,msgInfo.mtext);
return 0;
}
函數msgctl()
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
函數 msgctl 是消息隊列的控制函數,常用來刪除消息隊列。
msgctl() performs the control operation specified by cmd on the System V message queue with identifier msqid.
參數 msqid 是由 msgget 返回的消息隊列標識符。
參數 cmd 通常爲 IPC_RMID 表示刪除消息隊列。
IPC_STAT: 讀取消息隊列的數據結構msqid_ds,並將其存儲在buf指定的地址
IPC_STAT: Copy information from the kernel data structure associated with msqid into the msqid_ds structure pointed to by buf. The caller must have read permission on the message queue.
IPC_SET: 設置消息隊列的數據結構msqid_ds中的ipc_perm元素的值。這個值取自buf參數
IPC_SET: Write the values of some members of the msqid_ds structure pointed to by buf to the kernel data structure associated with this message queue, updating also its msg_ctime member. The following members of the structure are updated: msg_qbytes, msg_perm.uid, msg_perm.gid, and msg_perm.mode.
IPC_RMID: 從系統內核中移走消息隊列
IPC_RMID: Immediately remove the message queue, awakening all waiting reader and writer pro‐cesses (with an error return and errno set to EIDRM).
參數 buf 通常爲 NULL 即可。
消息隊列的msqid_ds結構,描述了消息隊列當前的狀態。對於每一個消息隊列都有一個msqid_ds來描述消息隊列當前的狀態。
struct 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 in queue (nonstandard)*/
msgqnum_t msg_qnum; /* Current number of messages in queue */
msglen_t msg_qbytes; /* Maximum number of bytes allowed in queue */
pid_t msg_lspid; /* PID of last msgsnd(2) */
pid_t msg_lrpid; /* PID of last msgrcv(2) */
};
struct ipc_perm:
消息隊列中的數據結構中唯一可以改動的元素就是ipc_perm。它包括隊列的存取權限和關於隊列創建者和擁有者的信息。僅可以改變用戶的id、用戶的組id以及消息隊列的存取權限
The ipc_perm structure is defined as follows , the highlighted fields are settable using IPC_SET
uid gid mode
struct ipc_perm {
key_t __key; /* Key supplied to msgget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions */
unsigned short __seq; /* Sequence number */
};
msgget_msgctl.c
#include <func.h>
typedef struct msgbuf{
long mtype;
char mtext[64];
}MSG_T;
int main(int argc, char* argv[])
{
int msgid=msgget(1000,IPC_CREAT|0600);
ERROR_CHECK(msgid,-1,"msgget");
int ret=msgctl(msgid,IPC_RMID,NULL);
ERROR_CHECK(ret,-1,"msgctl");
return 0;
}