消息队列的创建与读写ftok,msgget,msgsnd,msgrcv,指令ipcs,ipcrm 查看,删除消息队列

ipcs是Linux下显示进程间通信设施状态的工具。可以显示消息队列、共享内存和信号量的信息。对于程序员非常有用,普通的系统管理员一般用不到此指令。

ipcs -查看系统使用的IPC队列资源

ipcs -查看系统使用的IPC共享内存资源

ipcs -查看系统使用的IPC信号量资源

ipcs -a命令可以查看当前使用的共享内存、消息队列及信号量所有信息

ipcs -p命令可以得到与共享内存、消息队列相关进程之间的消息

ipcs -u命令可以查看各个资源的使用总结信息,其中可以看到使用的信号量集的个数、信号量的个数,

以及消息队列中当前使用的消息个数总数、占用的空间字节数。

默认不加参数时,使用的参数是 -(all,显示所有)

pcs -l命令可以查看各个资源的系统限制信息,可以看到系统允许的最大信号量集及信号量个数限制、最大的消息队列中消息个数等信息。

yxg@k8s:~$ ipcs -l

------ Messages Limits --------
max queues system wide = 32000   //系统最多的消息队列数量
max size of message (bytes) = 8192  //单个消息的最大字节数
default max size of queue (bytes) = 16384  //单个消息的最大字节数

------ Shared Memory Limits --------
max number of segments = 4096
max seg size (kbytes) = 18014398509465599
max total shared memory (kbytes) = 18014398442373116
min seg size (bytes) = 1

------ Semaphore Limits --------
max number of arrays = 32000
max semaphores per array = 32000
max semaphores system wide = 1024000000
max ops per semop call = 500
semaphore max value = 32767

这个限制可以通过增加内核参数 semmni 的取值来解决,该参数定义了系统能够拥有的信号量集合的
总数。Linux 可以动态调整大多数内核IPC 参数值的大小,也可以静态地修改

1、创建消息队列

消息队列是随着内核的存在而存在的,每个消息队列在系统范围内对应唯一的键值。要获得一个消息队列的描述符,

只需要提供该消息队列的键值即可,该键值通常由函数ftok返回。

key_t ftok(const char *pathname, int proj_id);

ftok函数根据pathname和proj_id这两个参数生成唯一的键值。

pathname:must refer to an existing, accessible file,在系统中一定要存在,且进程有访问权限。

proj_id:的取值范围为1-255 

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>

int main()
{

    int i = 0;
    for(i=0;i<5;i++)
    {
        printf("key[%d] = %lu
", i, ftok(".", i));
    }

    return 0;
}

 

ftok返回的键值可以提供给函数msgget,

msgget根据这个键值创建一个新的消息队列或者访问一个已存在的消息队列。

int msgget(key_t key, int msgflg);

参数key即为ftok函数的返回值。msgflag是一个标志参数。

以下是msgflg的可能取值:

IPC_CREAT:如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列,返回消息队列的描述符。

IPC_EXCL:和IPC_CREAT一起使用,如果对应键值的消息队列已经存在,则出错,返回-1

上述msgflg参数为模式标志参数,使用时需要与IPC对象存取权限(如0600)进行|运算来确定消息队列的存取权限.

如果用msgget创建了一个新的消息队列对象时,则msqid_ds结构成员变量的值设置如下:

Ÿ        msg_qnum、msg_lspid、msg_lrpid、 msg_stime、msg_rtime设置为0。

Ÿ        msg_ctime设置为当前时间。

Ÿ        msg_qbytes设成系统的限制值。

Ÿ        msgflg的读写权限写入msg_perm.mode中。

Ÿ        msg_perm结构的uid和cuid成员被设置成当前进程的有效用户ID,gid和cuid成员被设置成当前进程的有效组ID。

 

注意:IPC_EXCL单独使用是没有任何意义的。

该函数如果调用成功返回一个消息队列的描述符,否则返回-1

 

2、写消息队列

创建了一个消息队列后,就可以对消息队列进行读写了。函数msgsnd用于向消息队列发送(写)数据。

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

msqid:函数向msgid标识的消息队列发送一个消息

msgp:指向发送的消息。

msgsz:要发送消息的大小,不包含消息类型占用的4个字节。

msgflg:操作标识位。可以设置为0或者IPC_NOWAIT。如果为0,则当消息队列已满的时候,msgsnd将会阻塞,直到消息可写进消息队列;如果msgflg

为IPC_NOWAIT,当消息队列已满的时候,msgsnd函数将不等待立即返回。

msgsnd函数成功返回0,失败返回-1。常见错误码有:EAGAIN,说明消息队列已满。

EIDRM:说明消息队列已被删除

EACCES:说明无权访问消息队列

 

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <string.h>


//用户自定义消息缓冲
struct mymsgbuf{
long msgtype;
char buf[256];
};


int main()
{
    struct mymsgbuf mymsgbuffer;
    int msglen = 0;
    int i = 0;
    int msgkey = 0;



    int qid = 0;//消息队列标识符
    
    //获取键值
    msgkey = ftok(".", 11);
    
    
    qid = msgget(msgkey, IPC_CREAT|0660);
    printf("msgget return %d
", qid);
    


    //填充消息结构,发送到消息队列
    mymsgbuffer.msgtype = 4;
    strcpy(mymsgbuffer.buf, "manman");
    msglen = sizeof(struct mymsgbuf) - 4;

    if (msgsnd(qid, &mymsgbuffer, msglen, 0) == -1)
    {
        perror("msgsnd error
");
        exit(1);
    }

    return 0;
}

root@wilson-software:~/Project/xa# ./main
msgget return 0

执行程序之后,就向消息队列放入了一条消息,通过指令ipcs查看:

------ Message Queues --------
key      msqid    owner    perms    used-bytes   messages
0x0b014424       0      root    660      256       1

 

 

3、读消息队列

消息队列中放入数据后,其他进程就可以读取其中的消息了。读取消息的系统调用为

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

参数含义:

msqid:消息队列描述符

msgp:读取的消息存储到msgp指向的消息结构中

msgsz:消息缓冲区的大小

msgtyp:为请求读取的消息类型

msgtyp等于0 则返回队列的最早的一个消息。
msgtyp大于0,则返回其类型为mtype的第一个消息。
msgtyp小于0,则返回其类型小于或等于mtype参数的绝对值的最小的一个消息。

msgflg:操作标志位。msgflg可以为IPC_NOWAIT, MSG_EXCEPT  ,MSG_NOERROR

0:表示忽略

IPC_NOWAIT:如果没有满足条件的消息,调用立即返回,此时错误码为ENOMSG

如果不指定这个参数,那么进程将被阻塞直到函数可以从队列中得到符合条件的
消息为止。如果一个client 正在等待消息的时候队列被删除,EIDRM 就会被返回如果进

程在阻塞等待过程中收到了系统的中断信号,EINTR 就会被返回。

MSG_EXCEPT :与msgtype配合使用,返回队列中第一个类型不为msgtype的消息

MSG_NOERROR:如果队列中满足条件的消息内容大于所请求的msgsz字节,则把该消息截断,截断部分将被丢弃。

如果不指定这个参数,E2BIG 将被返回,而消息则留在队列中不被

取出。当消息从队列内取出后,相应的消息就从队列中删除了。

 可利用ipcrm -q 294912删除该消息队列。(294912为msgqid)因为消息队列是随内核持续存在的,在程序中若不利用msgctl函数或在命令行用ipcrm命令显式地删除,该消息队列就一直存在于系统中。另外信号量和共享内存也是随内核持续存在的

在读取前:

------ Message Queues --------
key      msqid    owner    perms    used-bytes    messages
0x0b014424   0     root    660      256       1

在读取后

key     msqid   owner   perms   used-bytes   messages
0x0b014424   0     root    660     0        0

 

调用msgrcv函数的时候,成功会返回读出消息的实际字节数,否则返回-1。

常见错误码有:

E2BIG:  表示消息的长度大于msgsz

EIDRM:表示消息队列已被删除

EINVAL:说明msgqid无效或msgsz小于0

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <string.h>


//用户自定义消息缓冲
struct mymsgbuf{
long msgtype;
char buf[256];
};


int main()
{
    struct mymsgbuf mymsgbuffer;
    int msglen = 0;
    int i = 0;
    int msgkey = 0;


    int qid = 0;//消息队列标识符
    
    //获取键值
    msgkey = ftok(".", 11);
    
    qid = msgget(msgkey, IPC_CREAT|0660);
    printf("msgget return %d
", qid);


   
    msglen = sizeof(struct mymsgbuf) - 4;

 //上面的程序中发送的消息类型是4
 //注意,msgrcv的msgflg参数可设置msgrcv函数是否是阻塞的,经测试msgflg是0的情况会阻塞,直到获取到消息
if (msgrcv(qid, &mymsgbuffer, msglen, 4, 0) == -1) { perror("msgsnd error "); exit(1); } printf("get message:%s ", mymsgbuffer.buf); return 0; }

 

运行结果:

root@wilson-software:~/Project/xa# ./main
msgget return 0
get message:manman

 +++++++++++++++++++++++++++++++++++++++++++++++++++++++

利用上面提到的msgrcv()对消息长度的处理,我们可以使用下面的方法来检查队列内
是存在符合条件的信息:
int peek_message( int qid, long type )
{
int result, length;
if((result = msgrcv( qid, NULL, 0, type, IPC_NOWAIT)) == -1)
{
if(errno == E2BIG)
return(TRUE);
}
return(FALSE);
}
这里我们将msgp 和msgsz 分别设为NULL 和零。然后检查函数的返回值,如果是E2BIG
则说明存在符合指定类型的消息。一个要注意的地方是IPC_NOWAIT 的使用,它防止了阻塞

原文地址:https://www.cnblogs.com/zhangxuan/p/6732552.html