C/C++ 进程间通信system V IPC对象超详细讲解(系统性学习day9)

目录

前言

[一、system V IPC对象图解](#一、system V IPC对象图解)

1.流程图解:

​编辑

2.查看linux内核中的ipc对象:

二、消息队列

1.消息队列的原理

2.消息队列相关的API

[2.1 获取或创建消息队列(msgget)](#2.1 获取或创建消息队列(msgget))

实例代码如下:

[2.2 发送消息到消息队列中](#2.2 发送消息到消息队列中)

实例代码如下:

[2.3 从消息队列中获取消息](#2.3 从消息队列中获取消息)

实例代码如下:

[2.4 消息队列相关的命令](#2.4 消息队列相关的命令)

[2.5 管理消息队列](#2.5 管理消息队列)

实例代码如下:

三、共享内存

1.概念

2.原理图解

3.相关的api函数

[3.1 创建共享内存对象](#3.1 创建共享内存对象)

实例代码如下:

[3.2 映射共享内存](#3.2 映射共享内存)

[3.3 取消映射](#3.3 取消映射)

实例代码reader.c如下:

实例代码writer.c如下:

[实例代码shm.h 如下:](#实例代码shm.h 如下:)

[3.4 管理共享内存](#3.4 管理共享内存)

实例代码如下:

[四、信号灯 (信号量)](#四、信号灯 (信号量))

1.概念

2.信号灯相关的api函数

[2.1 创建或获取信号灯对象](#2.1 创建或获取信号灯对象)

实例代码如下:

[2.2 实现P操作和V操作](#2.2 实现P操作和V操作)

实例代码如下:

[2.3 管理信号灯](#2.3 管理信号灯)

实例代码如下:

总结


前言

System V IPC(Inter-Process Communication)对象是一种用于在不同进程之间进行通信的机制。它包括三种类型的对象:消息队列(Message Queue)、信号量(Semaphore)和共享内存(Shared Memory)。


一、system V IPC对象图解

1.流程图解:

2.查看linux内核中的ipc对象:

**二、**消息队列

1.消息队列的原理

2.消息队列相关的API

2.1 获取或创建消息队列(msgget)

头文件:

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

int msgget(key_t key, int msgflg);

//参数1 ----- key :

动态获取key: ftok()

#include <sys/types.h>

#include <sys/ipc.h>

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

//参数1 ---- 工程目录

//参数2 ---- 工程编号

//返回值 ----- 成功,返回key值,失败:-1

静态分配: IPC_PRIVATE

//参数2 ------ msgflg:如果消息队列不存在,需要给出创建的关键字,并设置权限 IPC_CREAT | 0666

//返回值 ----- 成功:消息队列的ID,失败:-1

实例代码如下:

int main(void)
{
    key_t key;
    int msg_id;

    //获取key值
    key = ftok("./",0xa);
    if(key < 0){
        perror("ftok");
        exit(1);
    }
    //创建或获取消息对象
    msg_id = msgget(key,IPC_CREAT|0666);
    if(msg_id < 0){
        perror("msgget");
        exit(1);
    }
    return 0;
}

2.2 发送消息到消息队列中

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

//参数1 ----- 消息队列的id

//参数2 ----- 指向struct msgbuf结构体的指针,该结构体需要自己定义,如下:

struct msgbuf {

long mtype; /* message type, must be > 0 消息类型*/

char mtext[1]; /* message data 消息正文*/

};

//参数3 ---- 要发送的消息的长度

//参数4 ---- msgflg值如下:

IPC_NOWAIT 消息没有发送完成函数也会立即返回。

0:直到发送完成函数才返回

//返回值 --- 成功:0,失败:-1

实例代码如下:

int main(void)
{
    key_t key;
    int msg_id;

    //获取key值
    key = ftok("./",0xa);
    if(key < 0){
        perror("ftok");
        exit(1);
    }
    //创建或获取消息对象
    msg_id = msgget(key,IPC_CREAT|0666);
    if(msg_id < 0){
        perror("msgget");
        exit(1);
    }

//向消息队列中发送消息
    while(1){
        bzero(&buf,sizeof(buf));
        printf("请输入消息的类型:");
        scanf("%ld",&buf.mtype);
        printf("请输入消息:");
        while(getchar() != '\n');   //清空输入缓冲区
        fgets(buf.mtext,sizeof(buf.mtext),stdin);
        buf.mtext[strlen(buf.mtext)-1] = '\0';
        if(msgsnd(msg_id,&buf,strlen(buf.mtext),0) < 0){
            perror("msgsnd");
            exit(1);
        }
    }

    return 0;
}

2.3 从消息队列中获取消息

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

//参数1 ----- 消息队列的id

//参数2 ----- 指向struct msgbuf结构体的指针,该结构体需要自己定义,如下:

struct msgbuf {

long mtype; /* message type, must be > 0 消息类型*/

char mtext[1]; /* message data 消息正文*/

};

//参数3 ---- mtext的长度

//参数4 ---- msgtyp:要接收的消息类型

msgtyp > 0 ,表示接收指定类型的消息

msgtyp = 0 ,按先后顺序依次接收不同类型消息

msgtyp < 0 ,优先接收消息类型不大于|msgtyp|的最小类型的消息

//参数5 ---- msgflg值如下:

IPC_NOWAIT 消息没有发送完成函数也会立即返回。

0:直到发送完成函数才返回

//返回值 --- 成功:0,失败:-1

实例代码如下:

int main(void)
{
    key_t key;
    int msg_id;

    //获取key值
    key = ftok("./",0xa);
    if(key < 0){
        perror("ftok");
        exit(1);
    }
    //创建或获取消息对象
    msg_id = msgget(key,IPC_CREAT|0666);
    if(msg_id < 0){
        perror("msgget");
        exit(1);
    }

//从消息队列中获取消息
    while(1){
        bzero(&buf,sizeof(buf));
        printf("请输入消息的类型:");
        scanf("%ld",&buf.mtype);
        if(msgrcv(msg_id,&buf,sizeof(buf.mtext),buf.mtype,0) < 0){
            perror("msgsnd");
            exit(1);
        }
        printf("msg:%s\n",buf.mtext);
    }

    return 0;
}

2.4 消息队列相关的命令

peter@ubuntu:~/2308/proc/day04_code$ ipcs -q

------ Message Queues --------

key msqid owner perms used-bytes messages

0x0a010356 0 peter 666 61 5

peter@ubuntu:~/2308/proc/day04_code$ ipcrm -q 0

peter@ubuntu:~/2308/proc/day04_code$ ipcs -q

------ Message Queues --------

key msqid owner perms used-bytes messages

2.5 管理消息队列

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

//参数1 ---- 消息队列ID

//参数2 ---- 功能码:

IPC_STAT:读取消息队列的属性,并将其保存在buf指向的缓冲区中。

IPC_SET:设置消息队列的属性。这个值取自buf参数。

IPC_RMID:从系统中删除消息队列。

//参数3 ----struct msqid_ds 结构体指针

struct msqid_ds {

struct ipc_perm msg_perm; /* Ownership and permissions */

time_t msg_stime; /* Time of last msgsnd(2) */

time_t msg_rtime; /* Time of last msgrcv(2) */

time_t msg_ctime; /* Time of last change */

unsigned long __msg_cbytes; /* Current number of bytes in

queue (nonstandard) */

msgqnum_t msg_qnum; /* Current number of messages

in queue */

msglen_t msg_qbytes; /* Maximum number of bytes

allowed in queue */

pid_t msg_lspid; /* PID of last msgsnd(2) */

pid_t msg_lrpid; /* PID of last msgrcv(2) */

};

实例代码如下:

int main(int argc,char **argv)
{
    int msg_id;

    msg_id = atoi(argv[1]);

    if(msgctl(msg_id,IPC_RMID,NULL) < 0){
        perror("msgget");
        exit(1);
    }
    return 0;
}

三、共享内存

1.概念

共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝

为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间

进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。

由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等

2.原理图解

3.相关的api函数

3.1 创建共享内存对象

头文件:

#include <sys/ipc.h>

#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);

//参数1 ---- 动态获取key: ftok()

#include <sys/types.h>

#include <sys/ipc.h>

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

//参数1 ---- 工程目录

//参数2 ---- 工程编号

//返回值 ----- 成功,返回key值,失败:-1

静态分配: IPC_PRIVATE

//参数2 ---- 要创建或获取的共享内存的大小

//参数3 ---- 权限:IPC_CREAT | 0666

//返回值 ---- 成功:共享内存ID,失败:-1

实例代码如下:

int main(void)
{
    key_t key;
    int shm_id;

    //获取key值
    key = ftok("./",0xa);
    if(key < 0){
        perror("ftok");
        exit(1);
    }
    //创建或获取共享内存对象
    shm_id = shmget(key,SHM_SIZE, IPC_CREAT|0666);
    if(shm_id < 0){
        perror("shmget");
        exit(1);
    }
    return 0;
}

3.2 映射共享内存

void *shmat(int shmid, const void *shmaddr, int shmflg);

//参数1 ---- 共享内存ID

//参数2 ---- 指定进程虚拟空间的映射的起始地址,一般为NULL:让系统分配一个起始地址

//参数3 ---- 访问权限:SHM_RDONLY:共享内存只读

默认0:共享内存可读写

//返回值 --- 成功:映射的虚拟空间地址,失败:-1

3.3 取消映射

int shmdt(const void *shmaddr);

//参数 ----映射的虚拟空间的起始地址

//返回值 ----成功:0,失败:-1

实例代码reader.c如下:

#include "shm.h"

int main(void)
{
    key_t key;
    int shm_id;
    char *buf;

    //获取key值
    key = ftok("./",0xa);
    if(key < 0){
	perror("ftok");
	exit(1);
    }
    //创建或获取共享内存对象
    shm_id = shmget(key,SHM_SIZE, IPC_CREAT|0666);
    if(shm_id < 0){
	perror("shmget");
	exit(1);
    }

    //将共享内存映射到进程的虚拟空间中
    buf = (char*)shmat(shm_id,NULL,0);
    if(buf < 0){
	perror("shmat");
	exit(1);
    }


    //打印共享内存中的数据
    while(1){
	printf("%s",buf);
	sleep(1);
    }

    //解除映射
    if(shmdt(buf) < 0){
	perror("shmdt");
	exit(1);
    }
    return 0;
}

实例代码writer.c如下:

#include "shm.h"

int main(void)
{
    key_t key;
    int shm_id;
    char *buf;

    //获取key值
    key = ftok("./",0xa);
    if(key < 0){
	perror("ftok");
	exit(1);
    }
    //创建或获取共享内存对象
    shm_id = shmget(key,SHM_SIZE, IPC_CREAT|0666);
    if(shm_id < 0){
	perror("shmget");
	exit(1);
    }

    //将共享内存映射到进程的虚拟空间中
    buf = (char*)shmat(shm_id,NULL,0);
    if(buf < 0){
	perror("shmat");
	exit(1);
    }


    //向共享内存写数据
    while(1){
	printf("请输入字符串:");
	fgets(buf,SHM_SIZE,stdin);
    }

    //解除映射
    if(shmdt(buf) < 0){
	perror("shmdt");
	exit(1);
    }
    return 0;
}

实例代码shm.h 如下:

#ifndef __SHM_H__
#define __SHM_H__

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

#define SHM_SIZE  1024

#endif

3.4 管理共享内存

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

//参数1 ---- 共享内存的ID

//参数2 ---- 功能码:

IPC_STAT (获取对象属性)

IPC_SET (设置对象属性)

IPC_RMID (删除对象)

//参数3 ----struct shmid_ds 结构体指针

struct shmid_ds {

struct ipc_perm shm_perm; /* Ownership and permissions */

size_t shm_segsz; /* Size of segment (bytes) */

time_t shm_atime; /* Last attach time */

time_t shm_dtime; /* Last detach time */

time_t shm_ctime; /* Last change time */

pid_t shm_cpid; /* PID of creator */

pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */

shmatt_t shm_nattch; /* No. of current attaches */

...

};

实例代码如下:

int main(int argc,char **argv)
{
    int shm_id;

    shm_id = atoi(argv[1]);

    if(shmctl(shm_id,IPC_RMID,NULL) < 0){
        perror("shmget");
        exit(1);
    }
    return 0;
}

四、信号灯 (信号量)

1.概念

信号灯(semaphore),也叫信号量。它是不同进程间或一个给定进程内部不同线程间同步的机制。

信号灯种类:

posix有名信号灯

posix基于内存的信号灯(无名信号灯)

System V信号灯(IPC对象)

1》 二值信号灯:用于表示资源是否可用

值为0或1。与互斥锁类似,资源可用时值为1,不可用时值为0。

2》 计数信号灯:用于表示资源的数量

值在0到n之间。用来统计资源,其值代表可用资源数

3》 等待操作,也称为P操作

是等待信号灯的值变为大于0,然后将其减1;

4》 释放操作,也称为V操作

用来唤醒等待资源的进程或者线程

5》System V的信号灯是一个或者多个信号灯的一个集合。其中的每一个都是单独的计数信号灯。而Posix信号灯指的是单个计数信号灯

2.信号灯相关的api函数

2.1 创建或获取信号灯对象

头文件:

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);

//参数1 ---- 动态获取key: ftok()

#include <sys/types.h>

#include <sys/ipc.h>

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

//参数1 ---- 工程目录

//参数2 ---- 工程编号

//返回值 ----- 成功,返回key值,失败:-1

静态分配: IPC_PRIVATE

//参数2 ---- 集合中信号灯的个数

//参数3 ---- 访问权限:IPC_CREAT | 0666

//返回值 ---- 成功:信号灯对象ID,失败:-1

实例代码如下:

int main(void)
{
    key_t key;
    int sem_id;

    //获取key值
    key = ftok("./",0xa);
    if(key < 0){
        perror("ftok");
        exit(1);
    }
    //创建或获取共享内存对象
    sem_id = semget(key,1, IPC_CREAT|0666);
    if(sem_id < 0){
        perror("semget");
        exit(1);
    }
    return 0;
}

2.2 实现P操作和V操作

int semop(int semid, struct sembuf *sops, size_t nsops);

//参数1 ----- 信号灯对象的ID

//参数2 ----- 结构体指针

struct sembuf {

short sem_num; // 要操作的信号灯的编号

short sem_op; // 0 : 等待,直到信号灯的值变成0

// 1 : 释放资源,V操作

// -1 : 分配资源,P操作

short sem_flg; // 0, IPC_NOWAIT, SEM_UNDO

};

//参数3 -----nops: 要操作的信号灯的个数

//返回值 ---- 成功:0,失败:-1

实例代码如下:

//1》实现P操作
	void sem_p(int sem_id,int index)
    {
        struct sembuf buf = {index,-1,0};

        if(semop(sem_id,&buf,1) < 0){
            perror("semop");
            exit(1);
        }
    }
//2》实现v操作
    void sem_v(int sem_id,int index)
    {
        struct sembuf buf = {index,1,0};

        if(semop(sem_id,&buf,1) < 0){
            perror("semop");
            exit(1);
        }
    }

2.3 管理信号灯

int semctl(int semid, int semnum, int cmd, ...);

//参数1 ---- 信号灯对象ID

//参数2 ---- 集合中信号灯的编号

//参数3 ---- 功能码:

IPC_STAT ----获取信号灯对象属性

IPC_SET ----设置信号灯对象属性

IPC_RMID ----从内核中删除信号灯对象

SETALL ----设置集合中所有信号灯的值

SETVAL ----设置集合中编号为semnum的信号灯的值

//参数4 ---- 联合体变量,类型如下:

union semun {

int val; /* Value for SETVAL */

struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */

unsigned short *array; /* Array for GETALL, SETALL */

struct seminfo *__buf; /* Buffer for IPC_INFO

(Linux-specific) */

};

//返回值 -----成功:0,失败:-1

实例代码如下:

//初始化指定的信号灯
void sem_init(int sem_id,int semnum,int value)
{
    union semun su;
    su.val = value;
    if(semctl(sem_id,semnum,SETVAL,su) < 0){
        perror("semctl");
        exit(1);
    }
}
//初始化所有信号灯
void sem_init_all(int sem_id,unsigned short vals[])
{
    union semun su;
    su.array = vals;
    if(semctl(sem_id,0,SETALL,su) < 0){
        perror("semctl");
        exit(1);
    }
}

总结

本篇文章针对进程间通信system V IPC对象进行详细讲解,希望能够帮到大家!

以后还会给大家展现更多关于嵌入式和C语言的其他重要的基础知识,感谢大家支持懒大王!

希望这篇博客能给各位朋友们带来帮助,最后懒大王请来过的朋友们留下你们宝贵的三连以及关注,感谢你们!

相关推荐
Kisorge37 分钟前
【C语言】指针数组、数组指针、函数指针、指针函数、函数指针数组、回调函数
c语言·开发语言
机智的叉烧41 分钟前
前沿重器[57] | sigir24:大模型推荐系统的文本ID对齐学习
人工智能·学习·机器学习
量子-Alex2 小时前
【多模态聚类】用于无标记视频自监督学习的多模态聚类网络
学习·音视频·聚类
轻口味2 小时前
命名空间与模块化概述
开发语言·前端·javascript
吉大一菜鸡2 小时前
FPGA学习(基于小梅哥Xilinx FPGA)学习笔记
笔记·学习·fpga开发
晓纪同学3 小时前
QT-简单视觉框架代码
开发语言·qt
威桑3 小时前
Qt SizePolicy详解:minimum 与 minimumExpanding 的区别
开发语言·qt·扩张策略
飞飞-躺着更舒服3 小时前
【QT】实现电子飞行显示器(简易版)
开发语言·qt
明月看潮生3 小时前
青少年编程与数学 02-004 Go语言Web编程 16课题、并发编程
开发语言·青少年编程·并发编程·编程与数学·goweb
明月看潮生3 小时前
青少年编程与数学 02-004 Go语言Web编程 17课题、静态文件
开发语言·青少年编程·编程与数学·goweb