消息队列和共享内存
AI-摘要
                        
                        
                        
                        Tianli GPT
                    AI初始化中...
                    介绍自己
                        生成本文简介
                        推荐相关文章
                        前往主页
                        前往tianli博客
                    消息队列和共享内存
1. 消息队列概述
- 消息队列中,每一个消息内容都有规定的数据类型/格式【协议】
 - 消息队列采用【FIFO】 形式,先进先出,同时处理之后的消息,从当前队列中取出销毁,不在队列中存在。
 - 消息队列可以进行随机查询,消息处理可以根据消息类型,消息相关数据进行指定消息,指定次序处理。
 - 每一个消息都有一个唯一【消息标识符】,在整个队列中唯一
 - 每一个消息队列同样存在对应的一个【队列标识符】,在整个系统唯一。
 消息队列属于 System V 进程通信方式规范,在整个 IPC 通信过程中,系统会提供给当前消息队列一个唯一的 Key 值 --> 【队列标识符】
2. 消息队列相关函数
2.1 ftok 创建消息队列
函数文档
#include <sys/types.h> #include <sys/ipc.h> key_t ftok(const char *pathname, int proj_id);
- 功能:
 
- 根据当前用户提供的文件/文件夹路径,以及指定的项目 ID 号,创建一个消息队列,返回值是消息队列的 key 值,key 值是当前消息队列访问的唯一标识符。
 - 参数解释:
 
const char *pathname :提供给当前函数的一个文件/文件夹路径,要求当前文件/文件夹必须存在,如果不存在,报错!!!创建消息队列失败
- 补充说明: ftok 是利用系统分配给当前文件/文件夹的 inode 信息, 参与生成对应的 消息队列 key 值
 int proj_id :用户指定当前项目的 id 号,参与整个消息队列生成过程,用于生成对应的消息队列 Key 值。proj_id 数据范围是 1 ~ 255 ,对应一个字节数据,后续代码中如果需要使用对应的 proj_id 是对应当前数据的【低 8 位有效】- 返回值类型
 
- 创建成功,返回是对应消息队列 Key 值
 - 创建失败返回 -1
 
【注意】
- 采用相同文件/文件夹路径,以及 proj_id 得到 key 数据一致。
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <unistd.h>
/*
key_t ftok(const char *pathname, int proj_id);
*/
int main(int argc, char const *argv[])
{
    key_t key =  ftok("../share_dir", 6);
    printf("key : %d\n", key);
    key_t key2 =  ftok("../share_file.txt", 6);
    printf("key2 : %d\n", key2);
    return 0;
}
2.2 消息队列数据格式【重点】
typedef struct _msg
{
    long mtype;        // message type 消息类型
    char mtext[100];   // message text 字符数组,信息正文
    /* 其他内容可以自行添加,正文内容,可以有多个字段数据,多个字段类型,多个字段形式 */
} MSG;
后续涉及到的消息队列相关函数,都是基于当前 MSG 类型完成。
【要求】结构体第一个字段必须是 long 类型,作为整个消息队列数据包的格式限制
2.3 消息队列获取 msgget
函数文档
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgget(key_t key, int msgflg);
- 函数功能:
 
- 根据消息队列对应的 key 值和设置消息队列创建的标记,获取到消息队列
 - 函数参数
 
key_t key :可以利用 ftok 得到对应的 key_t 值,作为当前消息队列唯一标识int msgflg :获取当前消息队列方式,判断条件和权限情况
IPC_CREAT :如果指定的消息队列不存在,则创建一个新的消息 队列。IPC_EXCL :和IPC_CREAT如果对应当前 Key_t 值的消息队列存在,创建失败,返回值为 -1,同时errno设置为EEXIST- 【常用模式】msgflg =
 IPC_CREAT | 0666- 返回值:
 
- 创建成功,返回对应的消息队列标识符
 - 创建失败,返回 -1
 
2.4 消息队列数据发送 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);
- 函数功能:
 
- 在指定的消息队列中,发送目标数据包中的数据内容。
 - 函数参数:
 
int msqid :消息队列标识符,当前操作对应的是哪一个消息队列const void *msgp :发送的消息数据包 (message package) , 要求满足消息队列对应的数据格式,对应消息队列要求的 【MSG 格式】size_t msgsz :当前发送的数据包的字节个数。int msgflg :控制消息发送行为的标志。可以是以下值:
0:默认行为,如果消息队列已满,msgsnd会阻塞,直到有空间可用。IPC_NOWAIT:如果消息队列已满,不阻塞,立即返回-1,并将errno设置为EAGAIN。- 返回值类型:
 
- 发送成功,返回 0
 - 发送失败,并且 msgflg 设置为
 IPC_NOWAIT, 返回 -1
2.4 消息队列数据接收 msgrcv
函数文档
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
- 函数功能:
 
- 在指定的消息队列中,接收目标消息内容
 - 函数参数:
 
int msqid :消息队列标识符,当前操作对应的是哪一个消息队列void *msgp :用于接收消息数据的数据包缓冲空间。size_t msgsz :接收数据对应的字节个数**long msgtyp :当前消息类型**
- msgtyp = 0 : 返回整个消息队列中的第一个消息
 - msgtyp > 0 : 找到指定 消息结构体中的 mtype 对应的消息
 - msgtyp < 0 : 找出整个消息队列中信息类型,小于等于当前 msgtyp 绝对值的所有消息内容,可能存在多个数据情况。
 int msgflg :控制消息接收行为的标志。可以是以下值:
0:默认行为,如果消息队列中没有符合条件的消息,msgrcv会阻塞,直到有符合条件的消息到达。IPC_NOWAIT:如果消息队列中没有符合条件的消息,不阻塞,立即返回-1,并将errno设置为ENOMSG。MSG_NOERROR:如果接收到的消息长度超过msgsz,截断消息,只返回msgsz长度的数据,不会失败。- 返回值类型:
 
- 成功时返回接收到的消息数据部分的长度**(不包括
 mtype成员)。**- 失败时返回 -1 并设置 errno 来指示具体的错误原因,常见的错误包括:
 
EACCES:没有权限从指定的消息队列接收消息。ENOMSG:使用了IPC_NOWAIT标志,且消息队列中没有符合条件的消息。EFAULT:msgp指针指向的内存区域不可访问。
消息结构体头文件
#ifndef _04_MSG_
#define _04_MSG_
// 满足消息队列所需的消息结构体
typedef struct _msg
{
    long msg_type;
    char msg_data[64];
} MSG;
#endif
消息队列读取
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include "04-msg.h"
int main(int argc, char const *argv[])
{
    // 申请共享空间,得到对应 IPC 唯一标识符 key_t
    key_t key = ftok(".", 88);
    if (-1 == key)
    {
        perror("ftok failed");
    }
    // 获取已经存在/或者当前进程创建对应的消息队列内容
    int msgid = msgget(key, IPC_CREAT | 0666);
    if (-1 == msgid)
    {
        perror("msgget failed");
    }
    // 创建一个消息结构体变量
    MSG msg;
    /*
    利用 msgrcv 函数接收在消息队列中的消息数据
    */
    ssize_t size = msgrcv(msgid, &msg, sizeof(msg.msg_data), 0, 0);
    printf("size : %ld\n", size);
    printf("msg.msg_type : %ld\n", msg.msg_type);
    printf("msg.msg_data : %s\n", msg.msg_data);
    MSG msg2;
  
    size = msgrcv(msgid, &msg2, sizeof(msg2.msg_data), 0, 0);
    printf("size : %ld\n", size);
    printf("msg.msg_type : %ld\n", msg2.msg_type);
    printf("msg.msg_data : %s\n", msg2.msg_data);
    msgctl(msgid, IPC_RMID, NULL);
   
    return 0;
}
消息队列发送数据
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include "04-msg.h"
int main(int argc, char const *argv[])
{
    // 共享空间申请,得到对应 key_t 值,对应 IPC 唯一标识符
    key_t key = ftok(".", 88);
    if (-1 == key)
    {
        perror("ftok failed!");
    }
    // 获取消息队列,如果当前消息队列不存在,创建并且设置操作权限为 0666
    int msgid = msgget(key, IPC_CREAT | 0666);
    if (-1 == msgid)
    {
        perror("msgget failed!");
    }
    // 消息结构体变量
    MSG msg;
    // 给予当前消息数据结构体进行成员变量赋值操作
    msg.msg_type = 1;                     // 信息类型为 1
    strcpy(msg.msg_data, "Hello World!"); // 信息数据是一个字符串数据
    /*
    利用 msgsnd 函数对消息结构体数据进行发送操作,发送到当前消息队列中
    将指定的 MSG 数据包发送到指定的消息队列中。
    */
    int ret = msgsnd(msgid, &msg, sizeof(msg.msg_data), 0);
    MSG msg2;
  
    msg2.msg_type = 3;                   // 信息类型为 3
    strcpy(msg2.msg_data, "你好!世界!"); // 信息数据是一个字符串数据
  
    msgsnd(msgid, &msg2, sizeof(msg2.msg_data), 0);
    return 0;
}
2.5 msgctl 消息队列控制函数
3. 共享内存
3.1 概述
共享内存是两个或者两个以上的进程,对于特定内存空间数据共享。
特点
- 共享内存是进程之间共享数据最快的方式,因为共享内存之后,对应内存空间,在任何一个进程中都可以通过对应的【内存地址/共享内存标识符】直接访问对应的内存空间
 - 任何一个进程写入数据到共享内存,其他可以操作当前共享内存的进程,都可以获取到写入数据内容
 
3.2 获取共享内存标识符
函数文档
#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg);
- 函数功能:
 
- 根据 ftok 申请的唯一 IPC key_t 数据,以及设置的共享内存字节数,和对应的共享内存设置参数得到对应的【共享内存标识符】
 - 函数参数:
 
key_t key :通过 ftok 申请的 IPC Key_t 数据,在整个 IPC 管理中唯一size_t size :申请的共享内存字节数int shmflg :共享内存权限和创建状态设置
IPC_CREAT:如果对应共享内存不存在,创建对应共享内存,如果存在,无任何操作IPC_EXCL:和IPC_CREAT一起使用,如果共享内存存在,当前函数执行失败- 常见写法
 IPC_CREAT | 0666- 返回值:
 
- 获取成功,得到对应的共享内存标识符
 - 获取失败,返回 -1
 
共享内存申请
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    /*
    int shmget(key_t key, size_t size, int shmflg);
    */
    // 申请当前项目在 IPC (inter process communication) 中的唯一 key_t
    key_t key = ftok(".", 66);
    if (-1 == key)
    {
        perror("ftok failed!");
    }
    size_t shmid = shmget(key, 128, IPC_CREAT | 0666);
    if (-1 == shmid)
    {
        perror("shmget failed!");
    }
    sleep(10);
  
    return 0;
}
利用 Linux 命令查看当前内存状态
qf@qf:~$ ipcs -m数据结果
------------ 共享内存段 -------------- 键 shmid 拥有者 权限 字节 连接数 状态 0x42011df2 2 qf 666 128 0
也可以利用 Linux 命令对共享内存空间进行删除操作
qf@qf:~$ # ipcrm -m shmid qf@qf:~$ ipcrm -m 2
3.3 共享内存映射【attach】
函数文档
#include <sys/types.h> #include <sys/shm.h> void *shmat(int shmid, const void *shmaddr, int shmflg);
- 函数功能:
 
- 将共享内存映射到进程的地址空间,使进程能够通过指针直接访问共享数据。
 - 函数参数:
 
int shmid: 共享内存标识符const void *shmaddr :共享内存地址,【通常情况参数为 NULL,由系统自行管理分配】int shmflg:共享内存参数配置
- 推荐参数 0 表示当前共享内存可读可写
 SHM_RDONLY当前共享内存只读- 返回值
 
- 返回值为 void *,可以通过强制类型转换,将当前共享内存中的数据指定处理为目标数据类型。映射成功,返回值是当前内存空间首地址
 - 映射失败,返回 (void *) -1, -1 如果按照地址方式考虑,是一个非法地址。
 
共享内存写入和读取操作
03-shmat_write.c
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#define SHM_SIZE 128
int main(int argc, char const *argv[])
{
    // 获取 IPC 唯一进程间通信标识符
    key_t key = ftok(".", 68);
    if (-1 == key)
    {
        perror("ftok failed!");
    }
    // 获取/申请共享内存
    int shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
    if (-1 == shmid)
    {
        perror("shmget failed!\n");
    }
    /* 
    【内存映射】当前进程获取使用对应共享内存
        void *shmat(int shmid, const void *shmaddr, int shmflg);
        shmaddr 推荐使用 NULL ,系统默认分配内存地址
        shmflg 推荐 0,表示当前共享内存可读可写
    */
    char * shmaddr = shmat(shmid, NULL, 0);
    bzero(shmaddr, SHM_SIZE);
    char * data = "陈陈你的 open 函数呢?";
    memcpy(shmaddr, data, strlen(data));
    return 0;
}
03-shmat_read.c
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SHM_SIZE 128
int main(int argc, char const *argv[])
{
    // 获取 IPC 唯一进程间通信标识符
    key_t key = ftok(".", 68);
    if (-1 == key)
    {
        perror("ftok failed!");
    }
    // 获取/申请共享内存
    int shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
    if (-1 == shmid)
    {
        perror("shmget failed!\n");
    }
    /*
    【内存映射】当前进程获取使用对应共享内存
       void *shmat(int shmid, const void *shmaddr, int shmflg);
       shmaddr 推荐使用 NULL ,系统默认分配内存地址
       shmflg 推荐 0,表示当前共享内存可读可写
   */
  
    char *shmaddr = shmat(shmid, NULL, 0);
  
    printf("data = %s\n", shmaddr);
    return 0;
}
3.4 共享内存取消映射【detach】
函数文档
#include <sys/types.h> #include <sys/shm.h> int shmdt(const void *shmaddr);
- 函数功能:
 
- 解除当前进程和指定地址共享内存的联系,后续代码中当前进程无法使用共享内存。如果进程继续使用直接会导致【段错误,核心已转储】
 - 函数参数:
 
- 目标分离的共享内存空间首地址
 - 返回值
 
- 成功返回 0
 - 失败返回 -1
 
shmat.c
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SHM_SIZE 128
int main(int argc, char const *argv[])
{
    // 获取 IPC 唯一进程间通信标识符
    key_t key = ftok(".", 68);
    if (-1 == key)
    {
        perror("ftok failed!");
    }
    // 获取/申请共享内存
    int shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
    if (-1 == shmid)
    {
        perror("shmget failed!\n");
    }
    /*
    【内存映射】当前进程获取使用对应共享内存
       void *shmat(int shmid, const void *shmaddr, int shmflg);
       shmaddr 推荐使用 NULL ,系统默认分配内存地址
       shmflg 推荐 0,表示当前共享内存可读可写
   */
  
    char *shmaddr = shmat(shmid, NULL, 0);
  
    printf("data = %s\n", shmaddr);
    /*
    分离当前进程和共享内存
    */
    int ret = shmdt(shmaddr);
    if (-1 == ret)
    {
        perror("shmdt failed");
    }
    // 分离之后再次使用原本的共享内存,直接错误!
    printf("data = %s\n", shmaddr);
    sleep(30);
    return 0;
}
3.5 共享内存控制 shmctl
函数文档
#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> int shmctl(int shmid, int cmd, struct shmid_ds *buf);
- 函数功能
 
- 指定共享内存的控制函数,可以记录共享内存状态,也可以对共享内存状态进行获取,另外可以利用当前函数对共享内存进行关闭操作。
 - 函数参数
 
int shmid :共享内存标识符cmd:这是一个命令参数,用于指定要对共享内存段执行的操作。常见的命令有以下几种:
IPC_STAT:将共享内存段的当前状态信息复制到buf所指向的struct shmid_ds结构体中。这个结构体包含了共享内存段的各种属性,如权限、大小、创建时间等。IPC_SET:将buf所指向的struct shmid_ds结构体中的某些属性值设置到共享内存段中。可以设置的属性包括共享内存段的权限、所有者等。IPC_RMID:删除指定的共享内存段。一旦使用该命令删除共享内存段,该共享内存段将被标记为待删除状态,当所有连接到该共享内存段的进程都与之分离后,系统会自动释放该共享内存段所占用的资源。buf :
- 当
 cmd为IPC_STAT时,系统会将共享内存段的状态信息填充到buf所指向的结构体中。- 当
 cmd为IPC_SET时,系统会从buf所指向的结构体中读取相关属性值并设置到共享内存段中。- 当
 cmd为IPC_RMID时,buf参数通常被忽略,可以设置为NULL。
struct shmid_ds 结构体内容, 不同的操作系统情况下,结构体内容不一致
struct shmid_ds { struct ipc_perm shm_perm; /* 权限和所有者信息 */ size_t shm_segsz; /* 共享内存段的大小(以字节为单位) */ time_t shm_atime; /* 最后一次附加到共享内存段的时间 */ time_t shm_dtime; /* 最后一次分离共享内存段的时间 */ time_t shm_ctime; /* 最后一次更改共享内存段状态的时间 */ pid_t shm_cpid; /* 创建共享内存段的进程 ID */ pid_t shm_lpid; /* 最后一次执行 shmat 或 shmdt 操作的进程 ID */ shmatt_t shm_nattch; /* 当前附加到共享内存段的进程数量 */ ... };
shmctl
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SHM_SIZE 128
int main(int argc, char const *argv[])
{
    // 获取 IPC 唯一进程间通信标识符
    key_t key = ftok(".", 68);
    if (-1 == key)
    {
        perror("ftok failed!");
    }
    // 获取/申请共享内存
    int shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
    if (-1 == shmid)
    {
        perror("shmget failed!\n");
    }
    /*
    【内存映射】当前进程获取使用对应共享内存
       void *shmat(int shmid, const void *shmaddr, int shmflg);
       shmaddr 推荐使用 NULL ,系统默认分配内存地址
       shmflg 推荐 0,表示当前共享内存可读可写
   */
  
    char *shmaddr = shmat(shmid, NULL, 0);
  
    printf("data = %s\n", shmaddr);
    sleep(15);
    shmctl(shmid, IPC_RMID, NULL);
    return 0;
}
                - 感谢你赐予我前进的力量
                                
                                
                                    
 
赞赏者名单
                                    因为你们的支持让我意识到写文章的价值🙏
                                
                            
            本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载必须注明来自 卡卡罗特
        
     评论
            
                匿名评论
                隐私政策
            
            
                你无需删除空行,直接评论以获取最佳展示效果
            
        
            
        

                                    
                                    