Linux进程间共享内存通信时如何同步?(附源码)

嵌入式ARM 2023-05-10 13:00

今天,我们讲一讲进程间使用共享内存通信时为了确保数据的正确,如何进行同步?

在Linux中,进程间的共享内存通信需要通过同步机制来保证数据的正确性和一致性,常用的同步机制包括信号量互斥锁条件变量等。

其中,使用信号量来同步进程间的共享内存访问是一种常见的方法。每个共享内存区域可以关联一个或多个信号量,以保护共享内存区域的读写操作。在访问共享内存之前,进程需要获取信号量的使用权,当完成读写操作后,再释放信号量的使用权,以便其他进程可以访问共享内存区域。

1、信号量同步

下面是一个简单的示例程序,展示了如何使用信号量来同步共享内存区域的读写操作:

#include 
#include 
#include 
#include 
#include 
#include 

#define SHM_SIZE 1024
#define SEM_KEY 0x123456

// 定义联合体,用于信号量操作
union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};

int main() {
    int shmid, semid;
    char *shmaddr;
    struct sembuf semops[2];
    union semun semarg;

    // 创建共享内存区域
    shmid = shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0666);
    if (shmid == -1) {
        perror("shmget");
        exit(1);
    }

    // 将共享内存区域附加到进程地址空间中
    shmaddr = shmat(shmid, NULL0);
    if (shmaddr == (char *) -1) {
        perror("shmat");
        exit(1);
    }

    // 创建信号量
    semid = semget(SEM_KEY, 1, IPC_CREAT | 0666);
    if (semid == -1) {
        perror("semget");
        exit(1);
    }

    // 初始化信号量值为1
    semarg.val = 1;
    if (semctl(semid, 0, SETVAL, semarg) == -1) {
        perror("semctl");
        exit(1);
    }

    // 等待信号量
    semops[0].sem_num = 0;
    semops[0].sem_op = 0;
    semops[0].sem_flg = 0;
    if (semop(semid, semops, 1) == -1) {
        perror("semop");
        exit(1);
    }

    // 在共享内存中写入数据
    strncpy(shmaddr, "Hello, world!", SHM_SIZE);

    // 释放信号量
    semops[0].sem_num = 0;
    semops[0].sem_op = 1;
    semops[0].sem_flg = 0;
    if (semop(semid, semops, 1) == -1) {
        perror("semop");
        exit(1);
    }

    // 等待信号量
    semops[0].sem_num = 0;
    semops[0].sem_op = 0;
    semops[0].sem_flg = 0;
    if (semop(semid, semops, 1) == -1) {
        perror("semop");
        exit(1);
    }

    // 从共享内存中读取数据
    printf("Received message: %s\n", shmaddr);

    // 释放共享内存区域
    if (shmdt(shmaddr) == -1) {
        perror("shmdt");
        exit(1);
    }

    // 删除共享内存区域
    if (shmctl(shmid, IPC_RMID, NULL) == -1) {
        perror("shmctl");
        exit(1);
    }

    // 删除信号量
    if (semctl(semid, 0, IPC_RMID, semarg) == -1) {
        perror("semctl");
        exit(1);
    }

    return 0;

在这个示例程序中,使用了System V信号量来同步共享内存的读写操作。程序首先创建一个共享内存区域,并将其附加到进程地址空间中。然后,使用semget()函数创建一个信号量,并将其初始化为1。在写入共享内存数据之前,程序使用semop()函数等待信号量。一旦获取了信号量的使用权,程序就可以在共享内存区域中写入数据。写入数据完成后,程序再次使用semop()函数释放信号量的使用权。在读取共享内存数据时,程序同样需要等待信号量的使用权,读取数据完成后,再次释放信号量的使用权。

需要注意的是,使用信号量来同步共享内存访问时,需要确保每个进程都按照一定的顺序进行读写操作。否则,就可能出现死锁等问题。因此,在设计进程间共享内存通信时,需要仔细考虑数据的读写顺序,并采取合适的同步机制来确保数据的正确性和一致性。

2、互斥锁同步

互斥量也是一种常用的同步机制,可以用来实现多个进程之间的共享内存访问。在Linux中,可以使用pthread库中的互斥量来实现进程间共享内存的同步。

下面是一个使用互斥量实现共享内存同步的示例程序:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SHM_SIZE 1024

// 共享内存结构体
typedef struct {
    pthread_mutex_t mutex;
    char data[SHM_SIZE];
shm_data_t;

int main() {
    int fd;
    shm_data_t *shm_data;
    pthread_mutexattr_t mutex_attr;
    pthread_mutex_t *mutex;
    
    // 打开共享内存文件
    if ((fd = shm_open("/my_shm", O_CREAT | O_RDWR, 0666)) == -1) {
        perror("shm_open");
        exit(1);
    }

    // 调整共享内存文件大小
    if (ftruncate(fd, sizeof(shm_data_t)) == -1) {
        perror("ftruncate");
        exit(1);
    }

    // 将共享内存映射到进程地址空间中
    if ((shm_data = mmap(NULLsizeof(shm_data_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
        perror("mmap");
        exit(1);
    }

    // 初始化互斥量属性
    pthread_mutexattr_init(&mutex_attr);
    pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);

    // 创建互斥量
    mutex = &(shm_data->mutex);
    pthread_mutex_init(mutex, &mutex_attr);

    // 在共享内存中写入数据
    pthread_mutex_lock(mutex);
    sprintf(shm_data->data, "Hello, world!");
    pthread_mutex_unlock(mutex);

    // 在共享内存中读取数据
    pthread_mutex_lock(mutex);
    printf("Received message: %s\n", shm_data->data);
    pthread_mutex_unlock(mutex);

    // 解除共享内存映射
    if (munmap(shm_data, sizeof(shm_data_t)) == -1) {
        perror("munmap");
        exit(1);
    }

    // 删除共享内存文件
    if (shm_unlink("/my_shm") == -1) {
        perror("shm_unlink");
        exit(1);
    }

    return 0;
}

在这个示例程序中,使用了pthread库中的互斥量来同步共享内存的读写操作。程序首先创建一个共享内存文件,并将其映射到进程地址空间中。然后,使用pthread_mutex_init()函数创建一个互斥量,并将其初始化为共享内存中的一部分。

在写入共享内存数据之前,程序使用pthread_mutex_lock()函数等待互斥量。一旦获取了互斥量的使用权,程序就可以在共享内存区域中写入数据。写入数据完成后,程序再次使用pthread_mutex_unlock()函数释放互斥量的使用权。

在读取共享内存数据之前,程序再次使用pthread_mutex_lock()函数等待互斥量。一旦获取了互斥量的使用权,程序就可以在共享内存区域中读取数据。读取数据完成后,程序再次使用pthread_mutex_unlock()函数释放互斥量的使用权。最后,程序解除共享内存映射,并删除共享内存文件。

使用互斥量来同步共享内存访问有以下几点注意事项:

1、互斥量需要初始化。在创建互斥量之前,需要使用pthread_mutexattr_init()函数初始化互斥量属性,并使用pthread_mutexattr_setpshared()函数将互斥量属性设置为PTHREAD_PROCESS_SHARED,以便多个进程可以共享互斥量。

2、在访问共享内存之前,需要使用pthread_mutex_lock()函数获取互斥量的使用权。一旦获取了互斥量的使用权,程序才能访问共享内存。在完成共享内存的访问之后,需要使用pthread_mutex_unlock()函数释放互斥量的使用权,以便其他进程可以访问共享内存。

3、互斥量必须存储在共享内存区域中。在创建互斥量时,需要将其初始化为共享内存区域中的一部分,以便多个进程可以访问同一个互斥量。

4、程序必须保证互斥量的一致性。多个进程共享同一个互斥量时,必须保证互斥量的一致性。否则,可能会导致多个进程同时访问共享内存区域,导致数据错误或者系统崩溃。

总之,使用互斥量来同步共享内存访问可以有效地避免多个进程同时访问共享内存区域的问题,从而保证数据的一致性和程序的稳定性。在实际编程中,需要根据具体的需求选择不同的同步机制,以保证程序的正确性和效率。

3、条件变量同步

在Linux下,可以使用条件变量(Condition Variable)来实现多进程之间的同步。条件变量通常与互斥量(Mutex)结合使用,以便在共享内存区域中对数据进行同步访问。

条件变量是一种线程同步机制,用于等待或者通知某个事件的发生。当某个进程需要等待某个事件发生时,它可以通过调用pthread_cond_wait()函数来阻塞自己,并将互斥量释放。一旦事件发生,其他进程就可以通过调用pthread_cond_signal()或pthread_cond_broadcast()函数来通知等待线程。等待线程接收到通知后,会重新获取互斥量,并继续执行。

在共享内存通信中,可以使用条件变量来实现进程之间的同步。具体操作步骤如下:

初始化互斥量和条件变量。在创建共享内存之前,需要使用pthread_mutexattr_init()和pthread_condattr_init()函数分别初始化互斥量属性和条件变量属性。然后,需要使用pthread_mutexattr_setpshared()和pthread_condattr_setpshared()函数将互斥量属性和条件变量属性设置为PTHREAD_PROCESS_SHARED,以便多个进程可以共享它们。

等待条件变量。在读取共享内存之前,程序可以使用pthread_cond_wait()函数等待条件变量。调用pthread_cond_wait()函数会自动释放互斥量,并阻塞当前进程。一旦其他进程发送信号通知条件变量发生变化,等待线程就会重新获得互斥量,并继续执行。

发送信号通知条件变量变化。在向共享内存中写入数据之后,程序可以使用pthread_cond_signal()或pthread_cond_broadcast()函数发送信号通知条件变量发生变化。调用pthread_cond_signal()函数会发送一个信号通知等待线程条件变量发生变化,而调用pthread_cond_broadcast()函数会向所有等待线程发送信号通知条件变量发生变化。

使用条件变量来同步共享内存访问有以下几点注意事项:

1、程序必须使用互斥量来保护共享内存。在使用条件变量之前,程序必须先获取互斥量的使用权,以便保护共享内存区域中的数据不被多个进程同时访问。

2、程序必须保证条件变量的一致性。多个进程共享同一个条件变量时,必须保证条件变量的一致性。否则,可能会导致多个进程同时访问共享内存区域,导致数据错误或者系统崩溃。

3、程序必须正确使用条件变量。在使用条件变量时,需要正确地使用pthread_cond_wait()、pthread_cond_signal()和pthread_cond_broadcast()函数,否则可能会导致死锁或者其他问题。

4、程序必须正确处理信号。当调用pthread_cond_wait()函数时,程序可能会因为接收到信号而提前返回,此时程序需要正确地处理信号。

下面是一个使用条件变量实现进程间共享内存同步的示例代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SHM_SIZE 4096
#define SHM_NAME "/myshm"
#define SEM_NAME "/mysem"

typedef struct {
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    char buffer[SHM_SIZE];
shm_t;

int main(int argc, char *argv[]) {
    int fd, pid;
    shm_t *shm;
    pthread_mutexattr_t mutex_attr;
    pthread_condattr_t cond_attr;

    // 创建共享内存区域
    fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
    if (fd < 0) {
        perror("shm_open");
        exit(1);
    }

    // 设置共享内存大小
    if (ftruncate(fd, sizeof(shm_t)) < 0) {
        perror("ftruncate");
        exit(1);
    }

    // 将共享内存映射到进程地址空间
    shm = mmap(NULLsizeof(shm_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (shm == MAP_FAILED) {
        perror("mmap");
        exit(1);
    }

    // 初始化互斥量属性和条件变量属性
    pthread_mutexattr_init(&mutex_attr);
    pthread_condattr_init(&cond_attr);
    pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
    pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED);

    // 初始化互斥量和条件变量
    pthread_mutex_init(&shm->mutex, &mutex_attr);
    pthread_cond_init(&shm->cond, &cond_attr);

    // 创建子进程
    pid = fork();
    if (pid < 0) {
        perror("fork");
        exit(1);
    }

    if (pid == 0) {
        // 子进程写入共享内存
        sleep(1);
        pthread_mutex_lock(&shm->mutex);
        sprintf(shm->buffer, "Hello, world!");
        pthread_cond_signal(&shm->cond);
        pthread_mutex_unlock(&shm->mutex);
        exit(0);
    } else {
        // 父进程读取共享内存
        pthread_mutex_lock(&shm->mutex);
        pthread_cond_wait(&shm->cond, &shm->mutex);
        printf("Received message: %s\n", shm->buffer);
        pthread_mutex_unlock(&shm->mutex);
    }

    // 删除共享内存
    if (shm_unlink(SHM_NAME) < 0) {
        perror("shm_unlink");
        exit(1);
    }

    return 0;
}

在这个示例中,程序创建了一个名为"/myshm"的共享内存区域,并将其映射到进程地址空间中。然后,程序使用互斥量和条件变量来同步进程之间的访问共享内存区域。具体来说,父进程首先锁定互斥量,然后等待条件变量的信号。子进程等待一秒钟后,锁定互斥量,将"Hello, world!"字符串写入共享内存区域,然后发送条件变量信号,并释放互斥量。此时,父进程将收到条件变量信号并锁定互斥量,读取共享内存区域中的内容,并释放互斥量。

需要注意的是,在使用条件变量时,我们需要遵循一些规则来保证程序的正确性,如在等待条件变量时必须锁定互斥量,并使用while循环来检查条件变量的值是否满足要求,等待条件变量信号的线程必须在等待之前锁定互斥量,在等待之后解锁互斥量,等待条件变量信号的线程可能会因为接收到信号而提前返回等等。

总之,使用互斥量和条件变量来实现进程间共享内存通信的同步,需要我们仔细考虑程序中所有可能出现的情况,并正确地使用互斥量和条件变量函数来同步进程之间的访问。

小结

好了,这次我们通过Linux下进程间共享内存通信方式讲解了常用的同步机制:信号量、互斥锁、条件变量。希望对小伙伴们在日常的编程当中有所帮助。


END

来源:Linux兵工厂

版权归原作者所有,如有侵权,请联系删除。

推荐阅读
一款专业且全面的嵌入式开发调试工具
状态机——单片机的万能语言(附代码)
C语言指针p、*p、&p、*&p、&*p分别代表什么?

→点关注,不迷路←

嵌入式ARM 关注这个时代最火的嵌入式ARM,你想知道的都在这里。
评论 (0)
  •   卫星图像智能测绘系统全面解析   一、系统概述   卫星图像智能测绘系统是基于卫星遥感技术、图像处理算法与人工智能(AI)技术的综合应用平台,旨在实现高精度、高效率的地理空间数据获取、处理与分析。该系统通过融合多源卫星数据(如光学、雷达、高光谱等),结合AI驱动的智能算法,实现自动化、智能化的测绘流程,广泛应用于城市规划、自然资源调查、灾害监测等领域。   应用案例   目前,已有多个卫星图像智能测绘系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润卫星图像智能测绘系统
    华盛恒辉l58ll334744 2025-04-08 15:04 103浏览
  • ## DL/T645-2007* 帧格式:* 帧起始字符:68H* 地址域:A0 A1 A2 A3 A4 A5* 帧起始字符:68H* 控制码:1字节* 主站:* 13H:请求读电能表通信地址* 11H:请求读电能表数据* 1CH:请求跳闸、合闸* 从站:* 91H:正常应答读电能表* 9CH:正常应答跳闸、合闸* 数据域长度:1字节* 数据域:DI0 DI1 DI2 DI3* 发送方:每字节+33H* 接收方:每字节-33H* 数据标识:* 电能量* 最大需量及发生时间* 变量* 事件记录*
    四毛打印店 2025-04-09 10:53 68浏览
  • HDMI从2.1版本开始采用FRL传输模式,和2.0及之前的版本不同。两者在物理层信号上有所区别,这就需要在一些2.1版本的电路设计上增加匹配电路,使得2.1版本的电路能够向下兼容2.0及之前版本。2.1版本的信号特性下面截取自2.1版本规范定义,可以看到2.1版本支持直流耦合和交流耦合,其共模电压和AVCC相关,信号摆幅在400mV-1200mV2.0及之前版本的信号特性HDMI2.0及之前版本采用TMDS信号物理层,其结构和参数如下:兼容设计根据以上规范定义,可以看出TMDS信号的共模电压范
    durid 2025-04-08 19:01 175浏览
  •   物质扩散与污染物监测系统:环境守护的关键拼图   一、物质扩散原理剖析   物质扩散,本质上是物质在浓度梯度、温度梯度或者压力梯度等驱动力的作用下,从高浓度区域向低浓度区域迁移的过程。在环境科学范畴,物质扩散作为污染物在大气、水体以及土壤中迁移的关键机制,对污染物的分布态势、浓度动态变化以及环境风险程度有着直接且重大的影响。   应用案例   目前,已有多个物质扩散与污染物监测系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润物质扩散与污染物监测系统。这些成功案例为物质
    华盛恒辉l58ll334744 2025-04-09 11:24 64浏览
  • 在人工智能技术飞速发展的今天,语音交互正以颠覆性的方式重塑我们的生活体验。WTK6900系列语音识别芯片凭借其离线高性能、抗噪远场识别、毫秒级响应的核心优势,为智能家居领域注入全新活力。以智能风扇为起点,我们开启一场“解放双手”的科技革命,让每一缕凉风都随“声”而至。一、核心技术:精准识别,无惧环境挑战自适应降噪,听懂你的每一句话WTK6900系列芯片搭载前沿信号处理技术,通过自适应降噪算法,可智能过滤环境噪声干扰。无论是家中电视声、户外虫鸣声,还是厨房烹饪的嘈杂声,芯片均能精准提取有效指令,识
    广州唯创电子 2025-04-08 08:40 194浏览
  • 文/郭楚妤编辑/cc孙聪颖‍伴随贸易全球化的持续深入,跨境电商迎来蓬勃发展期,物流行业 “出海” 成为不可阻挡的必然趋势。加之国内快递市场渐趋饱和,存量竞争愈发激烈。在此背景下,国内头部快递企业为突破发展瓶颈,寻求新的增长曲线,纷纷将战略目光投向海外市场。2024 年,堪称中国物流企业出海进程中的关键节点,众多企业纷纷扬帆起航,开启海外拓展之旅。然而,在一片向好的行业发展表象下,部分跨境物流企业的经营状况却不容乐观。它们受困于激烈的市场竞争、不断攀升的运营成本,以及复杂的国际物流环境,陷入了微利
    华尔街科技眼 2025-04-09 15:15 96浏览
  •   卫星图像智能测绘系统:地理空间数据处理的创新引擎   卫星图像智能测绘系统作为融合卫星遥感、地理信息系统(GIS)、人工智能(AI)以及大数据分析等前沿技术的综合性平台,致力于达成高精度、高效率的地理空间数据采集、处理与应用目标。借助自动化、智能化的技术路径,该系统为国土资源管理、城市规划、灾害监测、环境保护等诸多领域输送关键数据支撑。   应用案例   目前,已有多个卫星图像智能测绘系统在实际应用中取得了显著成效。例如,北京华盛恒辉北京五木恒润卫星图像智能测绘系统。这些成功案例为卫星
    华盛恒辉l58ll334744 2025-04-08 16:19 88浏览
  •   物质扩散与污染物监测系统软件:多领域环境守护的智能中枢   北京华盛恒辉物质扩散与污染物监测系统软件,作为一款融合了物质扩散模拟、污染物监测、数据分析以及可视化等多元功能的综合性工具,致力于为环境科学、公共安全、工业生产等诸多领域给予强有力的技术支撑。接下来,将从功能特性、应用场景、技术实现途径、未来发展趋势等多个维度对这类软件展开详尽介绍。   应用案例   目前,已有多个物质扩散与污染物监测系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润物质扩散与污染物监测系统。这
    华盛恒辉l58ll334744 2025-04-09 14:54 114浏览
  • 在万物互联时代,智能化安防需求持续升级,传统报警系统已难以满足实时性、可靠性与安全性并重的要求。WT2003H-16S低功耗语音芯片方案,以4G实时音频传输、超低功耗设计、端云加密交互为核心,重新定义智能报警设备的性能边界,为家庭、工业、公共安防等领域提供高效、稳定的安全守护。一、技术内核:五大核心突破,构建全场景安防基座1. 双模音频传输,灵活应对复杂场景实时音频流传输:内置高灵敏度MIC,支持环境音实时采集,通过4G模块直接上传至云端服务器,响应速度低至毫秒级,适用于火灾警报、紧急呼救等需即
    广州唯创电子 2025-04-08 08:59 158浏览
  • 文/Leon编辑/侯煜‍就在小米SU7因高速交通事故、智驾性能受到质疑的时候,另一家中国领先的智驾解决方案供应商华为,低调地进行了一场重大人事变动。(详情见:雷军熬过黑夜,寄望小米SU7成为及时雨)4月4日上午,有网友发现余承东的职务发生了变化,华为官网、其个人微博认证信息为“常务董事,终端BG董事长”,不再包括“智能汽车解决方案BU董事长”。余承东的确不再兼任华为车BU董事长,但并非完全脱离华为的汽车业务,而是聚焦鸿蒙智行。据悉,华为方面寻求将车BU独立出去,但鸿蒙智行仍留在华为终端BG部门。
    华尔街科技眼 2025-04-09 15:28 95浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦