如果要实现两个进程之间进行通信 两个进程之间是不能直接通信的 必须借助内核,
如进程1和进程2进行通信的话 进程1可能会借助内核中的某一块内存空间 这块内存空间要是两个进程都能访问的 然后进程1将要通信的内容写入(write 本质上copy)到这块内存空间中去
然后进程2再从这块内存空间中去读取(read 其本质也是copy) 这样就实现了进程间的通信。
问题:
假设我们有三个进程 进程1 进程2 进程3 我们如果假设进程1中某块内存作为进程2 进程3的共享内存 也就是说进程2 和进程3通信其实是通过进程1中的某块内存进程通信的 这种方式是否可行?
不行 因为我们讲进程之间的不能直接访问的 而且另一个问题是 如果进程1死掉了 那么进程2和进程3可能也会跟着出问题。
在内核中开辟一块共享内存,其他进程通过“映射”方式获取这段共享内存的引用(指针)
System V共享内存
通过ftok()函数获取System V IPC对象的key —> 通过key创建/打开这个IPC的设施(msg/shm/sem) —>通过System V IPC提供的读/写函数接口交换数据 —>关闭设施
相关函数:
ftok用来创建一个System V IPC对象的key
#include
#include
key_t ftok(const char *pathname, int proj_id);
函数功能:ftok用来创建一个System V IPC对象的key
参数列表:
pathname: 一个文件系统中的路径名(必须要存在的并且有权限读取的)
proj_id: 就是一个整数 这个参数存在的意思在于在一个文件也能生成多个IPC key键值。
ftok利用同一个文件最多可得到IPC key键值256个
因为ftok只取proj_id值二进制后8位 及16进制后2位 与文件信息合成IPC key键值
也就是ftok("/home/china",257) 《==》ftok("/home/china",1) 生成的键值是一样的
返回值:
成功生成一个唯一的System V IPC对象的key(类型key_t --->int)
失败返回-1 同时errno被设置
shmget 创建/打开一个System V的共享内存
#include
#include
int shmget(key_t key, size_t size, int shmflg);
函数功能:创建/打开一个System V的共享内存
参数列表:
key :System V IPC对象的key 一般由ftok的返回值获得
size:以字节为单位指定共享内存区的大小
当实际操作为创建一个新的共享内存区时 必须指定一个不为0的size值(page_size的整数倍:4096)
如果实际操作为访问一个已经存在的共享内存去 那么size一般为0
shmflg:标志位
如果是创建共享内存 : IPC_CREAT | 权限位
如果是打开共享内存 : 0
其实你完全按照创建的方式指定也可以 因为在创建共享内存的时候 如果发现key对应的共享内存
已经存在了 那么他会自动忽略后面的选项直接打开
返回值:
成功返回创建地共享内存的id (shmid)
失败返回-1
shmat 映射操作
#include
#include
void *shmat(int shmid, const void *shmaddr, int shmflg);
函数功能:将shmid指定的共享内存 映射到进程的地址空间
参数列表:
shmid: 表示你要映射哪块共享内存 (shmget的返回值)
shmaddr: 表示你要映射到进程的地址空间的具体哪个地方
一般为NULL 表示让系统自行分配
shmflg: 标志位权限
1. SHM_RDONLY 只读
2. 0 读写
返回值:
成功返回映射空间的首地址(指针)
失败返回(void *)-1 并且errno被设置
int shmdt(const void *shmaddr);
函数功能:解除映射
参数列表:
shmaddr: 映射空间的首地址
返回值:
成功返回0
失败返回-1 并且errno被设置
设置共享内存 比如删除共享内存
#include
#include
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
函数功能: 对共享内存的一些控制操作
头文件:如上
参数列表:
shmid : 你要对哪块共享内存进行操作
cmd: 操作命令 不同的命令第三个参数不一样
cmd == IPC_RMID 删除对应的共享内存
buf: 如果cmd == IPC_RMID buf就为NULL
返回值:
成功返回0
失败返回-1 并且errno被设置
使用共享内存实现父子进程通信
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
pid_t pid;
pid=fork();
if(pid ==0)
{
//创建钥匙
key_t key=ftok("/home/china",250);
if(key ==-1)
{
perror("key error\n");
return -1;
}
//创建共享内存
int shmid=shmget(key,4096,IPC_CREAT |0666);
if(shmid ==-1)
{
perror("shmid erorr\n");
return -1;
}
//映射
char * p=shmat(shmid,NULL,0);
if(p==(char *)-1)
{
perror("shmat error\n");
return -1;
}
//写数据
strcpy(p,"Hello share memory");
//解映射
shmdt(p);
}
else if(pid >0)
{
//创建钥匙
key_t key=ftok("/home/china",250);
if(key ==-1)
{
perror("key error\n");
return -1;
}
//创建共享内存
int shmid=shmget(key,4096,IPC_CREAT |0666);
if(shmid ==-1)
{
perror("shmid erorr\n");
return -1;
}
//映射
char * p=shmat(shmid,NULL,0);
if(p==NULL)
{
perror("shmat error\n");
return -1;
}
//读数据
char buf[1024]={0};
strcpy(buf,p);
printf("%s\n",buf);
//解映射
shmdt(p);
//删除共享内存
//shmctl(shmid,IPC_RMID,NULL);
//wait(NULL);
}
return 0;
}