自我测试

  • | 什么是IPC进程间通信,有三种,分别用什么符号表示,他们三种都有id与key吗,id与key是什么怎么使用?怎么创建一个ipc的key值,怎么获得id,是先有id还是先有key,这个key的对象是文件夹
  • | 如何显示所有的IPC对象
  • | 如何根据id删除共享内存ipc;如何根据id删除队列ipc;如何根据id删除信号量ipc
  • | 如何根据key删除共享内存ipc;如何根据key删除队列ipc;如何根据key删除信号量ipc
  • | 如何获取其他进程的共享内存的key值
  • | 怎么打开一个共享内存,没有就自动创建
  • | 映射共享内存的函数是什么
  • | 断开共享内存的函数是什么
  • | 删除共享内存的函数是什么
  • | 查看系统最大共享内存大小的指令是什么
  • | 消息队列有什么特点,创建一个消息结构体
  • | 如何打开一个消息队列,没有就自动创建
  • | 如何向消息队列发送消息;如何从消息队列接收消息
  • | 如何删除消息队列
  • | 信号量/信号灯的特点,信号灯与信号灯集是什么
  • | 信号灯集如何打开,没有就自动创建
  • | 信号灯集如何初始化
  • | 什么是P/V操作,如何进行P/V操作
  • | 如何删除信号灯

第一讲

  • IPC对象是内核级别的多个进程间通信的工具,这些工具都有编号:共享内存(会议室)、消息队列(运送消息的火车,火车=车头+车厢)和信号量(摇旗)

  • 每个IPC对象有唯一的ID与key值,key值对于一个文件夹,相当于开启一个钥匙,只有钥匙权限才能获取房间里面的ipc的id

  • IPC对象用完记得删除(会议室用完关门,火车回站台,旗子回收)

  • 因为IPC是多个进程间通信的东西,创建IPC的进程有IPC的ID号可以直接打开,那么其他进程如何使用该IPC与创造IPC的进程通信呢;KEY是IPC的属性,使得不同进程可以打开同一个IPC,创建该IPC的进程可以通过ID号打开IPC,其他进程不知道这个ID只能通过KEY值获取到IPC的ID

  • key=0=IPC_PRIVATE代表这个私有的,意思就是这个IPC对象只能我自己这个进程私自使用,其他进程不能通过我的私有IPC来获取我的PIC号

命令 截图
ipcs命令
//显示所有IPC对象
ipcrm命令
ipcrm -M key 根据key删除内存类型ipc
ipcrm -m id根据pic号删除内存型pic


ipcrm -Q key根据键值删除队列pic
ipcrm -q id 根据pic号删除队列型pic


ipcrm -S key根据键值删除信号量pic
ipcrm -s id根据pic号删除信号量pic


id=pic号;key=键值;m=内存类型;q=队列类型;s=信号量类型

1.1 生成key值/创建IPC (先创建ipc,再去ipc里面选哪个类型顺便给你一个身份证id) ftok函数file to key,创建ipc的key值

key_t ftok(const char*path, int proj_id);
path = 文件路径
proj_id = 生成key的数字,不等于0
返回值:key键值

功能:为当前进程生成一个键值 ====================================================================== 23 void main() 24 {  25 key_t key; 26 27 //为本进程深沉一个健 28 //参数1:本进程的文件路径 29 //参数2:一个随机值x,对应的经过函数处理后返回y,也就是健值,不同的x-->不同的Y 30 key=ftok("/home/farsight/Desktop",1); 31 32 //如果健值=-1代表深生成错误 33 if(key==-1) 34 {  35 perror("\nkey error:\n"); 36 } 37 else 38 {  39 //健是一个整数,可以用%d打印察看 40 printf("\n健:%d\n",key); 41 } 42 } 
共享内存 1最高效的进程间通信方式
2可以直接读写内存
3由于共享内存在内核空间内创建,所有需要将其映射用户空间才能访问
4由于多个进程可同时访问共享内存,因此需要同步和互斥机制配合使用
共享内存的操作步骤 截图如下
步骤1:创建/打开共享内存 int shmget(key_t key int size, int shmflg);


参数1:key就是进程的键值;IPC_PRIVATE的话就不用为进程新建键值,IPC_PRIVATE就是键值0;
参数2:共享内存的字节大小
参数3:IPC_CREAT 与 0666(记住)
返回值:共享内存的的id;小于0则错误
注意:第一个进程创建共享内存后,后面的进程使用该函数因为已经创建了共享内存,就不会再创建,直接返回该共享内存的id号就ok
步骤2:映射共享内存 void * shmat(int shmi, const voidshmaddr, int shmflg);
返回映射后的地址,失败返回(void
)-1
参数1:共享内存ID
参数2:NULL表示自动映射
参数3:0表示对映射地址读写; SHM RDONLY表示映射地址只读
;at是在什么什么地方
步骤3:读写共享内存 对映射地址的写=sprintf,然后直接用printf就能打印映射地址的内容
步骤4:断开内存映射 int shmdt(void*shmaddr);
成功时返回0,失败时返回EOF
参数:被断开连接的映射地址;dt=disconnect
步骤5:删除共享内存 int shmctl(int shmi, int cmd, struct shmid_ds*buf);
1:成功时返回0,失败时返回EOF
参数1:要操作的共享内存的id
参数2:IPC_STAT(获取共享内存的属性);IPC_SET(设置共享内存的属性);IPC_RMID(释放内核中的共享内存)
参数3 shmid_ds结构体变量,用于接收shmctl函数返回的共享内存的属性
ipcs -l 注意:共享内存的空间大小是限制的,为了不超过共享内存的最大字节数,我们先用ipcs -l 指令查看最大内存空间是多少,在决定共享内存的空间多大
l=limit
功能介绍: 1:创建两个.c的父进程文件分别为text1.c与text2.c 2:要求两个父进程通过共享内存,实现text1.c写数据,text2.c读数据

text1.c的代码 /*=============================================================== * Copyright (C) 2020 All rights reserved. * * 文件名称:text1.c * 创 建 者:liujing * 创建日期:2020年07月14日 * 描 述: * * 更新日志: * ================================================================*/ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <strings.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> #include <sys/ipc.h> #include <sys/shm.h> void main() {  /*******************************创建key值***********************************/ key_t key; //为本进程深沉一个健 //参数1:本进程的文件路径 //参数2:一个随机值x,对应的经过函数处理后返回y,也就是健值,不同的x-->不同的Y key=ftok("/home/farsight/Desktop",1); //如果健值=-1代表深生成错误 if(key==-1) {  perror("\nkey error:\n"); } else {  //健是一个整数,可以用%d打印察看 printf("\n健:%d\n",key); } /*******************************创建共享内存***********************************/ int picid = shmget(key,2000,IPC_CREAT|0666); if(-1 == picid) {  perror("\n创建共享内存error\n"); } else {  printf("\n创建共享内存成功,picid=%d\n",picid); } /*******************************映射共享内存***********************************/ char*addr; //在进程地址空间建立一个存放char的空间地址用于对接内存的映射地址 addr = (char*)shmat(picid,NULL,0);//由于shmat返回的是void*,所以需要强转 if(addr==(char*)-1) {  perror("\n映射共享内存error\n"); } else {  printf("\n映射共享内存成功\n"); } /****************************共享内存的映射区域的操作******************************/ int i = 0; while(i<5) {  sprintf(addr,"hello word"); printf("\n写入hello word成功\n"); i++; sleep(1); } /****************************断开映射******************************/ if(EOF == shmdt((void*)addr)) {  perror("\n 断开映射error\n"); } else {  printf("\n断开映射成功\n"); } /****************************删除内核中的共享内存******************************/ if(EOF == shmctl(picid,IPC_RMID,NULL)) {  perror("\n删除共享内存error\n"); } else {  printf("\n删除共享内存成功\n"); } } text2.c的代码 /*=============================================================== * Copyright (C) 2020 All rights reserved. * * 文件名称:text2.c * 创 建 者:liujing * 创建日期:2020年07月14日 * 描 述: * * 更新日志: * ================================================================*/ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <strings.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> #include <sys/ipc.h> #include <sys/shm.h> void main() {  /*******************************创建key值***********************************/ key_t key; //为本进程深沉一个健 //参数1:本进程的文件路径 //参数2:一个随机值x,对应的经过函数处理后返回y,也就是健值,不同的x-->不同的Y key=ftok("/home/farsight/Desktop",1); //如果健值=-1代表深生成错误 if(key==-1) {  perror("\nkey error:\n"); } else {  //健是一个整数,可以用%d打印察看 printf("\n健:%d\n",key); } /*******************************创建共享内存***********************************/ int picid = shmget(key,2000,IPC_CREAT|0666); if(-1 == picid) {  perror("\n创建共享内存error\n"); } else {  printf("\n创建共享内存成功,picid=%d\n",picid); } /*******************************映射共享内存***********************************/ char*addr; //在进程地址空间建立一个存放char的空间地址用于对接内存的映射地址 addr = (char*)shmat(picid,NULL,0);//由于shmat返回的是void*,所以需要强转 if(addr==(char*)-1) {  perror("\n映射共享内存error\n"); } else {  printf("\n映射共享内存成功\n"); } /****************************共享内存的映射区域的操作******************************/ int i = 0; while(i<5) {  if(*addr != 0) {  puts(addr); i++; } } /****************************断开映射******************************/ if(EOF == shmdt((void*)addr)) {  perror("\n 断开映射error\n"); } else {  printf("\n断开映射成功\n"); } /****************************删除内核中的共享内存******************************/ if(EOF == shmctl(picid,IPC_RMID,NULL)) {  perror("\n删除共享内存error\n"); } else {  printf("\n删除共享内存成功\n"); } } ======================================================================== text1.c的运行结果: farsight@ubuntu:~/Desktop$ ./text1

健:16854188 创建共享内存成功,picid=753680 映射共享内存成功

写入hello word成功

写入hello word成功

写入hello word成功

写入hello word成功

写入hello word成功

断开映射成功

删除共享内存error


text2.c的运行结果: farsight@ubuntu:~/Desktop$ ./text2

健:16854188 创建共享内存成功,picid=753680 映射共享内存成功
hello word
hello word
hello word
hello word
hello word

断开映射成功

删除共享内存成功

第二讲 消息队列机制

  • 消息队列是头尾相连的消息链表组成,消息链表里面存放着数据类型一致的大量数据

>>消息队列的结构图

2.1 消息队列的使用步骤

消息格式 消息是一个结构体,需要自己定义,该结构体第一个成员是>0的long类型数据代表该消息的类型,后面的成员是若干数组,用于存放用户需要发送的数据
下面放上一个消息结构体的最简单的定义格式
步骤1:打开/创建消息队列msgget int msgget(key _t key ,int msgflg);
该函数与共享内存的创建/打开函数使用一致

参数1:这个key值与前面的共享内存的key一致,都是通过ftok函数产生,所有结论就是消息队列/共享内存/信号量的key都是IPC的key,都是通过ftok函数生成…如果是私有key就写IPC_PRIVATE,不是私有key就用ftok生成

参数2:该参数与共享内存的参数一样,也是填IPC_CREAT 与 0666(记住)
步骤2:向消息队列发送消息 msgsnd int msgsnd (int msgid, const void*msgp, size t size int msgflg);
返回值:0=成功;EOF=失败
参数1:消息队列的ID
参数2:被发送的消息的地址
参数3:发送的消息长度
参数4: 0=发送完后该函数就立即返回成功=0
步骤3:·从消息队列接收消息 msgrcv int msgrcv(int msgid, void*msgp, size t size, long msgtype, int msgflg):
返回值:成功时返回收到的消息长度,失败时返回-1
参数1:消息队列id
参数2:接收消息的队列消息结构体地址
参数3:接收消息的长度
参数4:接收消息的类型
参数5:0=阻塞式(常用),一直等接收到消息;IPC_NOWAIT,没有消息就立即返回错误
步骤4:·控制消息队列 msgctl
int msgctl(int msgid, int cmd, struct msqid_ds * buf);
成功时返回0,失败时返回-1
参数1:消息队列id
参数2:要执行的操作 IPC_STAT/ PC_SET/ IPC_RMID
参数3:存放消息队列属性的地址
//注意:消息队列执行IPC_RMID命令会立即释放队列,一般在最后一个进程对消息队列进行删除
使用消息队列,实现两个进程text1.c与text2.c之间的进程通信

text1.c的代码如下: ================================================================*/ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <strings.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/ipc.h> #include <sys/msg.h> //队列消息的结构体 struct mymsg {  long mtype; /* Message type. */ char mtext[32]; /* Message text. */ }; void main() {  //第一步:创建IPC的key值 key_t key; key = ftok("/home/farsight/Desktop",1); if(key == EOF) {  perror("\nkey create error:\n"); } else {  printf("\nkey create success\n"); } //第二步:根据key值获取队列ID / 创建,打开队列 int queueid = 0; queueid = msgget(key,IPC_CREAT|0666); if(queueid == -1) {  perror("\nqueueid get error:\n"); } else {  printf("\nqueueid get success\n"); } //第三步:向队列发队列消息 struct mymsg mymsg1 = { 1,"hello word"}; //发送队列缓冲区 struct mymsg getmsg; //接受队列缓冲区 char getbuf[32] = { 0}; int i=0; //五次循环后退出 while(i<5) {  sleep(1); msgsnd(queueid,(void*)&mymsg1,sizeof(mymsg1.mtext),0); printf("\n向队列发%s\n",mymsg1.mtext); sleep(1); //接受text2.c进程2发送的2号队列链表里面的队列数据 msgrcv(queueid,(void*)&getmsg,sizeof(mymsg1.mtext),2,0); printf("\n队列接受到%s\n",getmsg.mtext); i++; } //释放消息队列 if(-1 == msgctl(queueid,IPC_RMID,NULL)) {  perror("\nmsgctl error:\n"); } else {  printf("\n释放队列完成\n"); } } text2.c的代码如下: ================================================================*/ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <strings.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/ipc.h> #include <sys/msg.h> //队列消息的结构体 struct mymsg {  long mtype; /* Message type. */ char mtext[32]; /* Message text. */ }; void main() {  //第一步:创建IPC的key值 key_t key; key = ftok("/home/farsight/Desktop",1); if(key == EOF) {  perror("\nkey create error:\n"); } else {  printf("\nkey create success\n"); } //第二步:打开队列,获得队列的ID int queueid = 0; queueid = msgget(key,IPC_CREAT|0666); if(queueid == -1) {  perror("\nqueueid get error:\n"); } else {  printf("\nqueueid get success\n"); } //第三步:向队列发队列消息 struct mymsg mymsg1 = { 2,"HELLO WORD"}; struct mymsg getmsg;//该消息结构体变量用于接受队列消息 int i = 0; while(i<5) {  sleep(1); msgsnd(queueid,(void*)&mymsg1,sizeof(mymsg1.mtext),0); printf("\n向队列发%s\n",mymsg1.mtext); sleep(1); //接受另一个text1.c进程1发送的1号队列链表里面的队列数据 msgrcv(queueid,(void*)&getmsg,sizeof(mymsg1.mtext),1,0); printf("\n队列接受到%s\n",getmsg.mtext); i++; } //队列的使用,最后一个进程释放队列,其他进程不管 } ================================================================== text1.c的运行结果: farsight@ubuntu:~/Desktop$ ./text1

key create success

queueid get success

向队列发hello word

队列接受到HELLO WORD

向队列发hello word

队列接受到HELLO WORD

向队列发hello word

队列接受到HELLO WORD

向队列发hello word

队列接受到HELLO WORD

向队列发hello word

队列接受到HELLO WORD

释放队列完成 ================================================================= text2.c 的运行结果
farsight@ubuntu:~/Desktop$ ./text2

key create success

queueid get success

向队列发HELLO WORD

队列接受到hello word

向队列发HELLO WORD

队列接受到hello word

向队列发HELLO WORD

队列接受到hello word

向队列发HELLO WORD

队列接受到hello word

向队列发HELLO WORD

队列接受到hello word >>结果:成功! 

第三讲

信号灯类型 信号灯=无名信号灯+有名信号灯+ system v信号灯集
system v 信号灯集 / 系统5信号灯集 多个信号灯的集合
同时操作多个信号灯
系统5信号灯是产生解决了多个单个的信号灯的操作有死锁bug的问题
system 5 信号灯集的使用步骤 讲解
步骤1:信号灯集的创建/打开 int semget(key_t key, int nsems, int semflg);
成功时返回信号灯的d,失败时返回-1
参数1:PIC的key值
参数2:信号灯的个数;
参数3:IPC_CREAT , 0666
整体用法与前面的共享内存与消息队列的创建/打开函数类似
步骤2:信号灯集初始化 信号灯的值是脏数据,需要初始化
int semctl(int semid, int semnum, int cmd, …);
返回值:成功时返回0,失败时返回EOF
参数1:信号灯ID
参数2:0;表示初始化第一个信号灯;x表示初始化第x+1个信号灯
参数3:SETVAL(设置信号灯的值);IPC_RMID(删除ID)

如果参数3的值是SETVAL,设置信号灯的值,我们就设置的值封装成共用体的形式传递给参数4;参数4:需要传递一个union共用体类型,需要自己定义,具体查看man手册
步骤3:P/V操作 int semop(int semid struct sembuf *sops, unsigned nsops);
返回值:成功时返回0,失败时返回-1
参数1被操作的信号灯集的ID
参数2:一个描述对怎么对信号灯操作的结构体;同理如果需要同时操作多个,就需要传入结构体数组
参数3:要操作的信号灯的个数

信号灯操作结构体如下(该结构体库里面有,直接调用就ok)
op=option=选项,选p还是v
步骤4:删除信号灯 semctl,传递命令参数为_IPCRM
================================================================*/ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <strings.h> #include <sys/ipc.h> #include <sys/sem.h> static int A = 0; static int B = 0; //初始化信号值 union semun {  int val; struct semid_ds *buf; unsigned short *array; } arg[2]; void main() {  //pic的key值创建 key_t semkey; semkey = ftok("/home/farsight/Desktop",1); if(semkey == -1) {  perror("ftok error:"); } else {  printf("\ncreate key success\n"); } //创建信号灯集,获取信号灯集ID int semid = 0; semid = semget(semkey,2,IPC_CREAT|0666); if(semid == -1) {  perror("semget id error:"); } else {  printf("\nreturn semid success\n"); } //初始化信号灯集合 arg[0].val = 0; arg[1].val = 0; //初始化信号1 semctl(semid,0,SETVAL,arg[0]); //初始化信号2 semctl(semid,1,SETVAL,arg[1]); printf("\ninit success\n"); //v操作 struct sembuf semcontol[2]; semcontol[0].sem_num = 0; semcontol[0].sem_op = 1; semcontol[0].sem_flg = 0; semcontol[1].sem_num = 1; semcontol[1].sem_op = 1; semcontol[1].sem_flg = 0; semop(semid,semcontol,2); printf("\nV option success\n"); //p操作 semcontol[0].sem_num = 0; semcontol[0].sem_op = 0; semcontol[0].sem_flg = 0; semcontol[1].sem_num = 1; semcontol[1].sem_op = 0; semcontol[1].sem_flg = 0; if( 0 == semop(semid,semcontol,2)) {  printf("\nP option success\n"); } else {  perror("P option error:"); } A++; B++; printf("A = %d",A); printf("B = %d",B); semctl(semid,0,IPC_RMID); semctl(semid,1,IPC_RMID); }