Linux基础内容(19)—— 进程间通信(介绍与管道内容)_哈里沃克的博客-CSDN博客
目录
1.共享内存的原理
2.共享内存的概念和特点
创建共享内存
共享内存的形式
共享内存(ipc资源)的调用和特征
用户接口删除共享内存
共享内存关联
去关联
特点
共享内存大小
1.共享内存的原理
1.由于进程本身的结构 --- 虚拟内存地址空间,使得进程间互相看不见对面的物理内存地址,达不到进程间直接通信的目的
2.但如果OS能提供一个接口,这个接口是用户调用之可以构建出共享的内存,并且共享内存的地址通过页表映射到虚拟地址空间中,两个进程如果都这里映射了同一个共享内存,那么就可以达到进程间通信的目的
3.如果不想通信了,需要切断两边的映射(去关联)和释放共享内存
理解:
1.进程间通信是专门设计的,用户只需要调用接口
2.共享内存是一种通信方式,所有想通信的进程都可以用
3.OS中一定可能同时存在多个共享内存
2.共享内存的概念和特点
通过让不同的进程看到同一块内存块,这样的内存称之为共享内存
创建共享内存
size:申请多大的共享内存
key:能进行唯一性表示的数,具体内容不重要,看到同一个key就可以看到同一个资源了
shmflg:
IPC_CREAT -- 指定的共享内存,不存在,创建;存在,共享内存标志返回
IPC_EXCL -- 无法单独使用;只有当 IPC_CREAT|IPC_EXCL 同时使用,如果不存在,创建,如果存在,出错返回
返回值:成功返回共享内存的标识符,失败返回-1
注意:创建时还得把共享内存的权限属性给传入
转换得到key值
pathname:一个字符串
proj_id:一个数字
这样组合出唯一的数字key返回
返回:成功返回key值,失败-1
共享内存的形式
1.c语言中malloc函数需要输入大小,但是free反而不需要。这是因为malloc函数不会只给我们申请指定大小空间,它一定要申请更大的空间来存储它的属性方便管理
2.关于以前进程的也是先描述再组织
3.那么对于共享内存,也是如此,我们不会只申请指定大小就结束了,我们还会存储共享内存的相关属性,这样方便管理,那么对于共享内存而言:物理内存存储块+共享内存属性
4.上面说过,共享内存可能有很多,那么如何表示唯一性呢?答案很明显,key就是唯一的,那么创建时我们能通过key得到唯一的共享内存。
5.那么key也是一种属性,所以这个key也被放在描述共享内存的属性之中。
6.那么链接共享内存就是通过得到一样的key,在组织数据结构中遍历找每个内存属性中放key的值,找到了就唯一指定了,key是内核级别的属性
7.shmid和key的关系是:shmid是在用户级别调用的数值。其区别就于fd和inode类似,inode内核级别表示文件的唯一性,而用户直接不使用inode,而是调用函数返回的fd进行操作
共享内存(ipc资源)的调用和特征
1.共享内存的生命周期随着OS的,不随进程而消除(system V特性)
2.ipcs -m查看共享内存
perm:文件权限
nattch:关联的进程
3.ipcrm -m shmid删除共享内存
3.ipcs -q查看消息队列
4. ipcs -s查看信号量
用户接口删除共享内存
该接口是对共享内存进行控制,控制里包括删除
shmid:指定shmid
cmd:指令
IPC_STAT
IPC_SET
IPC_RMID:立即移除共享内存
buf:获取属性存储空间,不要则输入nullptr
返回值:失败则-1
删除该共享内存的前提就是将内存与进程相关联,毕竟调用系统接口删除的前提是有这块地址
共享内存关联
用于映射到虚拟地址空间的哪个位置中
后两位数指定位置,一般不指定:设为nullptr和0
返回值:共享内存地址空间的返回值
失败是返回-1
去关联
shmdt()
返回0成功,-1失败
特点
优点:所以进程间通信中速度最快的
因为我们写入共享内存,另一方就能得到,能大大减少拷贝的次数
同样的代码用来通信,(单纯的收发消息)需要拷贝几次才能完成任务:
管道实现:4+2次
共享内存实现:2+2次
缺点:不给我们进行同步互斥消息的,没有对数据做任何保护
共享内存大小
建议是4KB的整数倍
因为系统分配共享内存是以4KN为单位的 -- 内存划分内存块的基本单位
给大给小都会向上取整:使用的内存显示为申请的,但是内存中的确开辟,只是用户调不到
comm.hpp
#ifndef _COMM_HPP_
#define _COMM_HPP_
#include <iostream>
#include <cstdio>
#include <cerrno>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#define PATHNAME "."
#define PROJ_ID 0x66
#define MAX_SIZE 4096
key_t getKey()
{
key_t k = ftok(PATHNAME, PROJ_ID);
if (k < 0)
{
std::cerr << errno << ":" << strerror(errno) << std::endl;
exit(1);
}
return k;
}
int getShmHelper(key_t k, int flags)
{
int shmid = shmget(k, MAX_SIZE, flags);
if(shmid<0)
{
std::cerr << errno << ":" << strerror(errno) << std::endl;
exit(2);
}
return shmid;
}
int creatShm(key_t k)
{
return getShmHelper(k, IPC_CREAT | IPC_EXCL | 0600); // 创建一个新的,所以如果没有就判错
}
int getShm(key_t k)
{
return getShmHelper(k, IPC_CREAT /*0*/); // 调用创建的,0也可以
}
void* attchShm(int shmid)
{
void* mem = shmat(shmid,nullptr,0);
if((long long)mem==-1L)
{
std::cerr << errno << ":" << strerror(errno) << std::endl;
exit(3);
}
return mem;
}
void detachShm(void* start)
{
if(shmdt(start)==-1)
{
std::cerr << errno << ":" << strerror(errno) << std::endl;
}
}
void deleteShm(int shmid)
{
if(shmctl(shmid,IPC_RMID,nullptr)==-1)
{
std::cerr << errno << ":" << strerror(errno) << std::endl;
exit(1);
}
}
#endif
server.cc
#include "comm.hpp"
int main()
{
key_t k = getKey();
printf("0x%x\n",k);
int shmid = creatShm(k);
printf("shmid:%d\n",shmid);
sleep(5);
char* start = (char*)attchShm(shmid);
printf("attach success, adress start: %p\n",start);
//使用
while(true)
{
shmid_ds ds;
shmctl(shmid,IPC_STAT,&ds);
printf("获取信息: size:%d ,pid: %d ,myself: %d",ds.shm_segsz,ds.shm_cpid,getpid());
printf("client say:%s\n",start);
sleep(1);
}
//去关联
detachShm(start);
//删除
deleteShm(shmid);
return 0;
}
client
#include "comm.hpp"
int main()
{
key_t k = getKey();
printf("0x%x\n",k);
int shmid = getShm(k);
printf("shmid:%d\n",shmid);
sleep(5);
char* start = (char*)attchShm(shmid);
const char* message = "hello server,我是另一个进程";
pid_t id = getpid();
int cnt = 1;
//char* buffer[1024];
while(true)
{
snprintf(start,MAX_SIZE,"%s[pid:%d][消息编号%d]",message,id,cnt);
//snprintf(buffer,sizeof(buffer),"%s[pid:%d][消息编号%d]",message,id,cnt);
//memcpy(start,buffer,strlen(buffer)+1);
// pid,count,mess
}
detachShm(start);
return 0;
}