System V通信机制

IPC对象概述

IPC对象 消息队列 共享内存 信号量
特点 多对多 共享数据 具备阻塞的全局变量

设置流程

  1. 获取IPC对象key
  2. xxxget//获取对象ID值
  3. xxxctl//操作函数
  4. 相对独立的操作函数

IPC相关bash命令

bash 复制代码
ipcs [-参数]
>-a:显示所有 IPC 设施(默认选项)。
>-q:仅显示消息队列。
>-m:仅显示共享内存段。
>-s:仅显示信号量。
>-l:显示 IPC 资源的系统限制。
>-u:显示 IPC 设施的汇总信息。
>-p:显示与 IPC 设施相关的进程 ID。
>-t:显示时间信息,如上次操作时间。
>-c:显示创建者的用户和组信息。
>-b:显示 IPC 设施的权限和大小信息。


ipcrm -[参数]#在bash中删除IPC对象
> -m, --shmem-id <id>        remove shared memory segment by id
> -M, --shmem-key <key>      remove shared memory segment by key
> -q, --queue-id <id>        remove message queue by id
> -Q, --queue-key <key>      remove message queue by key
> -s, --semaphore-id <id>    remove semaphore by id
> -S, --semaphore-key <key>  remove semaphore by key
> -a, --all[=shm|msg|sem]    remove all (in the specified category)
> -v, --verbose              explain what is being done
> -h, --help                 display this help
> -V, --version              display version

消息队列

1.创建key

函数原型 key_t ftok(const char *pathname, int proj_id);
头文件 #include <sys/ipc.h>
参数 pathname:指向一个已存在文件的路径字符串,key:一个 8 位的项目标识符(范围 0-255)
返回值 成功返回key_t类型,失败返回*-1*

2.创建对象

函数原型 int msgget(key_t key, int msgflg);
头文件 #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h>
参数 key:由 ftok msgflg:标志位,用于指定队列的权限和创建行为
返回值 成功时返回消息队列的标识符(非负整数)。失败时返回 -1
msgflg
IPC_CREAT IPC_CREAT:如果队列不存在,则创建。
IPC_EXCL 与 IPC_CREAT 一起使用,确保队列不存在时创建,否则返回错误。
权限模式 IPC_CREAT|0666:在创建时同时复制权限

3.进行发送

函数原型 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
头文件 #include <sys/msg.h>
参数 msqid:消息队列标识符,由 msgget 函数返回。msgp:指向消息缓冲区的指针,缓冲区需包含消息类型和消息内容。msgsz:消息正文的大小(以字节为单位),不包括消息类型字段;msgflg:控制标志,通常为 0(阻塞模式)或 IPC_NOWAIT(非阻塞模式)。
返回值 成功时返回 0,失败时返回 -1 并设置 errno。
msgflag
IPC_NOWAIT 非阻塞模式
0 进行阻塞

在创建根据手册描述需要创建一个包含正文,和类型的结构体

c 复制代码
struct msgbuf {
    long mtype;     // 消息类型,必须为正整数
    char mtext[1];  // 消息正文,实际使用时需自定义长度
};

示例

c 复制代码
typedef struct msgbuf
{
    long mtype;       /* message type, must be > 0 */
    char mtext[1024];    /* message data */
}msgbuf;

int main(void)
{
    key_t key = ftok(".", 123);//在当前路径下创建生成一个"钥匙"
    int mag_id = msgget(key, IPC_CREAT | 0664);//创建一个新的消息队列
    msgbuf my_msbuf;
    my_msbuf.mtype = 100;
 
    memset(my_msbuf.mtext, 0, sizeof(my_msbuf.mtext));
    scanf("%s", my_msbuf.mtext);
    msgsnd(mag_id, &my_msbuf, strlen(my_msbuf.mtext), 0);//进行阻塞的发送信息
    return 0;
}

运行结果

4.进行接收

函数原型 int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
头文件 #include <sys/msg.h>
参数 msqid:消息队列标识符,由 msgget 创建。msgp:通常是一个结构体,包含消息类型和消息正文。msgsz:消息正文的大小msgtyp:指定接收的消息类型;msgflg:控制行为的标志位
返回值 成功时返回实际接收的消息正文的字节数。失败时返回 -1,并设置 errno。
msgflag
IPC_NOWAIT 如果没有符合条件的消息,立即返回错误(不阻塞)
MSG_NOERROR 如果消息正文超过 msgsz,截断消息而不报错。
0 进行阻塞

示例

c 复制代码
int main(void)
{
    key_t key = ftok(".", 123);//在当前路径下创建生成一个"钥匙"
    int mag_id = msgget(key, IPC_CREAT|0664);//创建一个新的消息队列
    msgbuf my_msbuf;
    my_msbuf.mtype = 200;
    
    while (1)
    {
        memset(my_msbuf.mtext, 0, sizeof(my_msbuf.mtext));
        msgrcv(mag_id, &my_msbuf, sizeof(my_msbuf.mtext), 100, MSG_NOERROR);//进行阻塞的发送信息
        printf("2接收到程序:%s\n", my_msbuf.mtext);
    }
}

运行结果

5.操作消息队列

函数原型 int msgctl(int msqid, int cmd, struct msqid_ds *buf);
头文件 #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h>
参数 msqid:消息队列的标识符,由 msgget 函数创建或获取。cmd:控制命令;buf:指向 struct msqid_ds 的指针,用于存储或设置消息队列的属性。
返回值 成功时返回 0。失败时返回 -1,并设置 errno 以指示错误类型。

属性信息结构体struct msqid_ds

c 复制代码
struct msqid_ds {
    struct ipc_perm msg_perm;  // 权限信息
    time_t msg_stime;          // 最后发送消息的时间
    time_t msg_rtime;          // 最后接收消息的时间
    time_t msg_ctime;          // 最后修改时间
    unsigned long __msg_cbytes; // 当前队列中的字节数
    msgqnum_t msg_qnum;        // 当前队列中的消息数
    msglen_t msg_qbytes;       // 队列的最大字节数
    pid_t msg_lspid;           // 最后发送消息的进程 PID
    pid_t msg_lrpid;           // 最后接收消息的进程 PID
};

常用命令

cmd
IPC_STAT 获取消息队列的属性,存储到 buf 指向的结构体中
IPC_SET 设置消息队列的属性,从 buf 指向的结构体中读取
IPC_RMID 立即删除消息队列,忽略 buf 参数

示例

c 复制代码
int main(void)
{
    key_t key = ftok(".", 123);//在当前路径下创建生成一个"钥匙"
    int mag_id = msgget(key, IPC_CREAT | 0664);//创建一个新的消息队列
    msgbuf my_msbuf;
    my_msbuf.mtype = 100;
  
    struct msqid_ds msginfo;
    msgctl(mag_id, IPC_STAT,&msginfo);
    printf("信息数量:%lu\n", msginfo.msg_qnum);
    return 0;
}

运行结果

消息队列是流操作


共享内存

1.创建创建key

创建key与消息队列相同,这里就不过多赘述

*2.创建shareMemoryID

函数原型 int shmget(key_t key, size_t size, int shmflg);
头文件 #include <sys/ipc.h> #include <sys/shm.h>
参数 key:共享内存段的键值,通常由 ftok 生成或直接使用 IPC_PRIVATE。size:共享内存段的大小,通常是4096的倍数。如果是获取已存在的共享内存,此参数可设为 0。shmflg:权限标志
返回值 成功时返回共享内存段的标识符(非负整数)。失败时返回 -1,并设置 errno 表示错误原因。

shmflg

shmflg
IPC_CREAT 创建,可以与限权相或进行设置
IPC_EXCL 确保在创建新的 IPC 资源(如共享内存、消息队列或信号量)时,如果该资源已存在,则创建操作会失败。

3.映射内存

函数原型 void *shmat(int shmid, const void *shmaddr, int shmflg);
头文件 #include <sys/shm.h>
参数 shmid:共享内存标识符,由 shmget 调用返回;shmaddr:指定共享内存附加到进程地址空间的位置。通常设为 NULL,由系统自动选择地址。shmflg:附加标志,例如 SHM_RDONLY 表示只读访问。
返回值 shmat 返回共享内存附加到进程地址空间的起始地址。失败时,返回 (void *)-1,并设置 errno 以指示错误原因。

shmflg

shmflg
0 默认,代表共享内存可读可写。
SHM_RDONLY 代表共享内存只读。

示例

c 复制代码
int main(void)
{
    key_t key = ftok(".", 1);//在当前路径下创建生成一个"钥匙"
    if(key == -1)
    {
        perror("ftok sailed\n");
    }
    int shm_ID = shmget(key, 4096, IPC_CREAT|0664);
    if (shm_ID == -1) 
    {
        perror("shmget failed\n");
        exit(1);
    }
    int shmdt(const void *shmaddr);
    char * add = shmat(shm_ID, NULL, 0);
    sprintf(add, "1234");
    printf("%s", add+1);
    return 0;
}

执行结果

操作

函数原型 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
头文件 #include <sys/ipc.h> #include <sys/shm.h>
参数 shmid:指定的共享内存的ID;cmd:一些命令字;buf:用来存放共享内存信息的结构体
返回值 失败返回-1

cmd参数说明

cmd
IPC_STAT 将共享内存的当前状态信息复制到 buf 指向的 shmid_ds 结构中。调用者需具备读权限。
IPC_SET 通过 buf 修改共享内存的权限、所有者或时间戳等字段。仅超级用户或有效用户 ID 与共享内存所有者/创建者匹配的用户可执行。
IPC_RMID 标记共享内存段为待销毁状态。实际销毁会在最后一个附加进程分离后发生。此操作需超级用户或有效用户 ID 匹配所有者/创建者。