进程间通信【共享内存】

文章详细介绍了Linux系统中使用共享内存进行进程间通信的原理和步骤,包括创建、关联、去关联以及控制共享内存的方法,如shmget、shmat、shmdt和shmctl函数的使用,并提供了示例代码展示如何在两个进程中通过共享内存进行通信。
摘要由CSDN通过智能技术生成

共享内存

进程间通信的前提是:先让不同的进程,看到同一份资源

之前,管道进程通信是采用看到同一个文件,那么共享内存就是看到同一份内存

共享内存原理

image-20230106193445842

使用共享内存进行通信

1、先在物理内存中创建共享内存

2、将每一个进程通过页表和共享内存产生关联

3、使用共享内存进行通信

4、使用结束,将进程和共享内存去关联

5、删除共享内存

共享内存,首先需要在物理内存中先创建一份共享内存,通过页表把共享内存映射到各自进程的虚拟进程地址空间的共享区(在栈区和堆区之间(使用动态库时,动态库也映射到这部分))来实现各自进程关联共享内存

ps:有创建共享内存关联共享内存,就有删除共享内存去关联共享内存

创建共享内存

int shmget(key_t key, size_t size, int shmflg);

key:表示共享内存唯一性的值,两个进程使用同一个key值就可以看到同一个共享内存

size:共享内存大小(建议4kb的整数倍)

分配空间单位是4kb,如果申请4097个字节,它会分配给8kb,但是只有4097个字节可供使用

shmflg:IPC_CREAT,IPC_EXCL

PC_CREAT:创建共享内存,如果存在,就获取它,如果不存在,就创建共享内存

IPC_EXCL:不能单独使用,必须和IPC_CREAT组合使用,如果共享内存不存在,就创建共享内存,如果存在,返回失败(这是为了保证函数调用成功一定是一个全新的共享内存)

返回值:如果创建成功,会返回共享内存标识符(shmid),失败会返回-1

在这里我们介绍了shmget函数的参数,那么我们怎么知道这个共享内存是否存在呢?

首先,我们需要知道可能存在多个共享内存,那么就需要对共享内存进行管理,先描述,再组织,那么在内核中,内核会给我们维护共享内存的数据结构。

下表,共享内存的数据结构(ipc_ids

image-20221204195810257

ipc_ids中有一个结构体ipc_id_ary,在ipc_id_ary中有两个字段,size,p,p字段是一个指向kern_ipc_perm数据结构的指针数组

下表,kern_ipc_perm

kern_ipc_perm中有一个keyIPC关键字),通过key来标识共享内存,我们让两个进程使用相同key(key值由用户提供),就可以看到同一个共享内存,进行通信。那么也可以通过key来判断共享内存是否存在

这个key是由用户提供的,就是

int shmget(key_t key, size_t size, int shmflg);

的第一个参数(key_t key),这个key值我们通常使用ftok()函数生成

key_t ftok(const char *pathname, int proj_id);

pathname:一个文件路径

proj_id:一个8位数字,不能为0

返回值为一个密钥(根据文件的inodeproj_id生成)

关联共享内存

void *shmat(int shmid, const void *shmaddr, int shmflg);

将共享内存段连接到进程地址空间

shmid: 共享内存标识符(表示关联到那个共享内存)

shmaddr:指定连接的地址(表示关联到该进程虚拟进程地址空间的哪个位置,一般设为nullptr

shmflg:它的两个可能取值是SHM_RNDSHM_RDONLY(表示对于该共享内存的读写权限,一般设为0,默认读写方式)

返回值:成功返回一个指针,指向共享内存关联到进程地址空间的地址;失败返回-1

去关联共享内存

int shmdt(const void *shmaddr);

将共享内存段与当前进程脱离

shmaddr: 由shmat所返回的指针(共享内存关联到进程地址空间的地址)

返回值:成功返回0;失败返回-1

注意:将共享内存段与当前进程脱离不等于删除共享内存段

控制共享内存

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

用于控制共享内存

shmid:由shmget返回的共享内存标识符

cmd:将要采取的动作(有三个可取值)(IPC_RMID表示立即删除)

buf:指向一个保存着共享内存的模式状态和访问权限的数据结构

返回值:成功返回0;失败返回-1

注意:

当我们运行完毕创建全新的共享内存的代码后(进程退出),再次运行代码,会报错,因为这个共享内存是已存在的。

int main()
{
    key_t key=creat_key();
    cout<<"key:"<<key<<endl;
    //创建共享内存
    int shmid=shmget(key,4096,IPC_CREAT|IPC_EXCL);
    if(shmid<0)
    {
        log()<<"error"<<strerror(errno)<<endl;
        exit(2);
    }
    log()<<"shared memory: sucessed"<<endl;
    return 0;
}

system V下的共享内存,生命周期是随内核的,如果不显示删除,就必须重启os解决.

image-20221208100243302

如何显示删除

  • 命令行

    ipcrm -m shmid
    

    image-20221208100331885

  • 系统接口

    int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    

如何查看IPC资源

ipcs -m

image-20221208100313035

perms:表示该共享内存的读写权限(当前perms为0,那就无法对共享内存进行读写,所以创建共享内存时需要*|读写权限*)

nattch:表示挂接到该共享内存的进程数

使用共享内存

我们把共享内存和进程产生关联,是把共享内存通过页表映射到进程地址空间的栈区和堆区之间的共享区,也就是映射到用户空间,既然是用户空间,那么我们就可以直接进行使用,而不需要调用系统接口。

同时因为共享内存被关联到进程的用户空间,可以被进程直接使用,这使得共享内存是进程间通信最快的方式,同时导致共享内存没有任何访问控制机制,不会发生阻塞等待,共享内存可以直接通信,但不安全

代码

两个进程使用共享内存进行通信

comm.hpp

#pragma once

#include<iostream>
#include<map>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<cstring>
#include<sys/types.h>
#include<unistd.h>
#include<time.h>

#define IPC_PATH "/home/byld/test_linux"

//创建key值,两个进程使用相同的key值访问相同的共享内存
key_t creat_key()
{
    return ftok(IPC_PATH,0X12);
}

std::ostream& log()
{
   std::cout<<"时间戳"<<time(nullptr)<<"|";
   return std::cout;
}

client_IPC.cpp

#include"comm.hpp"

using namespace std;

int main()
{
    key_t key=creat_key();
    //获取共享内存
    int shmid=shmget(key,4096,IPC_CREAT);
    //关联到进程
    char* str=(char*)shmat(shmid,nullptr,0);
    if(str==(char*)-1)
    {
        log()<<"attach failed"<<endl;
        exit(3);
    }
    log()<<"attach successed"<<endl;

    //使用
    //写入
    while(1)
    {
        cout<<"客户端:输入#:"<<endl;
        ssize_t s = read(0,str,4096);
        if(s>0)
        {
            str[s]='\0';
        }
    }


    //去关联
    shmdt(str);
    log()<<"detach sucess"<<endl;

    return 0;
}

server_IPC.cpp

#include"comm.hpp"

using namespace std;

int main()
{
    key_t key=creat_key();
    cout<<"key:"<<key<<endl;
    //创建共享内存
    int shmid=shmget(key,4096,IPC_CREAT|IPC_EXCL|0666);
    if(shmid<0)
    {
        log()<<"error"<<strerror(errno)<<endl;
        exit(2);
    }
    log()<<"shared memory sucessed shmid:"<<shmid<<endl;
    //进程关联共享内存
    char* str=(char*)shmat(shmid,nullptr,0);
    if(str==(char*)-1)
    {
        log()<<"attach failed"<<endl;
        exit(3);
    }
    log()<<"attach successed shmid:"<<shmid<<endl;

    //使用
    //读出
    while(1)
    {
        cout<<"服务器端读出#:";
        cout<<str<<endl;
        sleep(1);
    }
    //进程去关联共享内存
    shmdt(str);
    log()<<"detach sucess"<<endl;
    //删除共享内存
    shmctl(shmid,IPC_RMID,nullptr);
    return 0;
}

动画

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值