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

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    :RISE_AND_GRIND@163.com
 * @date      :2024/05/28
 * @version   :1.0
 * @note      :
 * CopyRight (c)  2023-2024   RISE_AND_GRIND@163.com   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    :RISE_AND_GRIND@163.com
 * @date      :2024/05/28
 * @version   :1.0
 * @note      :
 * CopyRight (c)  2023-2024   RISE_AND_GRIND@163.com   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    :RISE_AND_GRIND@163.com
 * @date      :2024/05/28
 * @version   :1.0
 * @note      : 进程B和进程C使用信号量实现对共享内存的互斥访问,确保在同一时刻只能有一个进程访问共享内存。
 * CopyRight (c)  2023-2024   RISE_AND_GRIND@163.com   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

可见实现了互斥访问

posted @ 2024-05-28 21:26  舟清颺  阅读(23)  评论(0编辑  收藏  举报