- 一、实验目的
- 二、实验任务
- 三、实验要求
- 四、实验过程
- 五、实验测试
了解和熟悉linux系统下的信号量集和共享内存。
二、实验任务使用linux系统提供的信号量集和共享内存实现生产者和消费者问题。
三、实验要求- 写两个程序,一个模拟生产者过程,一个模拟消费者过程;
- 创建一个共享内存模拟生产者-消费者问题中缓冲队列,该缓冲队列有N(例如N=10)个缓冲区,每个缓冲区的大小为1024B,每个生产者和消费者对缓冲区必须互斥访问;
- 由第一个生产者创建信号量集和共享内存,其他生产者和消费者可以使用该信号量集和共享内存;
- 生产者程序:生产者生产产品(即是从键盘输入长度小于1024B的字符)放入空的缓冲区;
- 消费者程序:消费者消费产品(即从满的缓冲区中取出内容在屏幕上打印出来),然后满的缓冲区变为空的缓冲区;
- 多次运行生产者程序和消费者进程,可以产生多个生产者进程和多个消费者进程,这些进程可以共享这些信号量和共享内存,实现生产者和消费者问题;
- 在生产者程序程序中,可以选择: (1)生产产品; (2)退出。退出进程,但信号量和共享内存仍然存在,其他生产者进程和消费者进程还可以继续使用; (3)删除信号量和共享内存。显性删除信号量和共享内存,其他生产者进程和消费者进程都不能使用这些信号量和共享内存。
- 在消费者程序中,可以选择: (1)消费产品; (2)退出。退出进程,但信号量和共享内存仍然存在,其他生产者进程和消费者进程还可以继续使用; (3)删除信号量和共享内存。显性删除信号量和共享内存,其他生产者进程和消费者进程都不能使用这些信号量和共享内存。
1、实验结构:
实验一共包括2个文件,一个是producer.c,负责生产产品;一个是consumer.c,负责消费产品。编译后生成producer和consumer,负责展示生产者和消费者的过程。
2、producer.c文件
(1)宏定义
#define N 5 // 缓冲队列个数
#define BUFFERSIZE 1024 // 缓冲存储区大小为 1024B
#define SEMNUM 3 // 信号量集中信号量个数为 3,分别为 mutex、full 和 empty
宏定义使得程序的修改方便。
(2)结构定义
struct sharedMemory { // 共享内存结构
char buffer[N][BUFFERSIZE]; // 消息存储区
int put, take; // 两个指针
};
union semun {
int val; // value for setval
struct semid_ds *buf; // buffer for IPC_STAT & IPC_SET
unsigned short *array; // array for GETALL & SETALL
struct seminfo *_buf; // buffer for IPC_INFO
void *_pad;
};
sharedMemory是我们自己定义的共享内存结构,其中包括N个大小为BUFFERSIZE的消息队列和存放指针put、take。 semun是用来操作信号量的共用体,这里由我们自己定义,程序中主要使用它的val成员。
(3)封装函数操作
- P、V操作:
这里为了mian函数代码的简洁和直观性,封装了P、V操作。其中第一个参数是信号量集标识semid,第二个参数标识信号量集中第semnum个信号量。这里P、V操作对信号量的资源改变数量都为1.// 封装 P 操作 int P(int semid, unsigned short semnum) { struct sembuf semaphore; semaphore.sem_num = semnum; semaphore.sem_op = -1; semaphore.sem_flg = SEM_UNDO; return semop(semid, &semaphore, 1); } // 封装 V 操作 int V(int semid, unsigned short semnum) { struct sembuf semaphore; semaphore.sem_num = semnum; semaphore.sem_op = 1; semaphore.sem_flg = SEM_UNDO; return semop(semid, &semaphore, 1); }
- 信号量的读写操作:
出于和上面P、V操作一样的目的,这里前两个参数和上面一样,setSemVal操作中第三个参数val表示对信号量的赋值。// 封装对信号量的赋值操作 int setSemVal(int semid, int semnum, int val) { union semun smun; smun.val = val; return semctl(semid, semnum, SETVAL, smun); } // 封装读取信号量的操作 int getSemVal(int semid, int semnum) { return semctl(semid, semnum, GETVAL, 0); }
(4)main函数
-
main函数中主要变量
int main() { int key, i; // key 为共享内存的键值 int shmexist = 0; // 表示共享内存是否已存在 void *shm = NULL; // 连接共享内存的标记 struct sharedMemory *share; // 连接到共享内存的指针 int shmid; // 共享内存标识符 int semid; // 信号量集标识符 int choice; // 选择标识 int ifbreak = 0; // 跳出循环的标识 char message[BUFFERSIZE]; // 临时消息存储
其中需要说明的是:
shmexist表示该producer是否为第一个producer,以便后面对信号量、共享内存的初始化操作。 ifbreak表示循环是否需要退出,在该实验中,当用户输入2和3时需要退出循环,结束程序。 message用来缓存用户输入的消息。我们不能直接将用户输入的消息存入共享内存中,因为producer可能会因P操作而被挂起。
-
运行的前期操作,包括: ① 创建/获取共享内存
// 获取共享内存的键值 key = ftok("./consumer.c", 0); // 判断是否获取成功 if (key == -1) { printf("ftok() failed\n"); exit(EXIT_FAILURE); } // 请求创建新共享内存 shmid = shmget(key, sizeof(struct sharedMemory), 0666 | IPC_CREAT | IPC_EXCL); // 如果创建新的共享内存失败,则试图获取已有的共享内存 if (shmid == -1) { shmexist = 1; // 共享内存已存在 shmid = shmget(key, sizeof(struct sharedMemory), 0666 | IPC_CREAT); if (shmid == -1) { printf("shmget() failed\n"); exit(EXIT_FAILURE); } }
② 进程与共享内存的连接
// 进程请求连接共享内存,若失败则退出 if ((shm = shmat(shmid, NULL, 0)) == (void *) (-1)) { printf("shmat() failed\n"); exit(EXIT_FAILURE); } // share 访问共享内存 share = (struct sharedMemory *) shm;
③ 创建/获取信号量集
// 创建含有 3 个信号量的信号量集 // 0 : mutex // 1 : full // 2 : empty semid = semget(key, SEMNUM, IPC_CREAT | 0666);
④ producer特有的初始化
// 如果共享内存是第一次创建,则初始化 if (shmexist == 0) { for (i = 0; i buffer[i], ""); share->put = share->take = 0; setSemVal(semid, 0, 1); // 互斥信号量 mutex setSemVal(semid, 1, 0); // 信号量 full setSemVal(semid, 2, N); // 信号量 empty }
-
while函数(主运行程序) ① 输出程序状态,包括: producer的进程id; full、empty信号量的值; put、take指针位置; 共享内存中的数据; 可供用户选择的操作。 int full, empty;
// 获得信号量的值 full = getSemVal(semid, 1); empty = getSemVal(semid, 2); // 一系列输出 printf("This is %d pid producer,\n\n", getpid()); printf("full = %d, empty = %d, put = %d, take = %d.\n\n", full, empty, share->put, share->take); for (i = 0; i put) printf("%d --- %s \t
关注打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?