首页 > 系统相关 >3个进程实现共享内存、信号量、互斥访问

3个进程实现共享内存、信号量、互斥访问

时间:2024-05-28 21:35:08浏览次数:24  
标签:semid int errno 信号量 互斥 共享内存 data

V1.0 2024年5月28日 发布于博客园

题目

设计一个程序,作为进程A,进程A专门创建一个信号量集,要求信号量集中有1个信号量,对信号量集合中的信号量进行设置,要求集合中的信号量的初值为1,然后再设计2个程序,分别是进程B和进程C,要求进程B和进程C使用进程A创建的信号量集合中的信号量实现互斥访问。 提示:进程A、进程B、进程C需要使用共享内存作为临界资源的访问。

image

解答

我们先分别创建3个程序(对应3个进程)

processA.c

/**
 * @file name : processA.c
 * @brief     : 创建一个信号量集, 并初始化信号量的值为1, 同时创建一个共享内存段, 用于储存int data=0.
 * @author    :[email protected]
 * @date      :2024/05/28
 * @version   :1.0
 * @note      :
 * CopyRight (c)  2023-2024   [email protected]   All Right Reseverd
 */
#include <sys/types.h> //semget()
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <stdio.h>  //fprintf()
#include <string.h> //strerror()
#include <errno.h>  //errno
#include <stdlib.h> //exit()
int main(int argc, char const *argv[])
{
    int semid; // sem信号量id
    int shmid; // shm共享内存id
    int *data; // 共享内存段中的 data变量地址

    /**
     * 创建信号量集, 包含1个信号量(值还未设置)
     *
     * ftok(".", 10)获取一个当前未用的IPC的key, 10为1~255的任意值
     *
     * 信号集中有1个信号量, key对应信号量不存在则创建, 存在则报错, 权限666
     */
    semid = semget(ftok(".", 10), 1, IPC_CREAT | IPC_EXCL | 0666);
    if (semid == -1) // 错误处理
    {
        fprintf(stderr, "semget error, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }

    /**
     * 初始化信号量的值为1
     * semctl(信号量集的标识符, 信号集中信号量的索引(编号)从0开始, 操作命令要设置信号量元素的值, 将值设置为1)
     *
     * 设置信号量的初始值为1表示资源是可用的。信号量的值通常表示可用资源的数量:
     * 1表示资源是可用的,任何一个进程可以进入临界区。
     * 0表示资源不可用,任何试图进入临界区的进程都会阻塞。
     */
    if (semctl(semid, 0, SETVAL, 1) == -1)
    {
        fprintf(stderr, "semctl error, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }

    /**
     * 创建(申请)共享内存段
     *
     * shmget(共享内存的键值, 共享内存大小, 不存在则创建 存在则报错 权限666)
     *
     */
    shmid = shmget(ftok(".", 11), sizeof(int), IPC_CREAT | IPC_EXCL | 0666);
    if (shmid == -1) // 错误处理
    {
        fprintf(stderr, "shmid error, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }

    /**
     * 映射共享内存段附加到进程的地址空间
     *
     * (共享内存首地址)shmat(共享内存ID, NULL 自动选择合适的虚拟内存, 0 可读可写)
     *
     */
    data = (int *)shmat(shmid, NULL, 0);
    if (data == (int *)-1) // 错误处理
    {
        fprintf(stderr, "shmat error, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }

    // 初始化共享内存中的data变量
    *data = 0;

    printf("processA: 初始化信号量和共享内存成功.\n");

    /**
     * 分离共享内存段
     *
     * shmdt解除映射,因为processA不使用这个共享内存空间, 但解除映射不是删除共享内存空间, 空间还在, 通过keyID访问
     */
    if (shmdt(data) == -1)
    {
        fprintf(stderr, "shmdt error, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }
    return 0;
}

processB.c

/**
 * @file name : processB.c
 * @brief     : 获取信号量集和共享内存段,然后使用信号量实现对共享内存段的互斥访问,增加共享内存中的整数变量。
 * @author    :[email protected]
 * @date      :2024/05/28
 * @version   :1.0
 * @note      :
 * CopyRight (c)  2023-2024   [email protected]   All Right Reseverd
 */
#include <sys/types.h> //semget()
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <stdio.h>  //fprintf()
#include <string.h> //strerror()
#include <errno.h>  //errno
#include <stdlib.h> //exit()

// 对资源进行申请, P操作
void sem_p(int semid)
{
    /**
     * struct sembuf 信号量操作结构体{信号量元素序号(数组下标),-1表示P操作 一次减去1,0 可读可写}
     *
     * semop(semid, 信号量操作结构体地址, 结构体数组元素个数)
     */
    struct sembuf sb = {0, -1, 0};
    if (semop(semid, &sb, 1) == -1)
    {
        fprintf(stderr, "semop P error, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }
}

// 对资源进行释放, V操作
void sem_v(int semid)
{
    /**
     * struct sembuf 信号量操作结构体{信号量元素序号(数组下标),1表示v操作 一次增加1,0 可读可写}
     *
     * semop(semid, 信号量操作结构体地址, 结构体数组元素个数)
     */
    struct sembuf sb = {0, 1, 0};
    if (semop(semid, &sb, 1) == -1)
    {
        fprintf(stderr, "semop V error, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }
}

int main(int argc, char const *argv[])
{
    int semid; // sem信号量id
    int shmid; // shm共享内存id
    int *data; // 共享内存段中的 data变量地址

    /**
     * 获取信号量集(在别的程序已经创建), 包含1个信号量
     *
     * ftok(".", 10)获取已经创建了的IPC的key
     *
     * 信号集中有1个信号量, 权限666
     */
    semid = semget(ftok(".", 10), 1, 0666);
    if (semid == -1) // 错误处理
    {
        fprintf(stderr, "semget error, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }

    /**
     * 获取(申请)共享内存段
     *
     * shmget(已经存在的共享内存的键值, 共享内存大小, 不存在则创建 存在则报错 权限666)
     *
     */
    shmid = shmget(ftok(".", 11), sizeof(int), 0666);
    if (shmid == -1) // 错误处理
    {
        fprintf(stderr, "shmid error, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }

    /**
     * 映射共享内存段附加到进程的地址空间
     *
     * (共享内存首地址)shmat(共享内存ID, NULL 自动选择合适的虚拟内存, 0 可读可写)
     *
     */
    data = (int *)shmat(shmid, NULL, 0);
    if (data == (int *)-1) // 错误处理
    {
        fprintf(stderr, "shmat error, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }

    // 进入临界区 p操作 开始执行代码段
    sem_p(semid);
    (*data)++;
    printf("Process B: 改变 data 为 %d\n", *data);
    // 离开临界区 v操作 代码段执行完毕后
    sem_v(semid);

    /**
     * 分离共享内存段
     *
     * shmdt解除映射,因为processB不使用这个共享内存空间, 但解除映射不是删除共享内存空间, 空间还在, 通过keyID访问
     */
    if (shmdt(data) == -1)
    {
        fprintf(stderr, "shmdt error, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }

    return 0;
}

processC.c

/**
 * @file name : processC.c
 * @brief     : 获取信号量集和共享内存段,然后使用信号量实现对共享内存段的互斥访问,读取并输出共享内存中的整数变量。
 * @author    :[email protected]
 * @date      :2024/05/28
 * @version   :1.0
 * @note      : 进程B和进程C使用信号量实现对共享内存的互斥访问,确保在同一时刻只能有一个进程访问共享内存。
 * CopyRight (c)  2023-2024   [email protected]   All Right Reseverd
 */
#include <sys/types.h> //semget()
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <stdio.h>  //fprintf()
#include <string.h> //strerror()
#include <errno.h>  //errno
#include <stdlib.h> //exit()

// 对资源进行申请, P操作
void sem_p(int semid)
{
    /**
     * struct sembuf 信号量操作结构体{信号量元素序号(数组下标),-1表示P操作 一次减去1,0 可读可写}
     *
     * semop(semid, 信号量操作结构体地址, 结构体数组元素个数)
     */
    struct sembuf sb = {0, -1, 0};
    if (semop(semid, &sb, 1) == -1)
    {
        fprintf(stderr, "semop P error, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }
}

// 对资源进行释放, V操作
void sem_v(int semid)
{
    /**
     * struct sembuf 信号量操作结构体{信号量元素序号(数组下标),1表示v操作 一次增加1,0 可读可写}
     *
     * semop(semid, 信号量操作结构体地址, 结构体数组元素个数)
     */
    struct sembuf sb = {0, 1, 0};
    if (semop(semid, &sb, 1) == -1)
    {
        fprintf(stderr, "semop V error, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }
}

int main(int argc, char const *argv[])
{
    int semid; // sem信号量id
    int shmid; // shm共享内存id
    int *data; // 共享内存段中的 data变量地址

    /**
     * 获取信号量集(在别的程序已经创建), 包含1个信号量
     *
     * ftok(".", 10)获取已经创建了的IPC的key
     *
     * 信号集中有1个信号量, 权限666
     */
    semid = semget(ftok(".", 10), 1, 0666);
    if (semid == -1) // 错误处理
    {
        fprintf(stderr, "semget error, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }

    /**
     * 获取(申请)共享内存段
     *
     * shmget(已经存在的共享内存的键值, 共享内存大小, 不存在则创建 存在则报错 权限666)
     *
     */
    shmid = shmget(ftok(".", 11), sizeof(int), 0666);
    if (shmid == -1) // 错误处理
    {
        fprintf(stderr, "shmid error, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }

    /**
     * 映射共享内存段附加到进程的地址空间
     *
     * (共享内存首地址)shmat(共享内存ID, NULL 自动选择合适的虚拟内存, 0 可读可写)
     *
     */
    data = (int *)shmat(shmid, NULL, 0);
    if (data == (int *)-1) // 错误处理
    {
        fprintf(stderr, "shmat error, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }

    // 进入临界区 p操作 开始执行代码段
    sem_p(semid);
    printf("Process C: 读到 data 为 %d\n", *data);
    // 离开临界区 v操作 代码段执行完毕后
    sem_v(semid);

    /**
     * 分离共享内存段
     *
     * shmdt解除映射,因为processB不使用这个共享内存空间, 但解除映射不是删除共享内存空间, 空间还在, 通过keyID访问
     */
    if (shmdt(data) == -1)
    {
        fprintf(stderr, "shmdt error, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }

    return 0;
}

编译

gcc processA.c -o processA
gcc processB.c -o processB
gcc processC.c -o processC

运行

yuyi@IoTDevelopment:$ ./processA
processA: 初始化信号量和共享内存成功.
yuyi@IoTDevelopment:$ ./processB
Process B: 改变 data 为 1
yuyi@IoTDevelopment:$ ./processC
Process C: 读到 data 为 1
yuyi@IoTDevelopment:$ ./processB
Process B: 改变 data 为 2
yuyi@IoTDevelopment:$ ./processC
Process C: 读到 data 为 2
yuyi@IoTDevelopment:$ ./processB
Process B: 改变 data 为 3
yuyi@IoTDevelopment:$ ./processB
Process B: 改变 data 为 4
yuyi@IoTDevelopment:$ ./processB
Process B: 改变 data 为 5
yuyi@IoTDevelopment:$ ./processC
Process C: 读到 data 为 5
yuyi@IoTDevelopment:$ ./processA
semget error, errno:17,File exists
yuyi@IoTDevelopment:$ ./processC
Process C: 读到 data 为 5
yuyi@IoTDevelopment:$ ./processB
Process B: 改变 data 为 6
yuyi@IoTDevelopment:$ ./processC
Process C: 读到 data 为 6

可见实现了互斥访问

标签:semid,int,errno,信号量,互斥,共享内存,data
From: https://www.cnblogs.com/zqingyang/p/18218963

相关文章

  • 共享内存练习题
    设计一个程序,作为进程A,进程A专门创建一个信号量集,要求信号量集中有1个信号量,对信号量集合中的信号量进行设置,要求集合中的信号量的初值为1,然后再设计2个程序,分别是进程B和进程C,要求进程B和进程C使用进程A创建的信号量集合中的信号量实现互斥访问。提示:进程A、进程B、进程C需要使......
  • iceoryx源码阅读(四)——共享内存通信(二)
    目录1 队列数据结构2 共享内存获取2.1 PublisherImpl::loan2.2PublisherImpl::loanSample2.3 PublisherPortUser::tryAllocateChunk2.4ChunkSender::tryAllocate3 消息发送逻辑3.1PublisherImpl::publish3.2PublisherPortUser::sendChunk3.3ChunkSender::send3.4ChunkDistri......
  • 系统编程练习题---利用共享内存和信号量集,完成两个进程之间的互斥通信
    目录题目解析代码展示process_A.cprocess_B.cprocess_C.c结果展示重要知识点记录题目设计一个程序,作为进程A,进程A专门创建一个信号量集,要求信号量集中有1个信号量,对信号量集合中的信号量进行设置,要求集合中的信号量的初值为1,然后再设计2个程序,分别是进程B和进程C,要求进程B和进程......
  • 信号量集与共享内存
    信号量集与共享内存​ 今日练习:设计一个程序,作为进程A,进程A专门创建一个信号量集,要求信号量集中有1个信号量,对信号量集合中的信号量进行设置,要求集合中的信号量的初值为1,然后再设计2个程序,分别是进程B和进程C,要求进程B和进程C使用进程A创建的信号量集合中的信号量实现互斥访问。......
  • A申请共享内存并对信号量进行初始化,然后进程B与C实现互斥
    练习:设计一个程序,作为进程A,进程A专门创建一个信号量集,要求信号量集中有1个信号量,对信号量集合中的信号量进行设置,要求集合中的信号量的初值为1,然后再设计2个程序,分别是进程B和进程C,要求进程B和进程C使用进程A创建的信号量集合中的信号量实现互斥访问。提示:进程A、进程B、进......
  • 利用信息量集实现互斥访问
    进程A用来生成信号集,并将其初值设为1(运行顺序为进程A->B->C)/**@Author:[email protected]*@Date:2024-05-0817:20:41*@LastEditors:[email protected]*@LastEditTime:2024-05-2818:47:38*/#include<stdio.h>#include<stdlib.h>#include<sys/......
  • 进程间同步(互斥锁)
    【一】什么是互斥锁互斥锁是一种用于多线程编程中控制对共享资源访问的机制限制当前时间段只能由当前进程使用,当前进程使用完成后才能其他进程继续使用基本原理是在对共享资源进行访问前加锁,使得其他线程无法访问该资源,当访问完成后再解锁,使得其他线程可以进行访问【......
  • 共享内存练习题
    设计三个程序,要求三个程序申请一块共享内存,并分别映射到各自进程的地址空间,进程A和进程B对共享内存段中的数据进行修改,然后进程C不断输出共享内存段中的数据,并观察效果,要求实现进程间的互斥,避免竞争。进程A:/******************************************************************......
  • 【.NET深呼吸】线程信号量(Semaphore)
    Semaphore类可以控制某个资源允许访问的线程数,Semaphore有命名式的,也有不命名的;如果不考虑跨进程工作,一般在代码中使用不命名方式即可。信号量有点类似于等待句柄,某个线程如果调用了WaitOne方法,这个线程就会暂停,并且等待有可用的信号量时才会继续执行;某个线程调用Release方法,就......
  • 互斥锁、进程间通信(IPC)、队列(queue)模块、队列实现进程间通信、生产者和消费者模型
    【一】互斥锁【1】什么是进程同步(互斥锁)互斥锁(Mutex)是一种用于多线程编程中控制对共享资源访问的机制。其作用是保证在同一时刻只有一个线程在访问共享资源,从而避免多个线程同时读写数据造成的问题。互斥锁的基本原理是在对共享资源进行访问前加锁,使得其他线程无法访问该......