zoukankan      html  css  js  c++  java
  • 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解锁后,操作系统下次调度哪个进程加锁是不一定的,如果需要在满足一定条件后才被调度可以采用条件变量(但一般都是在多线程环境下了)。

  • 相关阅读:
    windwos8.1英文版安装SQL2008 R2中断停止的解决方案
    indwows8.1 英文版64位安装数据库时出现The ENU localization is not supported by this SQL Server media
    Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds
    SQL数据附加问题
    eclipse,myeclipse中集合svn的方法
    JAVA SSH 框架介绍
    SSH框架-相关知识点
    SuperMapRealSpace Heading Tilt Roll的理解
    SuperMap iserver manage不能访问本地目的(IE9)
    Myeclipse中js文件中的乱码处理
  • 原文地址:https://www.cnblogs.com/scu-cjx/p/8589640.html
Copyright © 2011-2022 走看看