Linux IPC msg
消息队列
消息队列是Linux内一种进程间通信的方式。
Linux下消息队列的应用
消息队列相关接口
msgget
获取或者创建一个消息队列。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
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);
msgrcv
从某个消息队列读取消息
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
消息队列Example
Sender
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
typedef struct msg_st {
long msg_type;
char content[1024];
} Message;
int main() {
//create one message queue
int msgid = -1;
Message message;
msgid = msgget((key_t) 0x01, 0666 | IPC_CREAT);
if(msgid == -1) {
printf("create message queue failed\n");
return -1;
}
//Send three message
message.msg_type = 1;
for(int i = 0; i < 3; i++) {
strcpy(message.content, "hello");
msgsnd(msgid, &message, 1024, 0);
}
printf("Sender Job finished\n");
return 0;
}
Receiver
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
typedef struct msg_st {
long msg_type;
char content[1024];
} Message;
int main() {
//create one message queue
int msgid = -1;
Message message;
msgid = msgget((key_t) 0x01, 0666 | IPC_CREAT);
if(msgid == -1) {
printf("create or get message queue failed\n");
return -1;
}
//receive three message
for(int i = 0; i < 3; i++) {
msgrcv(msgid, &message, 1024, 0, 0);
printf("message type %d\n", message.msg_type);
printf("message information %s\n", message.content);
}
//If needed, need to delete the message queue.
if (msgctl(msgid, IPC_RMID, 0) == -1)
{
printf("msgctl(IPC_RMID) failed\n");
}
return 0;
}
Linux 消息队列在Kernel的实现
相关的数据结构
msgget
msgget 的主要功能就是从已知的msg_ids消息队列数组中找到对应key的消息队列,如果没有找到,就调用new_queue来malloc一个新的队列,并添加到msg_ids.
asmlinkage long sys_msgget (key_t key, int msgflg)
{
int id, ret = -EPERM;
struct msg_queue *msq;
down(&msg_ids.sem);
if (key == IPC_PRIVATE)
ret = newque(key, msgflg);
// Try to find exist key from msg_ids, if fail, create one new queue.
else if ((id = ipc_findkey(&msg_ids, key)) == -1) { /* key not used */
if (!(msgflg & IPC_CREAT))
ret = -ENOENT;
else
ret = newque(key, msgflg);
} else if (msgflg & IPC_CREAT && msgflg & IPC_EXCL) {
ret = -EEXIST;
}
up(&msg_ids.sem);
return ret;
}
static int newque (key_t key, int msgflg)
{
int id;
int retval;
struct msg_queue *msq;
msq = ipc_rcu_alloc(sizeof(*msq));
if (!msq)
return -ENOMEM;
msq->q_perm.mode = (msgflg & S_IRWXUGO);
msq->q_perm.key = key;
msq->q_perm.security = NULL;
retval = security_msg_queue_alloc(msq);
if (retval) {
ipc_rcu_free(msq, sizeof(*msq));
return retval;
}
id = ipc_addid(&msg_ids, &msq->q_perm, msg_ctlmni);
if(id == -1) {
security_msg_queue_free(msq);
ipc_rcu_free(msq, sizeof(*msq));
return -ENOSPC;
}
msq->q_stime = msq->q_rtime = 0;
msq->q_ctime = get_seconds();
msq->q_cbytes = msq->q_qnum = 0;
msq->q_qbytes = msg_ctlmnb;
msq->q_lspid = msq->q_lrpid = 0;
INIT_LIST_HEAD(&msq->q_messages);
INIT_LIST_HEAD(&msq->q_receivers);
INIT_LIST_HEAD(&msq->q_senders);
msg_unlock(msq);
return msg_buildid(id,msq->q_perm.seq);
}
msgsnd
load_msg把消息内容从用用户态拷贝到内核态,并把msg_msg添加到对应的msg_id 消息队列的链表中。
asmlinkage long sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
{
msg = load_msg(msgp->mtext, msgsz);
msg->m_type = mtype;
msg->m_ts = msgsz;
msq = msg_lock(msqid);
if(!pipelined_send(msq,msg)) {
/* noone is waiting for this message, enqueue it */
list_add_tail(&msg->m_list,&msq->q_messages);
msq->q_cbytes += msgsz;
msq->q_qnum++;
atomic_add(msgsz,&msg_bytes);
atomic_inc(&msg_hdrs);
}
}
msgrcv
如果从对应的消息队列找到了message,拷贝到应用层,并从消息队列链表中,删除该消息。
asmlinkage long sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,
long msgtyp, int msgflg)
{
tmp = msq->q_messages.next;
found_msg=NULL;
while (tmp != &msq->q_messages) {
msg = list_entry(tmp,struct msg_msg,m_list);
if(testmsg(msg,msgtyp,mode) &&
!security_msg_queue_msgrcv(msq, msg, current, msgtyp, mode)) {
found_msg = msg;
if(mode == SEARCH_LESSEQUAL && msg->m_type != 1) {
found_msg=msg;
msgtyp=msg->m_type-1;
} else {
found_msg=msg;
break;
}
}
tmp = tmp->next;
}
if(found_msg) {
msg=found_msg;
if ((msgsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) {
err=-E2BIG;
goto out_unlock;
}
list_del(&msg->m_list);
msq->q_qnum--;
msq->q_rtime = get_seconds();
msq->q_lrpid = current->tgid;
msq->q_cbytes -= msg->m_ts;
atomic_sub(msg->m_ts,&msg_bytes);
atomic_dec(&msg_hdrs);
ss_wakeup(&msq->q_senders,0);
msg_unlock(msq);
out_success:
msgsz = (msgsz > msg->m_ts) ? msg->m_ts : msgsz;
if (put_user (msg->m_type, &msgp->mtype) ||
store_msg(msgp->mtext, msg, msgsz)) {
msgsz = -EFAULT;
}
free_msg(msg);
return msgsz;
}
}