实验目的
本实验的目的是理解进程同步与互斥的原理,以及解决进程同步和互斥的算法,从而进一步巩固进程同步和互斥等有关的内容。通过本实验,你将能够掌握进程同步和互斥的基本概念,理解进程同步和互斥的重要性,以及如何在实际编程中实现进程同步和互斥。
1、了解进程的创建,进程检查;
2、了解与进程有关函数的实现。如:fork(),exit(),wait(),getpid(),getppid()等
3、了解在多用户环境下进程之间的通信机制, 掌握用共享内存及消息队列实现进程之间的信息传递。
4、掌握进程并发执行的原理,及其所引起的同步、互斥问题的方法
实验内容
本实验的内容是编写程序实现生产者及消费者问题。你需要创建6个生产者及消费者进程,使用信号量机制实现生产者及消费者进程间的同步及互斥。每个生产者进程随机睡眠0~9秒模拟生产数据的过程,然后把自己的进程号写入共享存储区,每个消费者进程从中读取数据并输出并且同步输出自己的进程号。你需要确保程序结果可完整演示生产者及消费者同步生产数据及接受数据的全过程。
实验环境
本实验在VMware虚拟操作系统环境下进行,使用 C编程语言,采用进程(线程)同步和互斥的技术。
实验内容
(1) 信号量机制
1、信号量机制的基本操作:
资源使用者在使用临界资源之前“等待”信号量
资源使用者使用完临界资源后“通知”信号量
2、C语言实现:
注意:以下函数原形在 sys/types.h、sys/ipc.h和sys/sem.h包含文件中。
A)创建信号量集
int semget(key_t key,int nsems,int semflg)
其中:key—创建信号量集关键字。
nsems—信号量集中信号量的数量。
semflg—指定选项及其权限位。
IPC_CREAT—创建新的信号量集
IPC_EXCEL—如果信号量集已经存在,则返回错误。
<XXX XXX XXX>--和文件、目录一样权限。
返回一个信号量集ID--semid
B) 获得一个已经存在的信号量集
int semget(key_t key,0,0)
key—含义同上。
C)等待、通知一个信号量集
int semop(int semid,struct sembuf *sops,unsigned nsops)
semid—由函数semget()产生的。
sops—描述信号量的操作:等待、通知等
struct sembuf{
short sem_num;/*semaphore number */
short sem_op; /*semaphore operation:-1 for waiting*/
short sem_flg; /* operation flag :0、IPC_NOWAIT */
}
nsops—指定信号量集中操作的信号量个数。
D) 控制信号量集的操作
int semctl(int semid,int semnum,int cmd,union semun arg)
semid—由semget()创建的信号量集标识。
semnum—信号量数量。
cmd—对信号量semid所进行的操作:SETALL—所有参数。
arg—对信号量集操作的初始数据。
arg的类型:
union semun{
int val; /* 仅用于参数SETVAL*/
struct semid_ds *buf; /*指向IPC_STAT和IPC_SET的semid_ds结构*/
ushort *array; /*用于GETALL和SETALL:指向一个初值的数*/
};
union semun arg;
本参数主要用于存放各个信号量的初始值。
实验要求
编写程序,解决生产者及消费者问题。分别创建6个生产者及消费者进程,使用信号量机制实现生产者及消费者进程间的同步及互斥。每个生产者进程随机睡眠0~9秒模拟生产数据的过程,然后把自己的进程号写入共享存储区,每个消费者进程从中读取数据并输出并且同步输出自己的进程号。要求程序结果可完整演示生产者及消费者同步生产数据及接受数据的全过程。
具体实现
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/wait.h>
#define BUFFER_SIZE 5
#define NUM_PRODUCERS 6
#define NUM_CONSUMERS 6
int shmid;
int *buffer;
void producer(int id, int sem_id) {
while (1) {
sleep(rand() % 10); // 随机睡眠0~9秒
int data = id; // 生产数据为进程号
struct sembuf sops[1];
sops[0].sem_num = 0;
sops[0].sem_op = -1;
sops[0].sem_flg = 0;
semop(sem_id, sops, 1); // 等待缓冲区不满
sops[0].sem_num = 1;
sops[0].sem_op = -1;
sops[0].sem_flg = 0;
semop(sem_id, sops, 1); // 互斥访问缓冲区
buffer[*buffer] = data; // 生产数据
printf("生产者 %d 生产数据:%d\n", id, data);
*buffer = (*buffer + 1) % BUFFER_SIZE;
sops[0].sem_num = 1;
sops[0].sem_op = 1;
sops[0].sem_flg = 0;
semop(sem_id, sops, 1); // 释放缓冲区互斥访问
sops[0].sem_num = 2;
sops[0].sem_op = 1;
sops[0].sem_flg = 0;
semop(sem_id, sops, 1); // 增加共享数据的个数
}
}
void consumer(int id, int sem_id) {
int data;
while (1) {
struct sembuf sops[1];
sops[0].sem_num = 2;
sops[0].sem_op = -1;
sops[0].sem_flg = 0;
semop(sem_id, sops, 1); // 互斥访问缓冲区
sops[0].sem_num = 0;
sops[0].sem_op = -1;
sops[0].sem_flg = 0;
semop(sem_id, sops, 1); // 等待缓冲区非空
data = buffer[data]; // 消费数据
printf("消费者 %d 消费数据:%d\n", id, data);
*buffer = (*buffer + 1) % BUFFER_SIZE;
sops[0].sem_num = 0;
sops[0].sem_op = 1;
sops[0].sem_flg = 0;
semop(sem_id, sops, 1); // 释放共享数据的个数
sops[0].sem_num = 2;
sops[0].sem_op = 1;
sops[0].sem_flg = 0;
semop(sem_id, sops, 1); // 释放缓冲区互斥访问
}
}
int main() {
// 创建共享内存
shmid = shmget(IPC_PRIVATE, sizeof(int), IPC_CREAT | 0666);
if (shmid == -1) {
perror("shmget");
exit(1);
}
// 初始化共享内存
buffer = (int *)shmat(shmid, NULL, 0);
*buffer = 0;
// 创建信号量
int sem_id = semget(IPC_PRIVATE, 3, IPC_CREAT | 0666);
if (sem_id == -1) {
perror("semget");
exit(1);
}
// 初始化信号量
union semun {
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
} arg;
unsigned short sem_vals[3] = {BUFFER_SIZE, 0, 1};
arg.array = sem_vals;
if (semctl(sem_id, 0, SETALL, arg) == -1) {
perror("semctl");
exit(1);
}
// 创建生产者进程
for (int i = 0; i < NUM_PRODUCERS; i++) {
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
} else if (pid == 0) {
producer(i + 1, sem_id);
exit(0);
}
}
// 创建消费者进程
for (int i = 0; i < NUM_CONSUMERS; i++) {
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
} else if (pid == 0) {
consumer(i + 1, sem_id);
exit(0);
}
}
// 等待子进程结束
for (int i = 0; i < NUM_PRODUCERS + NUM_CONSUMERS; i++) {
wait(NULL);
}
// 删除共享内存和信号量
shmctl(shmid, IPC_RMID, NULL);
semctl(sem_id, 0, IPC_RMID);
return 0;
实验结果
实验总结
这个实验是关于生产者和消费者问题的并发编程实验,通过共享内存和信号量来实现生产者和消费者之间的同步与互斥。
在这个实验中,我们首先创建了一个共享内存区域,用来作为生产者和消费者之间的缓冲区。然后,我们创建了三个信号量,分别用于表示缓冲区的状态(非空、非满)、互斥访问缓冲区以及共享数据的个数。通过对这些信号量的操作,实现了生产者和消费者之间的协调和互斥访问。
在主程序中,我们创建了若干个生产者和消费者进程,并通过 fork() 系统调用来创建子进程。每个子进程中都有对应的生产者或消费者函数,通过操作共享内存和信号量来实现生产和消费的过程。
最后,我们使用 wait() 函数等待所有子进程结束,并在程序结束时释放共享内存和信号量。
这个实验涉及了多进程的并发编程、共享内存和信号量的使用,是一个典型的操作系统课程中的并发控制实验。通过这个实验,可以更好地理解并发编程中的同步与互斥机制,以及进程间通信的方法和技巧。