Chinaunix首页 | 论坛 | 博客
  • 博客访问: 495155
  • 博文数量: 78
  • 博客积分: 5131
  • 博客等级: 大校
  • 技术积分: 1468
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-17 16:20
文章分类
文章存档

2012年(1)

2011年(29)

2010年(6)

2009年(24)

2008年(18)

我的朋友

分类: LINUX

2011-05-06 15:15:52

 

Linux的消息队列(queue)实质上是一个链表, 它有消息队列标识符(queue ID). msgget创建一个新队列或打开一个存在的队列; msgsnd向队列末端添加一条新消息; msgrcv从队列中取消息, 取消息是不一定遵循先进先出的, 也可以按消息的类型字段取消息.

1. 标识符(des)和键(key):

    消息队列, 信号量和共享存储段, 都属于内核中的IPC结构, 它们都用标识符来描述. 这个标识符是一个非负整数, 与文件描述符不同的是, 创建时并不会重复利用通过删除回收的整数, 而是每次+1, 直到整数最大值回转到0.

    标识符是IPC对象的内部名, 而它的外部名则是key(键), 它的基本类型是key_t, 在头文件中定义为长整型. 键由内核变换成标识符.

2. 消息队列状态msqid_ds:

    每个消息队列都有一个msqid_ds结构与其关联:

struct msqid_ds
...{
    
struct ipc_perm msg_perm; /**//* access */
     msgqnum_t        msg_qnum;
/**//* # of messages on queue */
     msglen_t            msg_qbytes;
/**//* max # of bytes on queue */
     pid_t                  msg_lspid;  
/**//* pid of last msgsnd() */
     pid_t                  msg_lrpid;  
/**//* pid of last msgrcv() */
     time_t                msg_stime;
/**//* last-msgsnd() time */
     time_t                msg_rtime;
/**//* last-msgrcv() time */
     time_t                msg_ctime;
/**//* last-change time */
     ...
     ...
     ...
}
;

3. 由路径名和项目ID产生一个key:

    如果客户进程和服务器进程认同一个路径名和项目ID(0~255间的字符值), 接着调用ftok将这两个值变换为一个key.

  • 原型: key_t ftok(const char *path, int id);
  • 头文件:
  • 返回值: 成功则返回key, 出错则返回(key_t)-1.
  • 参数: path参数必须引用一个现存文件. 当产生key时 只使用id参数的低8位.
  • 说 明: 如果两个路径名引用两个不同的文件, 对这两个路径名调用ftok通常返回不同的key. 但是, 因为i节点号和key通常存放在长整型中, 于是创建key时可能会丢失信息. 这意味着, 如果使用同一项目ID, 那么对于不同文件的两个路径名可能产生相同的key. 该函数的工作方式为:
    • 按给定的路径名取得其stat结构.
    • 从该结构中取出部分st_dev和st_ino字段, 与项目ID组合起来.

4. 创建/打开消息队列:

    msgget可以创建一个新队列或打开一个存在的队列.

  • 原型: int msgget(key_t key, int flag);
  • 头文件:
  • 返回值: 成功则返回消息队列ID, 出错则返回-1.
  • 参数:
    • key: 消息队列的key值.
    • flag: 标志位.
  • 说明:
    • 创建队列有两种方法:
      • key是IPC_PRIVATE.
      • key当前未与特定类型的IPC结构相结合, 并且flag中指定了IPC_CREAT位.
    • 初始化msqid_ds成员:
      • ipc_perm中的mode成员按flag进行设置.
      • msg_qnum, msg_lspid, msg_lrpid, msg_stime和msg_rtime都设置为0.
      • msg_ctime设置为当前时间.
      • msg_qbytes设置为系统限制值.

5. 消息队列的垃圾桶函数:

    msgctl类似于驱动程序中的ioctl函数, 可对消息队列执行多种操作.

  • 原型: int msgctl(int msqid, int cmd, struct msgqid_ds *buf);
  • 头文件:
  • 返回值: 成功则返回0, 出错则返回-1.
  • 参数: cmd参数说明对msqid指定的队列要执行的命令:
    • IPC_STAT: 取此队列的msqid_ds结构, 并将它存放在buf指向的结构中.
    • IPC_SET: 按由buf指向结构中的值, 设置与此队列相关结构中的msg_perm.uid, msg_perm.gid, msg_perm.mode和msg_qbytes. 该命令只有下列两种进程可以执行:
      • 有效用户ID等于msg_perm.cuid或msg_per.uid.
      • 具有超级用户特权的进程.
    • IPC_RMID: 从系统中删除该消息队列以及仍在该队列中的所有数据. 执行权限同上.

6. 将数据放到消息队列:

    调用msgsnd将数据放到消息队列中.

  • 原型: int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag);
  • 头文件:
  • 返回值: 成功则返回0, 出错则返回-1.
  • 说 明: 可以定义一个消息结构, 结构中带类型, 这样就可用非先进先出顺序取消息了. 当msgsnd成功返回, 与消息队列相关的msqid_ds结构得到更新, 以标明发出该调用的进程ID(msg_lsqid), 进行该调用的时间(msg_stime), 并指示队列中增加了一条消息(msg_qnum).

7. 从消息队列中取消息:

    调用msgrcv将从消息队列中取消息.

  • 原型: ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag);
  • 头文件:
  • 返回值: 成功则返回消息的数据部分的长度, 出错则返回-1.
  • 参数:
    • ptr: 指向一个长整型数(返回的消息类型存放在其中), 跟随其后的是存放实际消息数据的缓冲区.
    • nbytes: 数据缓冲区的长度. 若返回的消息大于nbytes, 且在flag中设置了MSG_NOERROR, 则该消息被截短.
    • type:
      • type == 0: 返回队列中的第一个消息.
      • type > 0: 返回队列中消息类型为type的第一个消息.
      • type < 0: 返回队列中消息类型值小于或等于type绝对值的消息, 如果这种消息有若干个, 则取类型值最小的消息.
  • 说明: 当msgrcv成功返回时, 与消息队列相关的msqid_ds结构被更新, 以指示调用者的进程ID(msg_lrpid), 调用时间(msg_rtime)和队列中的消息数(msg_qnum)减1.

例题:文件msg为空文件,可以为任何内容,这里只是为了ftok函数使用。程序通过建立消息队列,完成进程间通信,注意msgrcv的第四个参数为消息类型,他定义了从队列中取消息的类型。

下面是msgLucy.c,是建立消息队列的

#include
#include
#include
#include

#include
#include
#include
#include
#include

#define PROJID 0xFF
#define LUCY 1
#define PETER 2

int mqid;

void terminate_handler(int signo)
{
msgctl(mqid,IPC_RMID,NULL);
exit(0);
}

int main()
{
char filenm[] = "msg";
key_t mqkey;
struct msgbuf
{
     long mtype;     /* message type, must be > 0 */
   char mtext[256]; /* message data */
   }msg;
int ret;

mqkey = ftok(filenm,PROJID);
if(mqkey == -1)
{
   perror("ftok error: ");
   exit(-1);
}

mqid = msgget(mqkey,IPC_CREAT | IPC_EXCL | 0666);
if(mqid == -1)
{
   perror("msgget error: ");
   exit(-1);
}

signal(SIGINT, terminate_handler);
signal(SIGTERM, terminate_handler);

while(1)
{
   printf("Lucy: ");
   fgets(msg.mtext, 256, stdin);
   if (strncmp("quit", msg.mtext, 4) == 0)
   {
    msgctl(mqid,IPC_RMID,NULL);
    exit(0);
   }
   msg.mtext[strlen(msg.mtext)-1] = '\0';
   msg.mtype = LUCY;
   msgsnd(mqid,&msg,strlen(msg.mtext) + 1,0);
   msgrcv(mqid,&msg,256,PETER,0);
   printf("Peter: %s\n", msg.mtext);  
}  
}

下面的是msgPeter,是和Lucy通信的,程序先运行Lucy,再运行Peter

#include
#include
#include
#include

#include
#include
#include
#include
#include

#define PROJID 0xFF
#define LUCY 1
#define PETER 2

int main()
{
char filenm[] = "msg";
int mqid;
key_t mqkey;
struct msgbuf
{
        long mtype;     /* message type, must be > 0 */
         char mtext[256]; /* message data */
   }msg;
int ret;

mqkey = ftok(filenm, PROJID);
if(mqkey == -1)
{
   perror("ftok error: ");
   exit(-1);
}

mqid = msgget(mqkey, 0);
if(mqid == -1)
{
   perror("msgget error: ");
   exit(-1);
}

while(1)
{
   msgrcv(mqid,&msg,256,LUCY,0);
   printf("Lucy: %s\n",msg.mtext);
   printf("Peter: ");
   fgets(msg.mtext,256,stdin);
   if(strncmp("quit", msg.mtext, 4) == 0)
   {
    exit(0);
   }
   msg.mtext[strlen(msg.mtext)-1] = '\0';
   msg.mtype = PETER;
   msgsnd(mqid,&msg,strlen(msg.mtext) + 1,0);
}
}

阅读(1418) | 评论(0) | 转发(1) |
0

上一篇:linux静态库编译

下一篇:container_of 理解

给主人留下些什么吧!~~