Linux下的进程通信之system V共享内存

目录

使用system V共享内存进行进程间通信:

获取共享内存shmget

将共享内存关联到进程

去关联共享内存

删除共享内存

简易模拟实现server和client之间的通信:

服务端代码:

客户端代码:

共享内存的特点: 

其他进程间通信的方式

IPC之间的联系


上篇提到进程间通信的方式管道,system V共享内存也是一种进程间通信的方式。

使用system V共享内存进行进程间通信:

获取共享内存shmget

使用获取共享内存函数第一个参数key是标识共享内存的一个唯一数字,相当于两个进程之间约定的同一个文件进行通信。它标识了两个进程,将使用的同一块共享内存进行通信,即可以通过ftok函数通过传入的参数获取唯一的数值,但是这个函数不保证生成的数值唯一。

ftok函数的第一个参数是路径,第二个参数是一个整数,但是函数内部实现只会使用低8bit位的数生成key,创建失败时返回-1。

第二个参数是size表示要创建共享内存的大小,一般设置为4K的整数倍,也就是4096byte的整数倍,这是因为物理内存是以页为单位的,每页就是4KB的大小,假设传入4097字节,OS会分配8KB,但是用户只能使用4097字节的大小。

第三个参数是创建共享内存的方式,是使用宏作为参数。

可用宏有:

IPC_CREAT表示不存在共享内存就创建之,存在就返回之。

IPC_EXCL,这个宏必须搭配上面的宏一起使用,表示如果存在就错误返回,如果不存在就创建共享内存,这两个宏搭配使用,可以保证创建的共享内存是新的。

函数的返回值是共享内存的id即shmid。

将共享内存关联到进程

        使用共享内存之前,需要用我们的关联共享内存函数去关联我们的共享内存,使用的时候像使用从堆申请的内存一样去使用即可,使用完之后,需要调用系统调用去关联,最后调用系统调用或使用命令行去删除共享内存。本质上是共享内存和进程产生关联,也就是页表建立了进程地址空间共享区和物理内存的映射。

使用shmat函数:

第一个参数shmid是共享内存id,是shmget函数的返回值。

第二个参数shmaddr是指定关联到进程地址空间的某一个地址,常用NULL、nullptr表示先关联到系统可以获得到的地址。

第三个参数shmflg是设置关联共享内存的属性,可传入0表示读写权限,传入SHM_RDONLY表示只读权限。

当关联失败时会返回(void*) -1,比如shmget函数第三个参数没有带入文件权限,默认文件权限就是0,关联共享内存时就会失败。

去关联共享内存

使用shmdt去掉之前的关联

创建的共享内存是在系统内核中维护的,所以它的生命周期是属于内核的,如果不通过命令行或者系统调用的方式显式删除掉共享内存,只就只能通过重启操作系统来删除共享内存的,所以共享内存的生命周期是随内核的。

删除共享内存

可以使用shmctl函数,第一个参数是shmid,传入第二个参数是IPC_RMID即可。

或者在命令行使用ipcs -m查看共享内存,使用ipcsrm -m 共享内存id删除共享内存:

 

简易模拟实现server和client之间的通信:

server服务端进行如下步骤:

    1.创建key,并获取shm,返回shmid

    2.关联共享内存

    3.获取共享内存内容,直接打印

    4.去关联共享内存

    5.删除共享内存

服务端代码:

#include "util.hpp"

int main()
{
    // 1.创建key,并获取shm,返回shmid
    key_t key = CreateKey();
    int shmid = shmget(key, MEM_SIZE, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid < 0)
    {
        cerr << "shmget:" << strerror(errno) << endl;
        return 2;
    }

    // 2.关联共享内存
    char *shm = (char *)shmat(shmid, nullptr, 0);
    cout << "attach success" << endl;
    if (shm == (void *)-1)
    {
        cerr << "shmat error" << endl;
        return 3;
    }

    // 3.使用共享内存
    while (1)
    {
        printf("%s", shm);
        if (strncasecmp(shm, "QUIT", 4) == 0) // 用户控制退出,忽略大小写,需要使用strncasecmp
        {
            cout << "server quit!" << endl;
            break;
        }
        sleep(5);
    }

    // 4.去关联共享内存
    shmdt(shm);
    cout << "detach shm success!" << endl;

    // 5.删除共享内存
    shmctl(shmid, IPC_RMID, nullptr);
    cout << "remove shm success!" << endl;

    return 0;
}

客户端的步骤与服务端基本相同,先创建key值,然后获取共享内存,然后挂接共享内存,最后是使用共享内存,不需要进行删除共享内存,因为服务端会完成此工作。

客户端代码:

#include "util.hpp"

int main()
{
    // 1.创建key,并获取shm,返回shmid
    key_t key = CreateKey();
    int shmid = shmget(key, MEM_SIZE, IPC_CREAT | 0666);
    if (shmid < 0)
    {
        cerr << "shmget:" << strerror(errno) << endl;
        return 2;
    }

    // 2.关联共享内存
    char *shm = (char *)shmat(shmid, nullptr, 0);
    if (shm == (void *)-1)
    {
        cerr << "shmat error" << endl;
        return 3;
    }

    // 3.使用共享内存
    while (1)
    {
        printf("Please Enter#");
        fflush(stdout);
        ssize_t s = read(0, shm, MEM_SIZE); // 从键盘信息写入共享内存
        if (s > 0)
            shm[s - 1] = '\0';
        char *str = shm;
        if (strncasecmp(str, "QUIT", 4) == 0) // 用户控制退出,忽略大小写,需要使用strncasecmp
        {
            cout << "client quit!" << endl;
            break;
        }
    }

    // 4.去关联共享内存
    shmdt(shm);
    cout << "detach shm success!" << endl;

    return 0;
}

公共头文件代码:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <iostream>
#include <unistd.h>
#include <errno.h>
#include<string.h>
using std::cerr;
using std::endl;
using std::cout;
#define IPC_PATH "/home/ys/os-learning-code/oscall/IPC"
#define PROJ_ID 0x66
#define MEM_SIZE 4096
key_t CreateKey()
{
    key_t key = ftok(IPC_PATH, PROJ_ID);
    if (key == -1)
    {
        cerr << "ftok create key error" << endl;
        exit(1);
    }
    return key;
}

测试结果:

共享内存的特点: 

共享内存的特点如下,首先它没有访问控制,所以它是所有进程间通信方式中,通信速度最快的,其次共享内存没有访问控制,共享内存是进程之间访问看到同一份的资源,即临界资源,临界资源没有受到访问控制,即没有互斥和同步机制,会导致访问临界资源出现问题。

其他进程间通信的方式

还有消息队列,信号量等等。

信号量的本质是一个计数器,是一种预定的机制,类似于买电影票,预定的座位一样,只要拿到了票,就相当于获得了座位,就相当于获得了访问临界资源的权利,所以申请信号量的操作就相当于申请临界资源,对计数器的操作必须是原子性的才能保证,信号量和消息队列的生命周期也都是随内核的。

IPC之间的联系

内核在底层实现各种进程间通信的内核结构中,每种内核结构的第一个字段都是ipc_perm。操作系统用指针指向的内核结构,当访问不同类型的IPC资源时进行强制类型转换,即可访问到不同类型的ipc资源,这样子去定义结构,有利于OS以统一的规则管理IPC资源。

 

ipc_perm结构体的字段中包含key等:

 使用ipcs -m/-q/-s/-a命令查看共享内存,消息队列,信号量,或所有IPC资源。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值