3个进程实现共享内存、信号量、互斥访问
V1.0 2024年5月28日 发布于博客园
题目
设计一个程序,作为进程A,进程A专门创建一个信号量集,要求信号量集中有1个信号量,对信号量集合中的信号量进行设置,要求集合中的信号量的初值为1,然后再设计2个程序,分别是进程B和进程C,要求进程B和进程C使用进程A创建的信号量集合中的信号量实现互斥访问。 提示:进程A、进程B、进程C需要使用共享内存作为临界资源的访问。
解答
我们先分别创建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
可见实现了互斥访问
本文来自博客园,作者:舟清颺,转载请注明原文链接:https://www.cnblogs.com/zqingyang/p/18218963