• System V共享内存介绍


    (一)简单概念

          共享内存作为一种进程间通信的方式,其相较于其他进程间通信方式而言最大的优点就是数据传输速率快。其内部实现的方式采用了Linux进程地址空间中的mmap文件映射区,将文件内容直接映射到各自进程的进程地址空间中,进程对各自进程地址空间的访问即可

    完成数据通信,由于直接读取内存的方式,故其效率远远快于其他IPC方法,这是共享内存的一大优势。但对于共享内存来说,保证数据同步问题是一个难点,在一般情况下可以采用管道的方式,本节内容的实例代码采用了互斥锁机制。

    (二)System V共享内存API函数

    (1)int shmget(key_t key, ssize_t size, int oflag)

    功能:用于创建共享内存区域。

    参数:

    key:共享内存的名字,用于唯一确定一个共享内存;

    size:创建的共享内存的大小;

    oflag:共享内存的读写权限值集合,与文件创建中的mode标志是一样的。同样还可以与IPC_CREAT,IPC_EXCL等标志联合使用。

    返回:函数返回共享内存区的标识符。

    (2)void *shmat(int shmid, const void *shmaddr, int flag)

    功能:将一个创建好的共享内存区附接到进程对应的进程地址空间中。

    参数:

    shmid:shmget函数的返回值;

    shmaddr:将共享内存附接道shmaddr指定的进程地址空间的对应地址上。如果设置为NULL,将由系统指定合法的区域将共享内存映射,这是推荐的做法;

    flag:两个可能取值为SHM_RND和SHM_RDONLY。

    返回:函数返回映射区的起始地址。

    (3)int shmdt(const void *shmaddr)

    功能:将共享内存从该进程地址空间中脱离。

    参数:

    shmaddr:shmat函数的返回值。

    返回:如果成功,函数返回0,出错返回-1。

    注意:该函数只是将共享内存从进程的地址空间中脱离,不会删除该共享内存区域。并且当一个进程终止时,它当前附接的所有共享内存区都将会自动脱离。

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

    功能:提供对共享内存区的多种操作

    参数:

    shmid:shmget函数的返回值;

    cmd:具体操作。取值有1)IPC_RMID:从系统中删除shmid标识的共享内存区并拆除;2)IPC_SET:在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值;3)IPC_START:向调用者返回指定的共享内存区当前的shmid_ds结构。

    返回:成功返回0,出错返回-1。

    (三)采用互斥锁与共享内存的进程间通信方式

          由于共享内存需要解决的一个问题就是数据同步,进程间的同步方式采用信号量可以完成,但我考虑能否采用互斥锁的方式。这里采用互斥锁最初的疑惑为:两个没有近亲关系的进程,如何能够获取到同一个互斥锁(一般互斥锁用于线程间同步的时候比较多,这里也说回来,线程之间本来就是共享数据,所以只需要解决数据同步问题即可,故还没有听说过用共享内存来解决线程通信的 - -。)。

          这里来看下,如果设置互斥锁pthread_mutex_t,来使得不同进程可以获取到同一个互斥锁。

          对于一个互斥锁pthread_mutex_t来说,有两种方式进行初始化,一种是静态分配,一种是动态初始化。

          1)利用宏PTHREAD_MUTEX_INITALIZER来初始化静态分配的互斥锁

          pthread_mutex_t mutex = PTHREAD_MUTEX_INITALIZER

          2)利用pthread_mutex_init来动态初始化互斥锁

          函数原型:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)

         这里第一个参数mutex很好理解,就是定义的需要初始化的互斥锁。这里的第二个参数mutexattr代表什么?Linux下互斥锁具有一系列的属性,其中pthread_mutexattr_t结构体定义了一套完整的互斥锁属性。两种常用的属性是pshared和type。其中pshared属性指定了是否允许跨进程共享互斥锁,可选值为:1)PTHREAD_PROCESS_SHARED,互斥锁可以被跨进程共享;2)PTHREAD_PROCESS_PRIVATE,互斥锁只能隶属于一个进程,默认属性。

         我们可以通过设置互斥锁自身属性,来达到跨进程共享互斥锁的方法。

         结合实例代码来进行分析:

    //shmat.h
    
    #ifndef __SHMAT_H__
    #define __SHMAT_H__
    
    #include <pthread.h>
    #include <string.h>
    #include <sys/shm.h>
    #include <stdlib.h>
    
    #define SM_BUF_SIZE 1024
    #define SM_ID 0x1234
    
    struct shmat_msg
    {
        int flag;
        pthread_mutex_t shmat_mutex;
        char buf[SM_BUF_SIZE];
    };
    
    #endif

    头文件声明了共享内存结构体,其中shmat_mutex用于保证对共享内存的互斥访问。

    //shmat-read.c
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/mman.h>
    #include <fcntl.h>
    
    #include "shmat.h"
    
    int main(void)
    {
        int running = 1;
        int shm_id, ret;
        void *shared_memory = NULL;
        struct shmat_msg *sm_msg = NULL;
    
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);   //设置跨进程属性
    
        shm_id = shmget((key_t)SM_ID, sizeof(struct shmat_msg), 0666 | IPC_CREAT);   //创建共享内存,返回共享内存标识符
        if (shm_id < 0)
        {
            perror("fail to shmget
    ");
            exit(1);
        }
    
        shared_memory = shmat(shm_id, NULL, 0);   //映射到进程虚拟地址空间
        if (shared_memory == NULL)
        {
            perror("fail to shmat
    ");
            exit(1);
        }
        sm_msg = (struct shmat_msg*)shared_memory;   //转型为shmat_msg数据结构
        sm_msg->flag = 0;
        pthread_mutex_init(&sm_msg->shmat_mutex, &attr);   //初始化共享内存的互斥锁
    
        while (running)
        {
            pthread_mutex_lock(&sm_msg->shmat_mutex);   //互斥访问
            if (sm_msg->flag)
            {
                printf("read message : %s
    ", sm_msg->buf);
                sm_msg->flag = 0;
                if (strncmp(sm_msg->buf, "exit", 4) == 0)
                    running = 0;
                pthread_mutex_unlock(&sm_msg->shmat_mutex);
            }
            else
            {
                printf("no data to read, waiting
    ");
                pthread_mutex_unlock(&sm_msg->shmat_mutex);
                sleep(2);
            }
        }
    
        ret = shmdt(shared_memory);   //脱离共享内存的映射
        if (ret < 0)
        {
            perror("failed to shmdt
    ");
            exit(1);
        }
    
        if (shmctl(shm_id, IPC_RMID, 0) < 0)   //删除共享内存
        {
            perror("failed to shmctl
    ");
            exit(1);
        }
    
        return 0;
    }

    读文件。设置了共享内存互斥锁为跨进程属性。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/mman.h>
    #include <fcntl.h>
    
    #include "shmat.h"
    
    int main(void)
    {
        int running = 1;
        int shm_id, ret;
        void *shared_memory = NULL;
        struct shmat_msg *sm_msg = NULL;
    
        shm_id = shmget((key_t)SM_ID, sizeof(struct shmat_msg), 0666);   //获取共享内存
        if (shm_id < 0)
        {
            perror("failed to shmget
    ");
            exit(1);
        }
    
        shared_memory = shmat(shm_id, NULL, 0);   //映射
        if (shared_memory == NULL)
        {
            perror("failed to shmat
    ");
            exit(1);
        }
    
        sm_msg = (struct shmat_msg*)shared_memory;
        char buff[100];
    
        while (running)
        {
            pthread_mutex_lock(&sm_msg->shmat_mutex);   //获取共享内存的互斥锁
            if (sm_msg->flag == 0)
            {
                fgets(buff, 100, stdin);
                printf("write sm_msg : %s
    ", buff);
                strncpy(sm_msg->buf, buff, sizeof(buff));
                sm_msg->flag = 1;
                if (strncmp(sm_msg->buf, "exit", 4) == 0)
                    running = 0;
                pthread_mutex_unlock(&sm_msg->shmat_mutex);
            }
            else
            {
                printf("sm_msg waiting read
    ");
                pthread_mutex_unlock(&sm_msg->shmat_mutex);
                sleep(2);
            }
        }
    
        ret = shmdt(shared_memory);
        if (ret < 0)
        {
            perror("failed to shmdt
    ");
            exit(1);
        }
    
        ret = shmctl(shm_id, IPC_RMID, 0);
        if (ret < 0)
        {
            perror("failed to shmctl
    ");
            exit(1);
        }
    
        return 0;
    }

    写文件。

    程序运行结果:

    其中shmat-write程序运行后会阻塞在fgets等待用户输入。其中shmat-read与shmat-write两个程序抢占lock的时机是不确定的,可能同一个程序会多次抢占互斥锁,但由于没有内容可读或可写,则睡眠等待。

    unlock解锁后,操作系统下次调度哪个进程加锁是不一定的,如果需要在满足一定条件后才被调度可以采用条件变量(但一般都是在多线程环境下了)。

  • 相关阅读:
    .NET CORE 对接天翼云 OOS
    aspnetcore源码学习(一)
    identityserver4的使用和实践(一)
    不要那么快原谅自己(转)
    filebeat+elk 在docker下的简单部署和使用
    docker 环境下通过ocelot和consul 实现服务发现与自治
    .NET使用Office Open XML导出大量数据到 Excel
    SQL Queries from Transactional Plugin Pipeline
    DevExpress 控件设置必填及输入限制
    DevExpress 常用控件使用整理
  • 原文地址:https://www.cnblogs.com/scu-cjx/p/8589640.html
Copyright © 2020-2023  润新知