前言
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语言的其他重要的基础知识,感谢大家支持懒大王!