笔记五:进程间的通信(IPC通信之共享内存)
IPC通信
IPC通信(Inter-Process Communication)
三种: 共享内存、消息队列、信号灯
这个IPC对象,肯定是存在于内核中。而且用户空间的文件系统中有没有IPC的文件类型?没有。
有名管道为什么能实现无亲缘关系的进程之间的通信?
是因为用户空间有管道这种文件类型。
IPC是不是只能用于亲缘关系进程之间的通信呢?肯定不是
它是怎样实现无亲缘关系之间的通信呢?也即你是保证用户空间的二个进程对内核中的同一个IPC对象的操作。(ftok)
IPC对象的打开或创建: 类似于open的函数呢?
IPC和文件I/O函数的比较
文件I/O
|
IPC
|
open
| Msg_get
S
hm_get
S
em_get
|
read
write
| msgsnd msgrecv
shmat shmdt
semop
|
close
| msgctrl
shmctrl
semctrl
|
1.共享内存
(1) shmget
所需头文件
| #include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
|
函数原型
|
int shmget(key_t key, int size, int shmflg);
|
函数参数
|
key
:
IPC_PRIVATE
或
ftok
的返回值
|
|
size
:共享内存区大小
|
|
shmflg
:同
open
函数的权限位,也可以用
8
进制表示法
|
函数返回值
|
成功:共享内存段标识符---ID---文件描述符
|
函数返回值
|
成功:共享内存段标识符---ID---文件描述符
|
|
出错:
-1
|
查看IPC对象 ipcs –m:查看共享内存 ipcs -q:查看消息队列 ipcs -s:查看信号
删除IPC对象 ipcrm -m id
返回值:共享内存段标识符 IPC的ID号
通过宏定义IPC_PRIVATE创建共享内存,那么ID号都为0;
打开或创建一个共享内存对象,共享内核在内核是什么样子的?
一块缓存,变类似于用户空间的数组或malloc函数分配的空间一样
memcpy(p,"abcd",4);(字符串拷贝)
第一个参数:共享内存中;
第二个参数:字符串;
第三个参数:size(大小);
(2) ftok:创建key值。
函数原型: char ftok(const char *path, char key )
参数: 第一个参数:文件路径和文件名;
第二个参数: 一个字符。
返回值: 正确返回一个key值,出错返回-1
IPC_PRIVATE操作时,共享内存的key值都一样,都是0,所以使用ftok来创建key值。只要key值是一样的,用户空间的进程通过这个函数打开,则会对内核的同一个IPC对象操作。
例子:
#include "sys/types.h"
#include "sys/ipc.h"
#include "sys/shm.h"
#include "stdlib.h"
#include "stdio.h"
int main(int argc,char *argv[])
{
int key;
int shmid;
char *p;//
key=ftok("./a.c",'b');
if(key <0 )
{
printf("creat key failure\n");
return -1;
}
shmid= shmget(key,128,IPC_CREAT | 0777);
if(shmid < 0)
{
printf("creat share memory failure\n");
return -2;
}
return 0;
}
(3) shmat 将共享内存映射到用户空间的地址中
能不能用read ,write呢?(必须每次进入内核调用)
为了方便用户空间对共享内存的操作,使用地址映射的方式。
函数原型:void *shmat (int shmid, const void *shmaddr, int shmflg); //malloc(通过这个函数就会映射到用户空间,所以就更加方便了没有必要每次进入内核进行操作)
参数: 第一个参数:ID号;
第二个参数:映射到的地址,NULL为系统自动完成的映射;
第三个参数shmflg: SHM_RDONLY共享内存只读 默认是0,表示共享内存可读写。
返回值: 成功:映射后的地址; 失败:NULL。
共享内存特点:
共享内存创建之后,一直存在于内核中,直到被删除或系统关闭;
共享内存和管道不一样,读取后,内容仍在其共享内存中。
(4) shmdt:将进程里的地址映射删除
函数原型:int shmdt(const void *shmaddr);
参数: shmaddr共享内存映射后的地址
返回值: 成功:0 出错:-1
(5) shmctl:删除共享内存对象
函数原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
函数参数:第一个参数shmid:要操作的共享内存标识符。
第二个参数cmd:IPC_STAT (获取对象属性)--- 实现了命令ipcs -m
IPC_SET (设置对象属性)
IPC_RMID (删除对象) ---实现了命令ipcrm -m(删除的时候第三个参数可以设置为NULL);
第三个参数buf :指定IPC_STAT/IPC_SET时用以保存/设置属性。(进行设置的时候)
函数返回值:成功:0
出错:-1
有亲缘关系进程之间的通信的实例:
1.怎样通过共享内存实现有亲缘关系进程之间的通信框架
当使用IPC_PRIVATE 实现有亲缘关系进程之间通信时,fork()函数一定要放在shmget()函数之后
例子:(单向通信一个写端一个读端)
read.c端
#include "sys/types.h"
#include "sys/ipc.h"
#include "sys/shm.h"
#include "stdlib.h"
#include "stdio.h"
int main(int argc,char *argv[])
{
int key;
int shmid;
char *p;//
key=ftok("./a.c",'b');//通过ftok创建key值,实现非亲缘关系进程间的通信;
if(key <0 )
{
printf("creat key failure\n");
return -1;
}
shmid= shmget(key,128,IPC_CREAT | 0777);//创建共享内存
if(shmid < 0)
{
printf("creat share memory failure\n");
return -2;
}
p=(char *)shmat(shmid,NULL,0);//将内存映射到用户空间
if(p == NULL)
{
printf("shmat failure\n");
return -3;
}
/*从共享内存中读*/
while(1)
{
sleep(2);
printf("recv from share memory data : %s\n",p);
}
shmdt((void *)p);//将进程里的地址映射删除;
p=NULL;
//shmctl(shmid,IPC_RMID,NULL);//将内核空间的缓存进行释放,每次用完必须进行释放,否则一直存在于内核中;写端已经删除所以读端可以不用释放;
return 0;
}
write.c端
#include "sys/types.h"
#include "sys/ipc.h"
#include "sys/shm.h"
#include "stdlib.h"
#include "stdio.h"
int main(int argc,char *argv[])
{
int key;
int shmid;
char *p;//
key=ftok("./a.c",'b');
if(key <0 )
{
printf("creat key failure\n");
return -1;
}
shmid= shmget(key,128,IPC_CREAT | 0777);
if(shmid < 0)
{
printf("creat share memory failure\n");
return -2;
}
p=(char *)shmat(shmid,NULL,0);
if(p == NULL)
{
printf("shmat failure\n");
return -3;
}
/*往共享内存中写入*/
while(1)
{
printf("please input send to share memory data:\n");
fgets(p,128,stdin);
if(strncmp(p,"quit",4) == 0)
break;
}
shmdt((void *)p);
p=NULL;
shmctl(shmid,IPC_RMID,NULL);//将内核空间的缓存进行释放,每次用完必须进行释放,否则一直存在于内核中;
return 0;
}