一、共享内存
0、进程间的通讯方式
管道、信号、共享内存、消息队列、信号量、套接字
管道和信号:只能用于两个进程间进行通讯
IPC:可以用于多个进程间进行通讯
1、IPC是用于多个进程间进行通讯的手段(Inter-Process Communication,进程间通信)
共享内存、消息队列、信号量
2、怎样去识别我的IPC
比如有A B C三个进程,这三个进程要想通讯,可以使用共享内存、消息队列、信号量。三个进程访问同一块共享内存,要确定操作的是同一个共享内存,确定共享内存的唯一性 键值
3、键值的创建于意义
唯一识别某个IPC
保证我多个进程打开的是同一个IPC
4、查看指令
ipcs -m 看Shared Memory 共享内存
ipcs -s 看Semaphore Arrays 信号量
ipcs -q 看Message Queues 消息队列
删除指令ipcrm -q id
1、获取一个键值 ftok (file to key的缩写,即将文件转换成key)
函数功能:获取一个键值
函数头文件:#include <sys/types.h>
#include <sys/ipc.h>
函数原型:key_t ftok(const char *pathname, int proj_id);
函数的参数:const char *pathname:参考位置 (一般来说 写绝对路径)
int proj_id:参考的id 0-255
函数的返回值:成功返回 键值 失败返回 -1
无论在任何位置 无论何时执行这个程序,只要你传入的参数是一致的 得到的键值就是唯一
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/ipc.h>
4 #include<sys/shm.h>
5 int main()
6 {
7 key_t keyid;
8 int shmid;
9 keyid=ftok("/home/ldw",5);
10 if(keyid<0)
11 {
12 perror("ftok");
13 return -1;
14 }
15 printf("keyid:%d\n",keyid);
16 return 0;
17 }
//keyid:83951618
共享内存
他就是一个加强版的全局变量
全局变量的特点
1、作用域 是整个工程
2、存放于全局区
3、在作用域范围之内 不能重名
共享内存:
1:范围是整个计算机的进程
2:整个计算里的进程都可以对她进行操作
本质:本质就是内存里的一段空间
(1.作用域:系统下的所有的进程都可以访问到 2.一个进程对共享内存做修改 其他的进程都能看到)
2、创建一个共享内存 shmget
函数功能:创建一块共享内存
函数的头文件:#include <sys/ipc.h>
#include <sys/shm.h>
函数的原型:int shmget(key_t key, size_t size, int shmflg);
函数的参数:key_t key:共享内存的键值
size_t size:要申请的共享内存的大小
int shmflg:标志 IPC_CREAT | 0777
IPC_CREAT:没有则创建,有则打开
IPC(包括消息队列,共享内存,信号量)的xxxget()创建操作时,可以指定IPC_CREAT和IPC_EXCL选项。
以共享内存为例:①当只有IPC_CREAT选项打开时,不管是否已存在该块共享内存,则都返回该共享内存的ID,若不存在则创建共享内存;②当只有IPC_EXCL选项打开时,不管有没有该快共享内存,shmget()都返回-1;③所以当IPC_CREAT | IPC_EXCL时, 如果没有该块共享内存,则创建,并返回共享内存ID。若已有该块共享内存,则返回-1;
函数的返回值:返回值为共享内存的id号,后续的操作要用到这个id号
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/ipc.h>
4 #include<sys/shm.h>
5 int main()
6 {
7 key_t keyid;
8 int shmid;
9 keyid=ftok("/home/ldw",5);
10 if(keyid<0)
11 {
12 perror("ftok");
13 return -1;
14 }
15 printf("keyid:%d\n",keyid);
16 shmid=shmget(keyid,20,IPC_CREAT|0777);
17 printf("shmid:%d\n",shmid);
18 return 0;
19 }
//keyid:83951618
//shmid:8683532
3、将共享内存映射到进程空间 shmat
函数功能:将一段共享内存 映射到进程空间
函数的头文件:#include <sys/types.h>
#include <sys/shm.h>
函数的原型:void *shmat(int shmid, const void *shmaddr, int shmflg);
函数的参数:int shmid:共享内存的id
const void *shmaddr:你要映射到的进程空间的地址
一般来说 我们不清楚 进程的哪段内存空间是可用
一般这个位置 写0 表示由系统自动分配空间
int shmflg:一般来说 也是写0 表示共享内存可读写
函数的返回值:返回的就是系统分配给你的空间的首地址
1 #include<stdio.h> //写内容
2 #include<sys/types.h>
3 #include<sys/ipc.h>
4 #include<sys/shm.h>
5 #include<string.h>
6 int main()
7 {
8 key_t keyid;
9 int shmid;
10 keyid=ftok("/home/ldw",5);
11 if(keyid<0)
12 {
13 perror("ftok");
14 return -1;
15 }
16 shmid=shmget(keyid,20,IPC_CREAT|0777);
17 void *p=shmat(shmid,0,0);
18 strcpy(p,"hello!");
19 return 0;
20 }
1 #include<stdio.h> //读内容
2 #include<sys/types.h>
3 #include<sys/ipc.h> //读和写,在不同的终端中
4 #include<sys/shm.h>
5 #include<string.h>
6 int main()
7 {
8 key_t keyid;
9 int shmid;
10 keyid=ftok("/home/ldw",5);
11 if(keyid<0)
12 {
13 perror("ftok");
14 return -1;
15 }
16 shmid=shmget(keyid,20,IPC_CREAT|0777);
17 void *p=shmat(shmid,0,0);
18 printf("said:%s\n",(char *)p);
19 return 0;
20 }
//said:hello!
4、解除映射 shmdt
函数的功能:解除共享内存的映射
函数的头文件:#include <sys/types.h>
#include <sys/shm.h>
函数的原型:int shmdt(const void *shmaddr);
函数的参数:const void *shmaddr:系统分配给的内存空间的首地址
函数的返回值:成功返回 0 失败返回 -1
5、删除一个共享内存 shmctl
函数功能:删除一个共享内存
函数的头文件:#include <sys/ipc.h>
#include <sys/shm.h>
函数原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
函数的参数:int shmid:共享内存的id
int cmd:IPC_RMID 删除共享内存
struct shmid_ds *buf:NULL
函数的返回值:成功返回 0 失败返回 -1
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/ipc.h>
4 #include<sys/shm.h>
5 #include<string.h>
6 int main()
7 {
8 key_t keyid;
9 int shmid;
10 keyid=ftok("/home/ldw",5);
11 if(keyid<0)
12 {
13 perror("ftok");
14 return -1;
15 }
16 shmid=shmget(keyid,20,IPC_CREAT|0777);
17 void *p=shmat(shmid,0,0);
18 strcpy(p,"hello!");
19 shmdt(p);
20 shmctl(shmid,IPC_RMID,NULL);
21 return 0;
22 }
二、信号量
就是用来解决多个进程间资源的冲突的问题的,信号量就是用来解决进程间的同步与互斥问题的一种进程间通信机制。
信号量:本质就是一个普通的数值,你操作信号量其实就是在操作这个数值
对这个数字可以进行加或者减的操作,对应的就是 P V操作
当我的数字减到0的时候 表示无资源可以使用
P操作
消耗一个信号量
如果信号量的值大于零,就给它减1;
如果的值为零,就挂起该进程的执行
V操作:
释放一个信号量
1、创建或者打开一个信号量 semget
函数功能:创建或者打开一个信号量
函数的头文件:#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
函数的原型:int semget(key_t key, int nsems, int semflg);
函数的参数:key_t key:键值
int nsems:你要创建的信号量的个数,这个位置一般填1
int semflg:IPC_CREAT | 0644 打开或创建信号量,并赋予权限
函数的返回值:成功返回信号量的id 失败返回 -1 默认信号量的初值为0
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/ipc.h>
4 #include<sys/sem.h>
5 int main()
6 {
7 key_t keyid;
8 int semid;
9 keyid=ftok("/home/ldw",2);
10 semid=semget(keyid,1,IPC_CREAT|0644);
11 printf("semid:%d\n",semid);
12 return 0;
13 }
//semid:32768
2、设置或者删除一个信号量 semctl
函数功能:设置或者删除一个信号量
函数头文件:#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
函数的原型:int semctl(int semid, int semnum, int cmd, ...);
函数的参数:int semid:信号量的id
int semnum:要操作的信号量的数组的下标 一般写0
int cmd:① IPC_RMID:删除信号量、② SETVAL:设置信号量
①:如果此处为IPC_RMID:删除信号量,则后面第四个空没有参数
②:如果此处为SETVAL:设置信号量,则第四个参数需要传入一个共用体
union semun {
short val; /*SETVAL用的值*/(给信号量赋的初值)
struct semid_ds* buf; /*IPC_STAT、IPC_SET用的semid_ds结构(则是一个内核结构体)*/
unsigned short* array; /*SETALL、GETALL用的数组值*/
struct seminfo *buf; /*为控制IPC_INFO提供的缓存(表示信号量系统资源配置信息)*/
} ;
函数的返回值:成功返回 0 失败返回 -1
3、释放或者消耗一个信号量 semop
函数功能:释放或者消耗一个信号量
函数的头文件:#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
函数的原型:int semop(int semid, struct sembuf *sops, size_t nsops);
函数的参数:int semid:信号量的 id
struct sembuf *sops:你要对信号量的操作是一个核心结构体
struct sembuf{
unsigned short sem_num; //信号在信号集中的索引,0代表第一个信号,1代表第二个信号 (即信号量数组的下标)
short sem_op; //进行什么操作:①+1:V操作 释放一个信号量;②-1:P操作 消耗一个信号量
short sem_flg; //操作标志0 设置信号量的默认操作(表示申请不到信号量就阻塞)
}
size_t nsops :你要操作的信号量个数,一般写1
(进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作)
函数的返回值:成功返回 0 失败返回 -1
1 #include<stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include<unistd.h>
6 #include <sys/types.h>
7 #include <sys/ipc.h>
8 #include <sys/sem.h>
9 int main()
10 {
11 struct sembuf myops;
12 int pd;
13 pd=fork();
14 if(pd == 0)
15 {
16 key_t keyid;
17 int semid;
18 keyid=ftok("/home/ldw",2);
19 semid=semget(keyid,1,IPC_CREAT|0644);
20 semctl(semid,0,SETVAL,2);
21 myops.sem_num=0;
22 myops.sem_op=-1;
23 myops.sem_flg=0;
24 semop(semid,&myops,1);
25 int i;
26 for(i=0;i<5;i++)
27 {
28 printf("i==%d\n",i);
29 sleep(1);
30 }
31 myops.sem_op=1;
32 semop(semid,&myops,1);
33 }
34 if(pd > 0)
35 {
36 sleep(1);
37 int j;
38 key_t keyid;
39 int semid;
40 keyid=ftok("/home/ldw",2);
41 semid=semget(keyid,1,IPC_CREAT|0644);
42 myops.sem_num=0;
43 myops.sem_op=-1;
44 myops.sem_flg=0;
45 semop(semid,&myops,1);
46 for(j=0;j<5;j++)
47 {
48 printf("j==%d\n",j);
49 sleep(1);
50 }
51 myops.sem_op=1;
52 semop(semid,&myops,1);
53 semctl(semid,0,IPC_RMID);
54 }
55 return 0;
56 }
//i==0 j==0 i==1 j==1 i==2 j==2 i==3 j==3 i==4 j==4
三、消息队列
消息队列才是最符合要求进程间的通讯
消息队列的特点:
①消息先进先出
②可以同时接收多条消息
③写消息的时候,你写的消息会暂存在消息队列;当有进程读消息的时候,读取的最先进队列的
消息队列的消息的分类:
可以是 1类消息
可以是 2类消息..........
比如现在有三个进程:
A B C
还有一个消息队列
A往消息队列里写 1类消息
B往消息队列写 2类消息
C去消息队列里读 1类或者是2类消息
C去消息队列里读 3类消息 会阻塞
1、创建或者打开一个消息队列 msgget
函数功能:创建或者打开一个消息队列
函数头文件:#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数原型:int msgget(key_t key, int msgflg);
函数的参数:key_t key:键值
int msgflg:IPC_CREAT|0644
函数的返回值:成功返回 消息队列的 id 失败返回 -1
1 #include<stdio.h>
2 #include <sys/types.h>
3 #include <sys/ipc.h>
4 #include <sys/msg.h>
5 int main()
6 {
7 int msqid;
8 key_t keyid;
9 keyid=ftok("/home/ldw",2);
10 printf("keyid:%d\n",keyid);
11 msqid=msgget(keyid,IPC_CREAT|0644);
12 printf("msqid:%d\n",msqid);
13 return 0;
14 }
keyid:33619970
msqid:0
2、向消息队列发送一条消息 msgsnd
函数功能:向消息队列发送一条消息,将消息发送到消息队列的末尾
函数的头文件:#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数的原型:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
函数的参数:int msqid:消息队列的 id
const void *msgp:消息结构体指针
struct msgbuf {
long mtype; /* message type, must be > 0 */消息的类型
char mtext[1]; /* message data */要写的消息的正文
};
size_t msgsz:消息的大小(<4k),不包括type。则 msgsz = sizeof(msgbuf) - sizeof(mtype)
msgflg:一般写 0 表示无法发送就阻塞(没有满足条件就阻塞当前进程)
函数的返回值:成功返回 0 失败返回 -1
1 #include<stdio.h>
2 #include <sys/types.h>
3 #include <sys/ipc.h>
4 #include <sys/msg.h>
5 #include<string.h>
6 struct msgbuf{
7 long mtype;
8 char mtext[100];
9
10 } mybuf;
11 int main()
12 {
13 key_t keyid;
14 int msgid;
15 keyid=ftok("/home/ldw",2);
16 msgid=msgget(keyid,IPC_CREAT|0644);
17 mybuf.mtype=1;
18 strcpy(mybuf.mtext,"hello");
19 msgsnd(msgid,&mybuf,sizeof(mybuf)-sizeof(long),0);
20 return 0;
21 }
3、从消息队列获取一条消息 msgrcv
函数功能:从消息队列读取一条数据
函数头文件:#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数原型:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
函数的参数:int msqid:消息队列的 id
void *msgp:存放读取到的消息的位置
struct msgbuf {
long mtype; /* message type, must be > 0 */消息的类型
char mtext[1]; /* message data */要读的消息的正文
};
size_t msgsz:消息的大小,包括type (否则段错误)
long msgtyp:接收消息的类型
int msgflg:0 表示读取不到就阻塞(调用阻塞直到条件满足为止)
函数的返回值:成功返回 0 失败返回 -1
1 #include<stdio.h>
2 #include <sys/types.h>
3 #include <sys/ipc.h>
4 #include <sys/msg.h>
5 #include<string.h>
6 struct msgbuf{
7 long mtype;
8 char mtext[100];
9 }mybuf;
10 int main()
11 {
12 key_t keyid;
13 char buf[120];
14 int msgid;
15 keyid=ftok("/home/ldw",2);
16 msgid=msgget(keyid,IPC_CREAT|0644);
17 msgrcv(msgid,buf,sizeof(mybuf),1,0);
18 printf("buf=%s\n",(buf+8));
19 msgctl(msgid,IPC_RMID,NULL);
20 return 0;
21 }
//buf=hello
4、删除一个消息队列 msgctl
函数功能:删除一个消息队列
函数头文件:#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数原型:int msgctl(int msqid, int cmd, struct msqid_ds *buf);
函数的参数:int msqid:消息队列的 id
int cmd:IPC_RMID
struct msqid_ds *buf:NULL
函数的返回值:成功返回 0 失败返回 -1