next up previous contents
下一篇: msgtool:一个交互式消息 上一级: 6.4.2 消息队列 上一篇: 系统调用:msgsnd()

系统调用:msgctl()

通过前面介绍的包装函数的开发,您现在有了一种简单、有点优雅的方法来在您的应用程序中创建和使用消息队列。现在,我们将讨论转向直接操作与给定消息队列相关的内部结构。

要对消息队列执行控制操作,您可以使用msgctl()系统调用。


  SYSTEM CALL: msgctl();
  PROTOTYPE: int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
    RETURNS: 0 on success
             -1 on error: errno = EACCES (No read permission and cmd is IPC_STAT)
                                  EFAULT (Address pointed to by buf is invalid with IPC_SET and
                                          IPC_STAT commands)
                                  EIDRM  (Queue was removed during retrieval)
                                  EINVAL (msgqid invalid, or msgsz less than 0)
                                  EPERM  (IPC_SET or IPC_RMID command was issued, but
                                          calling process does not have write (alter)
                                          access to the queue)
  NOTES:

现在,常理告诉我们,直接操作内核内部数据结构可能会导致一些麻烦。不幸的是,如果您喜欢破坏 IPC 子系统,那么程序员因此需要承担的责任才能被归类为乐趣。通过使用msgctl()以及一组特定的命令,您有能力操作那些不太可能引起麻烦的项目。让我们看看这些命令

IPC_STAT

检索队列的 msqid_ds 结构,并将其存储在 buf 参数的地址中。

IPC_SET

设置队列的 msqid_ds 结构的 ipc_perm 成员的值。从 buf 参数获取值。

IPC_RMID

从内核中移除队列。

回顾我们关于消息队列的内部数据结构 (msqid_ds) 的讨论。内核为系统中存在的每个队列维护此结构的一个实例。通过使用 IPC_STAT 命令,我们可以检索此结构的副本以供检查。让我们看看一个快速的包装函数,它将检索内部结构并将其复制到传递的地址


int get_queue_ds( int qid, struct msgqid_ds *qbuf )
{
        if( msgctl( qid, IPC_STAT, qbuf) == -1)
        {
                return(-1);
        }
        
        return(0);
}

如果我们无法复制内部缓冲区,则向调用函数返回 -1。如果一切顺利,则返回 0(零)值,并且传递的缓冲区应包含由传递的队列标识符 (qid).

表示的消息队列的内部数据结构的副本。现在我们有了队列的内部数据结构的副本,哪些属性可以被操作,以及我们如何修改它们?数据结构中唯一可修改的项目是ipc_perm成员。这包含队列的权限,以及关于所有者和创建者的信息。但是,ipc_perm结构中可修改的成员是mode, uid,和gid。您可以更改所有者的用户 ID、所有者的组 ID 以及队列的访问权限。

让我们创建一个旨在更改队列模式的包装函数。模式必须作为字符数组传递(即 ``660'')。


int change_queue_mode( int qid, char *mode )
{
        struct msqid_ds tmpbuf;

        /* Retrieve a current copy of the internal data structure */
        get_queue_ds( qid, &tmpbuf);

        /* Change the permissions using an old trick */
        sscanf(mode, "%ho", &tmpbuf.msg_perm.mode);

        /* Update the internal data structure */
        if( msgctl( qid, IPC_SET, &tmpbuf) == -1)
        {
                return(-1);
        }
        
        return(0);
}

我们通过快速调用我们的get_queue_ds包装函数来检索内部数据结构的当前副本。然后我们调用sscanf()来修改关联的mode成员msg_perm结构。但是,在新的副本用于更新内部版本之前,不会发生任何更改。此任务通过调用msgctl()并使用 IPC_SET 命令来执行。

请务必小心! 更改队列的权限是可能的,这样做可能会不小心将自己锁定在外!记住,这些 IPC 对象不会消失,除非它们被正确删除,或者系统重新启动。因此,即使您无法使用ipcs看到队列,并不意味着它不存在。

为了说明这一点,似乎有必要讲一个有点幽默的轶事。在南佛罗里达大学教授 UNIX 内部原理课程时,我遇到了一个相当尴尬的障碍。前一天晚上我拨号连接到他们的实验室服务器,以便编译和测试为期一周的课程中要使用的实验作业。在我的测试过程中,我意识到我在用于更改消息队列权限的代码中犯了一个错字。我创建了一个简单的消息队列,并测试了发送和接收功能,没有发生任何问题。但是,当我尝试将队列的模式从“660”更改为“600”时,结果操作是我被锁定在自己的队列之外!结果,我无法在源代码目录的同一区域测试消息队列实验作业。由于我使用 ftok() 函数创建 IPC 密钥,因此我试图访问一个我没有适当权限的队列。最后我在上课当天早上联系了当地的系统管理员,花了整整一个小时向他解释什么是消息队列,以及为什么我需要他为我运行 ipcrm 命令。grrrr。

在成功从队列中检索消息后,消息将被删除。但是,如前所述,IPC 对象仍然存在于系统中,除非显式删除或系统重新启动。为了完成消息队列的生命周期,应该通过调用msgctl(),并使用 IPC_RMID 命令来删除


int remove_queue( int qid )
{
        if( msgctl( qid, IPC_RMID, 0) == -1)
        {
                return(-1);
        }
        
        return(0);
}

如果队列在没有问题的情况下被删除,则此包装函数返回 0,否则返回 -1。队列的删除本质上是原子的,随后对队列的任何目的的访问都将彻底失败。


next up previous contents
下一篇: msgtool:一个交互式消息 上一级: 6.4.2 消息队列 上一篇: 系统调用:msgsnd()

转换于
1996 年 3 月 29 日星期五 14:43:04 EST