操作系统--进程同步

实验二 进程同步
一、实验目的:
掌握基本的同步算法,理解经典进程同步问题的本质;学习使用Linux的进程同步机制,掌握相关API的使用方法;能利用信号量机制,采用多种同步算法实现不会发生死锁的哲学家进餐程序。
二、实验平台:
虚拟机:VMWare9以上
操作系统:Ubuntu12.04以上
编辑器:Gedit | Vim
编译器:Gcc
三、实验内容:
(1)以哲学家进餐模型为依据,在Linux控制台环境下创建5个进程,用semget函数创建一个信号量集(5个信号量,初值为1),模拟哲学家的思考和进餐行为:每一位哲学家饥饿时,先拿起左手筷子,再拿起右手筷子;筷子是临界资源,为每一支筷子定义1个互斥信号量;想拿到筷子需要先对信号量做P操作,使用完释放筷子对信号量做V操作。
伪代码描述:
semaphore chopstick[5]={1,1,1,1,1};
• 第i位哲学家的活动可描述为:
do{
printf("%d is thinking\n",i);
printf("%d is hungry\n",i);
wait(chopstick[i]); //拿左筷子
wait(chopstick[(i+1) % 5]); //拿右筷子
printf("%d is eating\n",i);
signal(chopstick[i]); //放左筷子
signal(chopstick[(i+1) % 5]); //放右筷子

}while[true];
在这里插入图片描述

运行该组进程,观察进程是否能一直运行下去,若停滞则发生了什么现象?并分析原因。

不能一直运行下去,会出现进程死锁,因为创建了五个进程,它们之间会相互竞争,一旦五个进程都同时拿到自己左筷子,那么都会因为拿不到右筷子而陷入等待,也就产生了死锁。

(2)解决哲学家进餐问题可采用如下方法:a.仅当哲学家的左、右两只筷子均可用时,才允许他拿起筷子进餐;b.至多只允许有4位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能够进餐;c.规定奇数号哲学家先拿起他左手的筷子,然后再拿起他右手的筷子,而偶数号哲学家则先拿起他右手的筷子,然后再拿起他左手的筷子。方法a在示例程序中给出,请用方法b和c写出不会发生死锁的哲学家进餐程序。
方法b:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *_buf;
};


#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
//获取互斥信号量
void wait_mutex(int mutex)
{
struct sembuf sb={0,-1,0};
semop(mutex,&sb,1);//对互斥信号量进行操作
}

//取得筷子
void wait_v(int semid,int num)
{
struct sembuf sb={num,-1,0};
semop(semid,&sb,1);
}
//释放筷子
void signal_p(int semid,int num)
{
struct sembuf sb={num,1,0};
semop(semid,&sb,1);
}

//释放互斥变量mutex
void signal_mutex(int semid0)
{
struct sembuf sb={0,1,0};
semop(semid0,&sb,1);
}
//ph函数
void ph(int num,int semid,int semid0)
{
int left=num;
int right=(num+1)%5;
for(;;)
{
printf("%d is thinking\n",num);
sleep(1);
printf("%d is hungry\n",num);
sleep(1);
//wait操作,控制哲学家最多4人能进餐
wait_mutex(semid0);//互斥 
wait_v(semid,left);
wait_v(semid,right);
printf("%d is eating\n",num);
sleep(1);
//signal操作
signal_p(semid,right);//释放右筷子
signal_p(semid,left);//释放左快子
signal_mutex(semid0);//释放互斥信号量
}
}
//主函数
int main(int argc,char *argv[])
{
int semid,semid0;
//创建两个信号量集
semid0=semget(IPC_PRIVATE,1,IPC_CREAT | 0666);
semid=semget(IPC_PRIVATE,5,IPC_CREAT | 0666);
//
union semun su;
su.val=1;
int i;
for(i=0;i<5;i++)
{
//semctl()系统调用在一个信号量集(或集合中的单个信号量)上执行各种控制操作
semctl(semid,i,SETVAL,su);
}
//设定semid0信号量的初始值
union semun su0;
su0.val=4;
semctl(semid0,0,SETVAL,su0);
//创建4个子进程
int num=0;
pid_t pid;
for(i=1;i<5;i++)
{
pid=fork();
if(pid<0) {ERR_EXIT("fork");}
if(pid==0) {num=i;break;}
}
//第num个哲学家要做的事
ph(num,semid,semid0);
return 0;
}
 

在这里插入图片描述

//方法c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};


#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)

//取得筷子
void wait_v(int semid,int num)
{
struct sembuf sb={num,-1,0};
semop(num,&sb,1);
}
//释放筷子
void signal_p(int semid,int num)
{
struct sembuf sb={num,-1,0};
semop(num,&sb,1);
}

//科学家要做的事
void ph(int semid,int num)
{
for(;;)//死循环
{
//判断哲学家的编号是奇数还是偶数
//奇数先申请左边的筷子,偶数先申请右边的筷子
if(num%2!=0)
{//判断奇数
printf("%d is thinking\n",num);
sleep(1);
printf("%d is hungry\n",num);
sleep(1);
//wait操作
wait_v(semid,num);
wait_v(semid,(num+1)%5);
printf("%d is eating\n",num);
sleep(1);
//signal操作
signal_p(semid,(num+1)%5);
signal_p(semid,num);
}
if(num%2==0)
{//判断偶数
printf("%d is thinking\n",num);
sleep(1);
printf("%d is hungry\n",num);
sleep(1);
//wait操作
wait_v(semid,(num+1)%5);
wait_v(semid,num);
//signal操作
signal_p(semid,num);
signal_p(semid,(num+1)%5);
}
}
}
int main(int argc,char *argv[])
{
int semid;
//创建5个信号量
semid=semget(IPC_PRIVATE,5,IPC_CREAT | 0666);
union semun su;
su.val=1;
int i;
for(i=0;i<5;i++)
{
//注意第二个参数也是索引
semctl(semid,i,SETVAL,su);
}
//创建4个子进程
pid_t pid;
int num=5;
for(i=0;i<4;i++)
{
pid=fork();
if(pid<0) { ERR_EXIT("fork");}
if(pid==0) { num=i;break;}
}
//哲学家要做的事
ph(semid,num);
return 0;
}

在这里插入图片描述

(3)设计程序,实现生产者/消费者进程(线程)的同步与互斥。在该程序中创建4个进程(或线程)模拟生产者和消费者,实现进程(线程)的同步与互斥。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>
#define N 2   // 消费者或者生产者的数目
#define M 10 // 缓冲数目
int buff[M] = {0}; // 缓冲初始化为0, 开始时没有产品
int in = 0;   // 生产者放置产品的位置
int out = 0; // 消费者取产品的位置
int full_sem,empty_sem;// 同步信号量, 当满了时阻止生产者放产品 当没产品时阻止消费者消费
int mutex;
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)

/* 打印缓冲情况 */
void print()
{
	int i;
	for(i = 0; i < M; i++)
	   printf("%d ", buff[i]);
	printf("\n");
}
//取得筷子
void wait_v(int semid)
{

struct sembuf sb={0,-1,0};
semop(semid,&sb,1);
}
//释放筷子
void signal_p(int semid)
{
struct sembuf sb={0,1,0};
semop(semid,&sb,1);
}
//释放互斥变量mutex
void signal_mutex(int semid0)
{
struct sembuf sb={0,1,0};
semop(semid0,&sb,1);
}
//获取互斥信号量
void wait_mutex(int mutex)
{
struct sembuf sb={0,-1,0};
semop(mutex,&sb,1);//对互斥信号量进行操作
}

/* 生产者方法 */ 
void product(int semid,int num,int full_sem,int empty_sem,int mutex){
 int id = ++num;
 while(1){
  sleep(1);
 //printf("%d", full_sem);

 wait_v(empty_sem); 
 //printf("有空位\t");
 
 wait_mutex(mutex);
 in = in % M;
 printf("product%d in %d. like: \t", id, in);

 buff[in] = 1;  
 print();  
 ++in;

 signal_mutex(mutex);
  signal_p(full_sem); 
 //printf("加\n");
 }
}

/* 消费者方法 */
void prochase(int semid,int num,int full_sem,int empty_sem,int mutex){
  int id = ++num;
 
 while(1){
 sleep(1);
 wait_v(full_sem);
 //printf("有东西\t");

 wait_mutex(mutex);
 
  out = out % M;
   printf("prochase%d out %d. like: \t", id, out);
   buff[out] = 0;
   print();
   ++out;
 
 signal_mutex(mutex);
 signal_p(empty_sem);
 // printf("减\n");
 }
}
int main(int argc,char *argv[])
{
int full_sem,empty_sem;// 同步信号量, 当满了时阻止生产者放产品 当没产品时阻止消费者消费
int mutex;
full_sem=semget(IPC_PRIVATE,1,IPC_CREAT | 0666);
empty_sem=semget(IPC_PRIVATE,1,IPC_CREAT | 0666);
union semun su;
su.val=M;
semctl(empty_sem,0,SETVAL,su);
union semun su4;
su4.val=0;
semctl(full_sem,0,SETVAL,su4);
mutex = semget(IPC_PRIVATE,1,IPC_CREAT | 0666);
union semun su1;
su1.val=1;
semctl(mutex,0,SETVAL,su1);
int semid1,semid2;
semid1=semget(IPC_PRIVATE,N,IPC_CREAT | 0666);
semid2=semget(IPC_PRIVATE,N,IPC_CREAT | 0666);
union semun su2;
su2.val=1;
int i;
for(i=0;i<N;i++)
{
//注意第二个参数也是索引
semctl(semid1,i,SETVAL,su2);
semctl(semid2,i,SETVAL,su2);
}

int num1=0;
pid_t pid1;
//int num2 = 0;
pid_t pid2;
//int j;

// 创建N个生产者线程
for(i=1;i<=N;i++)
{
	pid1=fork();
	if(pid1<0) {ERR_EXIT("fork");}
	if(pid1==0) {num1=i;
	printf("product%d creation \n", num1);
	product(semid1,num1,full_sem,empty_sem,mutex);
	break;}
	else{num2=i;
	printf("prochase%d creation \n", num2);
	prochase(semid2,num2,full_sem,empty_sem,mutex);
	}
}return 0;
}

在这里插入图片描述

自己写的修炼不够,不想写了,就这吧,运行了一下也不知道是个啥鬼东西,

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值