目录
一、题目
1.题目
实现父亲、女儿、儿子之间的同步。 桌上有个能盛得下5个水果的空盘子, 父亲不停地向盘中放苹果或桔子, 儿子不停地从盘中取桔子享用, 女儿不停地从盘中取苹果享用, 规定三人不能同时从盘子中取放水果。
2.目的
用简单的例子实现进程间的同步和通信。
3.思路
通过共享内存,不同的进程可以访问和修改同一份数据。通过信号量,进程可以协调它们的访问,确保数据的一致性和正确性。例如,father函数中的P操作确保在修改共享内存之前,没有其他进程也在修改它;V操作则确保修改完成后,其他等待的进程可以继续执行。
二、具体实现
注意:完整的程序需要包括信号量的创建、共享内存的分配和连接、以及其他可能涉及的进程创建和同步操作。下面从三个比较重要的部分来分析:共享内存结构体定义、信号量操作函数P和V的定义、以及father函数的功能。
1.共享内存结构体定义:
- 首先定义一个名为shared_memory的结构体,用于在进程间共享数据。该结构体包含三个整型变量:apple、orange和num,分别用于记录苹果的数量、桔子的数量和总水果数量。
#define SHM_SIZE 1024
struct shared_memory {
int apple;
int orange;
int num;
};
2.信号量操作函数P和V的定义:
- 定义了用于操作信号量的函数:P操作和V操作。
- P操作用于申请资源,将信号量的值减1。如果信号量的值为0,则调用进程将进入睡眠状态,直到信号量的值大于0。
- V操作用于释放资源,将信号量的值加1。如果有其他进程在等待该信号量,则其中一个进程将被唤醒。
void P(int semid) {
struct sembuf sem;
sem.sem_num = 0;
sem.sem_op = -1;
sem.sem_flg = SEM_UNDO;
semop(semid, &sem, 1);
}
void V(int semid) {
struct sembuf sem;
sem.sem_num = 0;
sem.sem_op = 1;
sem.sem_flg = SEM_UNDO;
semop(semid, &sem, 1);
}
3.father函数的功能
- father函数是进程的一部分,用于模拟爸爸往一个共享的果盘里放入水果。
- 在函数开始,首先执行P操作,申请资源(即信号量)。如果果盘里的水果数量少于5个,则随机决定放入一个苹果或桔子,并更新共享内存中的相应值。然后,释放资源,执行V操作。
void father(int semid, struct shared_memory *shm) {
P(semid);
if (shm->num < 5) {
int b = rand() % 2;
if (b == 0) {
shm->apple++;
shm->num++;
printf("爸爸放入一个苹果,现在盆子里有%d 个水果,其中苹果有%d 个,桔子有%d 个\n", shm->num, shm->apple, shm->orange);
} else {
shm->orange++;
shm->num++;
printf("爸爸放入一个桔子,现在盆子里有%d 个水果,其中苹果有%d 个,桔子有%d 个\n", shm->num, shm->apple, shm->orange);
}
}
V(semid);
}
三、运行结果及分析
1.分析
-
其中,父进程负责向共享内存中放入水果,而女儿和儿子进程则负责从共享内存中取出水果。
-
程序会要求用户输入运行次数,这个次数决定了本程序会进行多少次进程操作。
-
在每次循环中,会随机选择一个进程进行操作。具体的操作由father、daughter和son三个函数实现,分别对应父进程、女儿进程和儿子进程。
-
在每次操作后,程序会打印出当前共享内存中水果的数量和分布情况。最后,程序会删除共享内存和信号量。因为是随机选择进程进行操作,因此每次运行的结果都不一样。
2.运行结果
- 运行次数5
- 运行次数10
四、源码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define SHM_SIZE 1024
typedef int semaphore;
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
struct shared_memory {
int apple;
int orange;
int num;
};
void P(int semid) {
struct sembuf sem;
sem.sem_num = 0;
sem.sem_op = -1;
sem.sem_flg = SEM_UNDO;
semop(semid, &sem, 1);
}
void V(int semid) {
struct sembuf sem;
sem.sem_num = 0;
sem.sem_op = 1;
sem.sem_flg = SEM_UNDO;
semop(semid, &sem, 1);
}
void father(int semid, struct shared_memory *shm) {
P(semid);
if (shm->num < 5) {
int b = rand() % 2;
if (b == 0) {
shm->apple++;
shm->num++;
printf("爸爸放入一个苹果,现在盆子里有%d个水果,其中苹果有%d个,桔子有%d个\n", shm->num, shm->apple, shm->orange);
} else {
shm->orange++;
shm->num++;
printf("爸爸放入一个桔子,现在盆子里有%d个水果,其中苹果有%d个,桔子有%d个\n", shm->num, shm->apple, shm->orange);
}
} else {
printf("爸爸操作失败,盆子里已经有5个水果,其中有%d个苹果,%d个桔子\n", shm->apple, shm->orange);
}
V(semid);
}
void daughter(int semid, struct shared_memory *shm) {
P(semid);
if (shm->apple > 0) {
shm->apple--;
shm->num--;
printf("女儿拿了一个苹果,盆子里还有%d个水果,其中苹果有%d个,桔子有%d个\n", shm->num, shm->apple, shm->orange);
} else {
printf("女儿拿取失败,盆子里有%d个水果,其中苹果有%d个,桔子有%d个\n", shm->num, shm->apple, shm->orange);
}
V(semid);
}
void son(int semid, struct shared_memory *shm) {
P(semid);
if (shm->orange > 0) {
shm->orange--;
shm->num--;
printf("儿子拿了一个桔子,盆子里还有%d个水果,其中苹果有%d个,桔子有%d个\n", shm->num, shm->apple, shm->orange);
} else {
printf("儿子拿取失败,盆子里有%d个水果,其中苹果有%d个,桔子有%d个\n", shm->num, shm->apple, shm->orange);
}
V(semid);
}
int main() {
int n, a;
int shmid, semid;
struct shared_memory *shm;
union semun semarg;
// 创建共享内存
shmid = shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0666);
if (shmid == -1) {
perror("shmget");
exit(1);
}
// 连接共享内存
shm = (struct shared_memory *) shmat(shmid, NULL, 0);
if (shm == (struct shared_memory *) -1) {
perror("shmat");
exit(1);
}
// 初始化共享内存
shm->apple = 0;
shm->orange = 0;
shm->num = 0;
// 创建信号量
semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
if (semid == -1) {
perror("semget");
exit(1);
}
// 初始化信号量
semarg.val = 1;
if (semctl(semid, 0, SETVAL, semarg) == -1) {
perror("semctl");
exit(1);
}
printf("请输入你要运行的次数:");
scanf("%d", &n);
while (n--) {
a = rand() % 3;
if (a == 0) {
printf("爸爸进行操作\n");
father(semid, shm);
printf("\n");
} else if (a == 1) {
printf("女儿进行操作\n");
daughter(semid, shm);
printf("\n");
} else {
printf("儿子进行操作\n");
son(semid, shm);
printf("\n");
}
}
// 删除信号量
if (semctl(semid, 0, IPC_RMID, semarg) == -1) {
perror("semctl");
exit(1);
}
// 分离共享内存
if (shmdt(shm) == -1) {
perror("shmdt");
exit(1);
}
// 删除共享内存
if (shmctl(shmid, IPC_RMID, NULL) == -1) {
perror("shmctl");
exit(1);
}
return 0;
}