目录
前言
一、system V IPC对象图解
1.流程图解:
编辑
2.查看linux内核中的ipc对象:
二、消息队列
1.消息队列的原理
2.消息队列相关的API
2.1 获取或创建消息队列(msgget)
实例代码如下:
2.2 发送消息到消息队列中
实例代码如下:
2.3 从消息队列中获取消息
实例代码如下:
2.4 消息队列相关的命令
2.5 管理消息队列
实例代码如下:
三、共享内存
1.概念
2.原理图解
3.相关的api函数
3.1 创建共享内存对象
实例代码如下:
3.2 映射共享内存
3.3 取消映射
实例代码reader.c如下:
实例代码writer.c如下:
实例代码shm.h 如下:
3.4 管理共享内存
实例代码如下:
四、信号灯 (信号量)
1.概念
2.信号灯相关的api函数
2.1 创建或获取信号灯对象
实例代码如下:
2.2 实现P操作和V操作
实例代码如下:
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
#include
#includeint msgget(key_t key, int msgflg);
//参数1 —– key :
动态获取key: ftok()
#include
#include
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 ——–
keymsqid owner perms used-bytesmessages
0x0a010356 0 peter 666615peter@ubuntu:~/2308/proc/day04_code$ ipcrm -q 0
peter@ubuntu:~/2308/proc/day04_code$ ipcs -q—— Message Queues ——–
keymsqid owner perms used-bytesmessages
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_tmsg_qnum; /* Current number of messages
in queue */
msglen_tmsg_qbytes;/* Maximum number of bytes
allowed in queue */
pid_tmsg_lspid;/* PID of last msgsnd(2) */
pid_tmsg_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
#include
int shmget(key_t key, size_t size, int shmflg);
//参数1 —- 动态获取key: ftok()
#include
#include
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 #include #include #include #include #include #include #define SHM_SIZE1024#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_tshm_cpid;/* PID of creator */
pid_tshm_lpid;/* PID of last shmat(2)/shmdt(2) */
shmatt_tshm_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
#include
#include
int semget(key_t key, int nsems, int semflg);
//参数1 —- 动态获取key: ftok()
#include
#include
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语言的其他重要的基础知识,感谢大家支持懒大王!
希望这篇博客能给各位朋友们带来帮助,最后懒大王请来过的朋友们留下你们宝贵的三连以及关注,感谢你们!