【IPC】消息队列

本文详细介绍了Linux系统中的进程间通信(IPC)对象,特别是消息队列的创建、操作(如msgget、msgsnd、msgrcv)以及控制(msgctl),包括ftok函数的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、IPC对象

除了最原始的进程间通信方式信号、无名管道和有名管道外,还有三种进程间通信方式,这
三种方式称之为IPC对象
IPC对象分类:消息队列、共享内存、信号量(信号灯集)
IPC对象也是在内核空间开辟区域,每一种IPC对象创建好之后都会将其设置为全局,并且
会给其分配一个编号,只要找到唯一的这个编号就可以进行通信,所以不相关的进程可以通
过IPC对象通信。
IPC对象创建好之后,会在当前系统中可见,只要不删除或者不关闭系统,就会一直存在。
查看已经创建的IPC对象:
 ipcs 查看当前系统中所有创建的IPC对象
 ipcs ‐q 查看创建的消息队列
 ipcs ‐m 查看创建的共享内存
 ipcs ‐s 查看信号量
 ipcrm 删除IPC对象
 例如:ipcrm ‐q msqid 删除标号为msqid的消息队列

2、消息队列的概述

2.1 消息队列的概念

消息队列是消息的链表,存放在内存中,由内核维护
消息队列的特点
1、消息队列中的消息是有类型的。
2、消息队列中的消息是有格式的。
3、消息队列可以实现消息的随机查询。消息不一定要以先进先出的次序读取,编程时可以
按消息的类型读取
4、消息队列允许一个或多个进程向它写入或者读取消息。
5、与无名管道、命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删
6、每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的。
7、只有内核重启或人工删除消息队列时,该消息队列才会被删除。若不人工删除消息队
列,消息队列会一直存在于系统中。
在ubuntu 12.04中消息队列限制值如下:
每个消息内容最多为8K字节
每个消息队列容量最多为16K字节
系统中消息队列个数最多为1609个
系统中消息个数最多为16384个
System V提供的IPC通信机制需要一个key值通过key值就可在系统内获得一个唯一的消
息队列标识符
key值可以是人为指定的,也可以通过ftok函数获得
如果多个进程想通过IPC对象通信,则必须找到唯一的标识,而唯一的标识是由key决定
的,所以只要key知道,则就可以实现多个进程通信
(另外,Posix IPC的操作稍有不同,后面补充)

2.2 ftok函数

ftok - convert a pathname and a project identifier to a System V IPC key
 
 #include <sys/types.h>
 #include <sys/ipc.h>
 key_t ftok(const char *pathname, int proj_id);
 功能:通过文件名和目标值共同创造一个键值并返回值
 参数:
          pathname:任意一个文件名(文件名或者目录名)
         proj_id:目标值,范围一般是0~127
 返回值:
                 成功:键值
                 失败:‐1
 如果使用ftok函数获取键值,得到的键值是由ftok的第一个 参数对应文件的信息和第二个参数一起决定的
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>


int main(int argc, char const *argv[])
{
    //使用ftok函数获取键值
    //只要保证ftok的第一个参数对应的文件和第二个参数值相同,则不管程序运行多少遍或者多少个进程或者键值
    //键值一定都是唯一的

    key_t mykey;
    mykey = ftok("test",666);
    if(mykey==-1){
        perror("fail to ftok");
        exit(1);
    }
printf("mykey = %#x\n", mykey);


    return 0;
}

3、消息队列操作

3.1创建消息队列 -- msgget()

 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>
 int msgget(key_t key, int msgflg);
         功能:创建一个消息队列,得到消息队列的id
参数:
         key:键值,唯一的键值确定唯一的消息队列
                 方法1:任意指定一个数
                 方法2:使用ftok函数获取键值
         msgflg:消息队列的访问权限,
         一般设置为 IPC_CREAT | IPC_EXCL | 0777 或者 IPC_CREAT | 0777
 返回值:
         成功:消息队列的id
        失败:‐1
查看消息队列
 ipcs ‐q
 删除消息队列
 ipcrm ‐q msqid
测试msgget
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/msg.h>

int main(int argc, char const *argv[])
{
    key_t mykey;
    mykey = ftok("test", 666);
    if (mykey == -1)
    {
        perror("fail to ftok");
        exit(1);
    }
    printf("mykey = %#x\n", mykey);

    // 通过msgget函数创建一个消息队列
    int msgid;

    msgid = msgget(mykey, IPC_CREAT | 0666);
    if (msgid == -1)
    {
        perror("fail to msgget");
        exit(1);
    }

    printf("msgid = %d\n", msgid);
    system("ipcs -q");

    return 0;
}

3.2 发送消息-- 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);
 功能:向指定的消息队列发送数据(写操作)
 参数:
 msqid
        消息队列的id
msgp
        要写入的数据,需要自己定义结构体
struct struct_name{
         long mtype; //消息的编号,必须大于0
         char mtext[128]; //消息正文,可以定义多个成员
         ...
 }
 msgsz:消息正文的大小,不包括消息的编号长度
 msgflg:标志位
         0 阻塞
         IPC_NOWAIT 非阻塞
 返回值:
         成功:0
         失败:1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>


#define N 128

typedef struct 
{
    long msg_type; //消息类型,必须在结构体的第一个位置并且类型必须是long
    char msg_text[N]; //消息正文,也可以有多个成员并且类型也可以是任意
}MSG ;


#define MSG_SIZE (sizeof(MSG)-sizeof(long))
int main(int argc, char const *argv[])
{
   
key_t mykey;
    mykey = ftok("test", 666);
    if (mykey == -1)
    {
        perror("fail to ftok");
        exit(1);
    }
    printf("mykey = %#x\n", mykey);

    // 通过msgget函数创建一个消息队列
    int msgid;

    msgid = msgget(mykey, IPC_CREAT | 0666);
    if (msgid == -1)
    {
        perror("fail to msgget");
        exit(1);
    }

    //printf("msgid = %d\n", msgid);
    system("ipcs -q");


MSG msg1 ={1,"hello world"};
MSG msg2 ={3,"UCC"};

MSG msg3 ={2,"giant"};

MSG msg4 ={4,"美利达"};


if(msgsnd(msgid,&msg1,MSG_SIZE,0)==-1){
    perror("fail to send");
    exit(1);
}

if(msgsnd(msgid,&msg2,MSG_SIZE,0)==-1){
    perror("fail to send");
    exit(1);
}
if(msgsnd(msgid,&msg3,MSG_SIZE,0)==-1){
    perror("fail to send");
    exit(1);
}
if(msgsnd(msgid,&msg4,MSG_SIZE,0)==-1){
    perror("fail to send");
    exit(1);
}

system("ipcs -q");

    return 0;
}

3.3 消息接收--msgrcv()

#include <sys/ipc.h>
#include <sys/msg.h>
 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz,
 long msgtyp, int msgflg);
 功能:从消息队列中接收数据(读操作),接收的数据会从消息队列中删除
 参数:
 msqid
        消息队列id
 msgp
        保存接收到的数据的结构体
        struct struct_name{
                long mtype; //消息的编号,必须大于0
                char mtext[128]; //消息正文,可以定义多个成员
        }
 msgsz
        消息正文的大小
 msgtyp:设置要接收哪个消息
         0 按照写入消息队列的顺序依次读取
         >0 只读取消息队列中消息编号为当前参数的第一个消息
         <0 只读取消息队列中小于等于当前参数的绝对中内最小的第一个消息
 
msgflg:标志位
        0 阻塞
         IPC_NOWAIT 非阻塞
 返回值:
         成功:接收到的消息正文的长度
         失败:1
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/msg.h>



#define N 128
typedef struct 
{
    long msg_type; //消息类型,必须在结构体的第一个位置并且类型必须是long
    char msg_text[N]; //消息正文,也可以有多个成员并且类型也可以是任意
}MSG ;


#define MSG_SIZE (sizeof(MSG)-sizeof(long))

int main(int argc, char const *argv[])
{
   key_t mykey;
    mykey = ftok("test", 666);
    if (mykey == -1)
    {
        perror("fail to ftok");
        exit(1);
    }
    printf("mykey = %#x\n", mykey);

    // 通过msgget函数创建一个消息队列
    int msgid;

    msgid = msgget(mykey, IPC_CREAT | 0666);
    if (msgid == -1)
    {
        perror("fail to msgget");
        exit(1);
    }

    //printf("msgid = %d\n", msgid);
    system("ipcs -q");

//通过msgrcv函数接收消息队列中的信息(读操作)
//注意:如果没有第四个参数指定的消息时,msgrcv函数会阻塞等待
MSG msg;
//如果第四个参数为0,则按照先进先出的方式读取数据

//if(msgrcv(msgid,&msg,MSG_SIZE,0,0))

//如果第四个参数为>0,则获取当前值得消息类型的数据
//if(msgrcv(msgid, &msg, MSGTEXT_SIZE, 2, 0) == ‐1)

//如果第四个参数为<0,则获取当前值得绝对值内消息类型最小的数据

if(msgrcv(msgid, &msg, MSG_SIZE, -3, 0) == -1)
 {
 perror("fail to msgrcv");
 exit(1);
 }

 printf("recv_msg = %s\n", msg.msg_text);
 system("ipcs -q");
    return 0;
}

3.4 消息控制-- msgctl()

 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>
 int msgctl(int msqid, int cmd, struct msqid_ds *buf);
 功能:设置或者获取消息队列的信息
参数:
         msqid:指定的消息队列的id
         cmd:具体操作的指令
                 IPC_SET 设置属性
                 IPC_STAT 获取属性
                 IPC_RMID 删除消息队列
         msqid_ds:设置或者获取消息队列的属性
 返回值:
         成功:0
         失败:1

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

int main(int argc, char const *argv[])
{

    key_t mykey;
    mykey = ftok("test", 666);
    if (mykey == -1)
    {
        perror("fail to ftok");
        exit(1);
    }
    system("ipcs -q");

    // 关闭前面创建过的那个 ,可以通过 ipcs -q 查看
    if (msgctl(0,IPC_RMID, NULL))
    {
        perror("fail to msgctl");
        exit(1);
    }

    system("ipcs -q");


    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值