Chinaunix

标题: 消息队列的好处 [打印本页]

作者: KMitnick    时间: 2010-01-08 10:39
标题: 消息队列的好处
最近一个在mmo游戏的服务器,上面用到了消息队列。

觉得unix/linux下面的消息队列还是挺好的,可以支持多进程对一个同一个队列同时读写,因为对于消息队列
内核层已经为我们解决了同步问题,在用户层不需要额外的同步代码,直接使用就可以了。

下面贴一段代码是关于发送消息的msgsnd是实现:
关于代码的帖子:
http://topic.csdn.net/u/20090415/18/a8751048-19c4-4da3-b41d-46897cc5f2c7.html
// 先看msgsnd()函数,它通过系统调用接口界面,进入内核执行,代码如下:
SYSCALL_DEFINE4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,
        int, msgflg)
{
    long mtype;
    if (get_user(mtype, &msgp->mtype))
        return -EFAULT;
    return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg);
}
// 接下来看do_msgsnd()部分的代码,如下:
long do_msgsnd(int msqid, long mtype, void __user *mtext,
        size_t msgsz, int msgflg)
{
    struct msg_queue *msq;
    struct msg_msg *msg;
    int err;
    struct ipc_namespace *ns;
    ns = current->nsproxy->ipc_ns;
    if (msgsz > ns->msg_ctlmax || (long) msgsz  0 || msqid  0)
        return -EINVAL;
    if (mtype  1)
        return -EINVAL;
    msg = load_msg(mtext, msgsz);
    if (IS_ERR(msg))
        return PTR_ERR(msg);
    msg->m_type = mtype;
    msg->m_ts = msgsz;
    msq = msg_lock_check(ns, msqid);
    if (IS_ERR(msq)) {
        err = PTR_ERR(msq);
        goto out_free;
    }
    for (;;) {
        struct msg_sender s;
        err = -EACCES;
        if (ipcperms(&msq->q_perm, S_IWUGO))
            goto out_unlock_free;
        err = security_msg_queue_msgsnd(msq, msg, msgflg);
        if (err)
            goto out_unlock_free;
        if (msgsz + msq->q_cbytes = msq->q_qbytes &&
                1 + msq->q_qnum = msq->q_qbytes) {
            break;
        }
        /* queue full, wait: */
        if (msgflg & IPC_NOWAIT) {
            err = -EAGAIN;
            goto out_unlock_free;
        }
        ss_add(msq, &s);
        ipc_rcu_getref(msq);
        msg_unlock(msq);
        schedule();
        ipc_lock_by_ptr(&msq->q_perm);
        ipc_rcu_putref(msq);
        if (msq->q_perm.deleted) {
            err = -EIDRM;
            goto out_unlock_free;
        }
        ss_del(&s);
        if (signal_pending(current)) {
            err = -ERESTARTNOHAND;
            goto out_unlock_free;
        }
    }
    msq->q_lspid = task_tgid_vnr(current);
    msq->q_stime = get_seconds();
    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, &ns->msg_bytes);
        atomic_inc(&ns->msg_hdrs);
    }
    err = 0;
    msg = NULL;
out_unlock_free:
    msg_unlock(msq);
out_free:
    if (msg != NULL)
        free_msg(msg);
    return err;
}
// 在这段代码中,请注意临近入口位置的这个函数msg_lock_check(),我们跟进,看一下这个lock是如何check
// 的,代码如下:
static inline struct msg_queue *msg_lock_check(struct ipc_namespace *ns,
                        int id)
{
    struct kern_ipc_perm *ipcp = ipc_lock_check(&msg_ids(ns), id);
    if (IS_ERR(ipcp))
        return (struct msg_queue *)ipcp;
    return container_of(ipcp, struct msg_queue, q_perm);
}
// ipc_lock_check()是一个能够check所有IPC object同步信息的函数,它的定义如下:
struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, int id)
{
    struct kern_ipc_perm *out;
    out = ipc_lock(ids, id);
    if (IS_ERR(out))
        return out;
    if (ipc_checkid(out, id)) {
        ipc_unlock(out);
        return ERR_PTR(-EIDRM);
    }
    return out;
}
// 这里的ipc_lock()是至关重要的地方!通过这个函数的注释,也能明白它的作用了:
/**
* ipc_lock - Lock an ipc structure without rw_mutex held
* @ids: IPC identifier set
* @id: ipc id to look for
*
* Look for an id in the ipc ids idr and lock the associated ipc object.
*
* The ipc object is locked on exit.
*/
struct kern_ipc_perm *ipc_lock(struct ipc_ids *ids, int id)
{
    struct kern_ipc_perm *out;
    int lid = ipcid_to_idx(id);
    rcu_read_lock();
    out = idr_find(&ids->ipcs_idr, lid);
    if (out == NULL) {
        rcu_read_unlock();
        return ERR_PTR(-EINVAL);
    }
    spin_lock(&out->lock);
   
    /* ipc_rmid() may have already freed the ID while ipc_lock
     * was spinning: here verify that the structure is still valid
     */
    if (out->deleted) {
        spin_unlock(&out->lock);
        rcu_read_unlock();
        return ERR_PTR(-EINVAL);
    }
    return out;
}
对于msgrcv同理可得,都是在内核层已经加锁了。。。。。。。

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/109128/showart_2143673.html




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2