Linux进程间通信之共享内存篇

共享内存

  共享内存是Lunix系统中最底层的通信机制,也是最快的通信机制。共享内存通过两个或多个进程共享同一块内存区域来实现进程间的通信,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。通常是由一个进程创建一块共享内存区域,然后多个进程可以对其进行访问,一个进程将要传出的数据存放到共享内存中,另一个或多个进程则直接从共享内存中读取数据。因此这种通信方式是最高效的进程间通信方式。但实际的问题在于,当两个或多个进程使用共享内存进行通信时,同步问题的解决显得尤为重要,否则就会造成因不同进程同时读写一块共享内存中的数据而发生混乱。在通常的情况下,通过使用信号量来实现进程的同步。

  • 共享内存是由内核出于在多个进程间交换信息的目的而留出的一块内存区(段)。
  • 如果段的权限设置恰当,每个要访问该段内存的进程都可以把它映射到自己的私有地址空间中。
  • 如果一个进程更新了段中的数据,其他进程也立即会看到更新。
  • 由一个进程创建的段,也可以由另一个进程读写。

共享内存函数

1、shmget函数

功能:⽤来创建共享内存
原型:int shmget(key_t key, size_t size, int shmflg);
参数:
  key:这个共享内存段名字
  size:共享内存⼤⼩
  shmflg:由九个权限标志构成,它们的⽤法和创建⽂件时使⽤的mode模式标志是⼀样的
返回值:成功返回⼀个⾮负整数,即该共享内存段的标识码;失败返回-1
注意:只有创建权限是0666的才可以用命令行”ipcs -m”查看,其他类型权限的共享内存区无法被这个命令所查看。

2、shmctl函数

功能:⽤于控制共享内存
原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
  shmid:由shmget返回的共享内存标识码
  cmd:将要采取的动作(有三个可取值)
   IPC_ STAT:把shmid_ ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值
   IPC_ SET:在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值
   IPC_RMID:删除共享内存段
  buf:指向⼀个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

3、shmat函数

功能:将共享内存段连接到进程地址空间
原型:void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
  shmid:共享内存标识
  shmaddr:指定连接的地址
  shmflg:它的两个可能取值是SHM_ RND和SHM_RDONLY
返回值:成功返回⼀个指针,指向共享内存第⼀个节;失败返回-1

  • 说明
    shmaddr若为NULL,核⼼⾃动选择⼀个地址
    shmaddr不为NULL且shmflg⽆SHM_RND标记,则以shmaddr为连接地址
    shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会⾃动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
    shmflg=SHM_RDONLY,表⽰连接操作⽤来只读共享内存

4、shmdt函数

功能:将共享内存段与当前进程脱离
原型:int shmdt(const void *shmaddr);
参数:
  shmaddr:由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

共享内存特点

  1. 可以应用于任何进程
  2. 既能读又能写,双向通信
  3. 可按照随机访问来操作数据
  4. 没有同步和互斥机制
  5. 生命周期随内核

共享内存实现进程间通信

  以下两个程序是一个进程间通信的例子。这两个程序分别在不同的进程中运行,使用了共享内存进行通信。client读入数据,存放在共享内存中。server则从共享内存中读取数据,显示到屏幕上。

common.c

#include "common.h"

#include <stdio.h>

int ShmCommon(int size, int flags){
    key_t key = ftok(PATHNAME, PROJ_ID);
    if(key < 0){
        perror("ftok error");
        return -1;
    }
    int shmid = shmget(key, size, flags);
    if(shmid < 0){
        perror("shmget error");
        return 1;
    }
    return shmid;
}

int ShmCreate(int size){
    return ShmCommon(size, IPC_CREAT | IPC_EXCL | 0666);
}

int ShmOpen(){
    return ShmCommon(0, IPC_CREAT);
}

int ShmDestroy(int shmid){
    int ret = shmctl(shmid, IPC_RMID, NULL);
    if(ret < 0){
        perror("shmctl error");
        return -1;
    }
    return 0;
}

////////////////////////////////////////////////////////
//以下是测试代码
///////////////////////////////////////////////////////
#if 0

void TestCreate(){
    int shmid = ShmCreate(1024);
    printf("shmid = %d\n", shmid);
}

void TestOpen(){
    int shmid = ShmOpen();
    printf("shmid = %d\n", shmid);
}

void TestDestroy(){
    int shmid = ShmOpen();
    int ret = ShmDestroy(shmid);
    printf("ret expect 0, actual %d\n", ret);
}

int main(){
    //TestCreate();
    //TestOpen();
    TestDestroy();
    return 0;
}
#endif

创建之后:
描述

销毁之后:
描述

client.c

#include <stdio.h>
#include <unistd.h>
#include "common.h"

int main(){
    int shmid = ShmOpen();
    if(shmid < 0){
        perror("ShmOpen failed!");
        return 1;
    }
    sleep(1);
    //把物理内存关联(挂接,attach)到某个进程的虚拟地址空间之中
    char* addr = shmat(shmid, NULL, 0);
    sleep(2);
    int i = 0;
    while(i < 26){
        addr[i] = 'A' + i;
        i++;
        addr[i] = 0;
        sleep(1);
    }
    //将共享内存与当前进程脱离
    shmdt(addr);
    sleep(2);
    return 0;
}

server.c

#include <stdio.h>
#include <unistd.h>
#include "common.h"

int main(){
    int shmid = ShmCreate(1024);
    if(shmid < 0){
        perror("ShmCreate failed!");
        return 1;
    }
    char* addr = shmat(shmid, NULL, 0);
    sleep(2);
    int i = 0;
    while(i++ < 26){
        printf("client# %s\n", addr);
        sleep(1);
    }
    shmdt(addr);
    sleep(2);
    ShmDestroy(shmid);
    return 0;
}

描述

详细代码请参考Git

猜你喜欢

转载自blog.csdn.net/adorable_/article/details/80465790
今日推荐