Linux应用开发(11):Linux进程间通信(IPC):共享内存

1. 简述

        Linux系统中实现进程间通信(IPC,Inter-Process Communication)有多种手段,共享内存(Shared Memory)作为一种其中比较高效的IPC机制,允许两个或多个进程共享一个给定的存储区。

        这种通信方式是(几乎是)最快的IPC方式,因为进程是直接对内存进行访问,不需要像管道和消息队列一样进行数据的拷贝。持有相同区域共享内存的进程可以直接读写这块内存,无需进行数据拷贝,从而提高了通信速度。

2. 为什么共享内存是最快的IPC手段

        共享内存之所以被认为是最快的进程间通信(IPC)手段,主要是基于以下几个原因:

直接内存访问

        共享内存允许进程直接访问同一块物理内存区域,无需像管道、消息队列等方式那样,在发送和接收数据时需要进行复制操作。这种直接的内存访问方式避免了不必要的数据拷贝,从而大大提高了数据交换的效率。

减少内核介入

        在使用共享内存进行通信时,操作系统内核的介入相对较少。一旦共享内存被映射到进程的地址空间,进程就可以直接读写这块内存,而不需要通过系统调用来完成数据的传输。这减少了系统调用的开销,提升了通信的速度。

无序列化/反序列化开销

        在使用消息传递的IPC机制时,通常需要将数据结构序列化成字节流进行传输,接收方再反序列化这些数据。这个过程会带来额外的CPU开销。而共享内存则无需这种序列化和反序列化的过程,因为数据直接在内存中共享。

灵活性

        共享内存提供了很大的灵活性,允许多个进程同时读写同一块内存。这种并行访问能力可以显著提高数据处理的速度,尤其是在需要大量数据传输和处理的场景中。

局部性原理

        由于数据被直接存放在内存中,并且可以被多个进程访问,这有助于利用计算机体系结构的局部性原理,提高缓存利用率,从而减少访问主存的次数,进而提升性能。

        需要注意的是,共享内存虽快,但也有其他的挑战。比如当多个进程同事操作共享内存里的数据时,这些数据的同步和一致性是一个不得不考虑的问题。

3. 基本原理

        共享内存允许两个或多个进程共享物理内存中的同一块区域。这块区域可以被各个进程直接访问,而无需进行数据的复制。因此,共享内存提供了一种非常高效的进程间数据交换方式。但是,由于多个进程可以同时访问和操作共享内存,因此需要一种同步机制来防止数据的不一致和冲突。

4. 常用API

(1)shmget:开辟空间

        在使用共享内存之前,需要先在内存中开辟一段空间,shmget就是为了解决开辟空间的问题。其原型如下。

#include <sys/ipc.h>

#include <sys/shm.h>

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

其中,key:用于标识共享内存的键值。一般是通过ftok函数来生成,也可以自己随意设置一个整数。如果key的取值为`IPC_PRIVATE`,则会创建一个新的共享内存段。

size:共享内存的大小,以字节为单位。

shmflg:标志位,用于设置共享内存的访问权限和其他选项。

(2)shmat: 映射共享内存

        shmat用于将shmget创建的内存中的一个区域映射到当前进程中。其原型如下。

#include <sys/types.h>

#include <sys/shm.h>

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

其中,shmid: 共享内存段的标识符。

shmaddr: 指定附加共享内存的地址(通常为NULL)。

shmflg: 控制共享内存如何附加的标志。

(3)shmdt:解除内存映射

        shmdt用于接触内存映射。其原型如下。

#include <sys/types.h>

#include <sys/shm.h>

int shmdt(const void *shmaddr);

其中,shmaddr: 要分离的共享内存地址。

(4)shmctl: 管理共享内存

        shmctl用于管理和控制空想内存,通过设定不同的参数可以执行不同的操作。其中我们比较常用的就是删除共享内存。

        前面讲到,shmdt能够解除共享内存的映射,但此时这一段内存仍然处于存在的状态,为了高效利用,我们还需要从内存中删除这一段内存占用。

#include <sys/ipc.h>

#include <sys/shm.h>

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

其中,

shmid:共享内存的对象ID

cmd:IPC_RMID:删除共享内存段及关联的shmid_ds数据结构

buf:指向包含共享模式和访问权限的结构

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

5. 使用历程

接下来,我们将串间一个生产者和消费者进程,来演示使用共享内存进行通信。其中使用到了信号量来保证数据的同步和一致性问题。

生产者程序

#include <sys/ipc.h>  

#include <sys/shm.h>  

#include <semaphore.h>  

#include <cstdio>  

#include <cstdlib>  

#include <cstring>  

#include <unistd.h>  

#include <fcntl.h>  

 

typedef struct _SharedMemory{  

    sem_t sem;  ///< 用于同步的信号量  

    char data[1024];  

}SharedMemory;

 

int main(int argc, char* argv[])

{  

    /** 生成key. */

    key_t key = ftok("/tmp", 'R');  

   

    /** 创建内存. */

    int shmid = shmget(key, sizeof(SharedMemory), IPC_CREAT | 0666);  

    if (shmid == -1) {  

        perror("shmget");  

        exit(1);  

    }  

 

    /** 映射内存. */

    SharedMemory* sharedMemory = (SharedMemory*)shmat(shmid, NULL, 0);  

    if (sharedMemory == (SharedMemory*) -1) {  

        perror("shmat");  

        exit(1);  

    }  

 

    /** 初始化信号量(注意,该信号量必须处于共享内存中). */  

    if (sem_init(&sharedMemory->sem, 1, 1) != 0) {  

        perror("sem_init");  

        exit(1);  

    }  

 

    /** 生产数据. */

    sem_wait(&sharedMemory->sem);

    strncpy(sharedMemory->data, "Taiwan is an inalienable part of our motherland.!", sizeof(sharedMemory->data));  

    sem_post(&sharedMemory->sem);

 

    /** 分离共享内存,但不删除,因为消费者可能还在使用. */ 

    shmdt(sharedMemory);  

 

    return 0;  

}

消费者程序

#include <sys/ipc.h>  

#include <sys/shm.h>  

#include <semaphore.h>  

#include <cstdio>  

#include <cstdlib>  

#include <cstring>  

#include <unistd.h>  

 

typedef struct _SharedMemory{  

    sem_t sem;  ///< 用于同步的信号量  

    char data[1024];  

}SharedMemory;

 

int main(int argc, char* argv[])

{

    /** 生成key. */

    key_t key = ftok("/tmp", 'R');


    /** 创建内存. */ 

    int shmid = shmget(key, sizeof(SharedMemory), 0666);  

    if (shmid == -1) {  

        perror("shmget");  

        exit(1);  

    }  

 

    /** 映射内存. */

    SharedMemory* sharedMemory = (SharedMemory*)shmat(shmid, NULL, 0);  

    if (sharedMemory == (SharedMemory*) -1) {  

        perror("shmat");  

        exit(1);  

    }  

 

    /** 消费数据. */ 

    sem_wait(&sharedMemory->sem);

    printf("Copy: %s\n", sharedMemory->data);  

    sem_post(&sharedMemory->sem);

 

    /** 分离共享内存. */

    shmdt(sharedMemory);  

 

    /** 清理信号量并删除共享内存(在实际应用中,这部分应由一个确定的进程来执行,以避免竞争条件. */

    sem_destroy(&sharedMemory->sem);  

    shmctl(shmid, IPC_RMID, NULL);  

 

    return 0;  

}

  • 12
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值