问题描述

在两个城市南北方向之间存在一条铁路,多列火车可以分别从两个城市的车站排队等待进入车道向对方城市行驶,该铁路在同一时间,只能允许在同一方向上行车,如果同时有相向的火车行驶将会撞车。请模拟实现两个方向行车,而不会出现撞车或长时间等待的情况。您能构造一个管程来解决这个问题吗?

程序实现

dp.h

#include <iostream>
#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>
#include <sys/msg.h>
#include <sys/wait.h>
/*信号灯控制用的共同体*/
typedef union semuns
{int val;
} Sem_uns;
//管程中使用的信号量
class Sema
{public:
Sema(int id);
~Sema();
int down(); //信号量加 1
int up(); //信号量减 1
private:
int sem_id; //信号量标识符
};
//管程中使用的锁
class Lock
{public:
Lock(Sema *lock);
~Lock();
void close_lock();
void open_lock();
private:
Sema *sema; //锁使用的信号量
};
class Condition
{public:
Condition(Sema *sema1, Sema *sema2);
~Condition();
void Wait(Lock *conditionLock, int direct); //过路条件不足时阻塞
int Signal (int direc);
//唤醒相反方向阻塞车辆
private:
Sema* sema0; // 一个方向阻塞队列
Sema* sema1; // 另一方向阻塞队列
Lock* lock; // 进入管程时获取的锁
};
class OneWay
{public:
OneWay (int maxall, int maxcur);
~OneWay();
void Arrive (int direc);
// 车辆准备上单行道,direc 为行车方向
void Cross (int direc);
// 车辆正在单行道上
void Quit (int direc);
// 车辆通过了单行道
int *eastCount;
int *westCount;
int *eastWait;
int *westWait;
int *sumPassedCars;//已经通过的车辆总数
private:
//建立或获取 ipc 信号量的一组函数的原型说明
int get_ipc_id (char *proc_file, key_t key);
int set_sem(key_t sem_key, int sem_val, int sem_flag);
//创建共享内存
char *set_shm(key_t shm_key, int shm_num, int shm_flag);
int rate; //车速
int *maxCars;//最大同向车数
int *numCars; //当前正在通过的车辆数
int *currentDire;//当前通过的车辆的方向
Condition *condition; //通过单行道的条件变量
Lock *lock;//单行道管程锁
};

dp.c

#include "dp.h"
using namespace std;
Sema::Sema(int id)
{sem_id = id;
}
Sema::~Sema()
{}
/*
* 信号灯上的 down/up 操作
* semid:信号灯数组标识符
* semnum:信号灯数组下标
* buf:操作信号灯的结构
*/
int Sema::down()
{struct sembuf buf;
buf.sem_op = -1;
buf.sem_num = 0;
buf.sem_flg = SEM_UNDO;
if ((semop(sem_id, &buf, 1)) < 0)
{perror("down error ");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
int Sema::up()
{Sem_uns arg;
struct sembuf buf;
buf.sem_op = 1;
buf.sem_num = 0;
buf.sem_flg = SEM_UNDO;
if ((semop(sem_id, &buf, 1)) < 0)
{perror("up error ");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
/*
* 用于单行道管程的互斥执行
*/
Lock::Lock(Sema * s)
{sema = s;
}
Lock::~Lock()
{}
//上锁
void Lock::close_lock()
{sema->down();
}
//开锁
void Lock::open_lock()
{sema->up();
}
int OneWay::get_ipc_id (char *proc_file, key_t key)
{#define BUFSZ 256
FILE *pf;
int i, j;
char line[BUFSZ], colum[BUFSZ];
if ((pf = fopen(proc_file, "r")) == NULL)
{perror("Proc file not open");
exit(EXIT_FAILURE);
}
fgets(line, BUFSZ, pf);
while (!feof(pf))
{i = j = 0;
fgets(line, BUFSZ, pf);
while (line[i] == ' ')
i++;
while (line[i] != ' ')
colum[j++] = line[i++];
colum[j] = '\0';
if (atoi(colum) != key)
continue;
j = 0;
while (line[i] == ' ')
i++;
while (line[i] != ' ')
colum[j++] = line[i++];
colum[j] = '\0';
i = atoi(colum);
fclose(pf);
return i;
}
fclose(pf);
return -1;
}
/*
*
set_shm 函数建立一个具有 n 个字节 的共享内存区
*
如果建立成功,返回 一个指向该内存区首地址的指针 shm_buf
*
输入参数:
*
shm_key 共享内存的键值
*
shm_val 共享内存字节的长度
*
shm_flag 共享内存的存取权限
*/
char * OneWay::set_shm(key_t shm_key, int shm_num, int shm_flg)
{int i, shm_id;
char * shm_buf;
//测试由 shm_key 标识的共享内存区是否已经建立
if ((shm_id = get_ipc_id("/proc/sysvipc/shm", shm_key)) < 0)
{//shmget 新建 一个长度为 shm_num 字节的共享内存
if ((shm_id = shmget(shm_key, shm_num, shm_flg)) < 0)
{perror("shareMemory set error");
exit(EXIT_FAILURE);
}
//shmat 将由 shm_id 标识的共享内存附加给指针 shm_buf
if ((shm_buf = (char *) shmat (shm_id, 0, 0)) < (char *) 0)
{perror("get shareMemory error");
exit(EXIT_FAILURE);
}
for (i = 0; i < shm_num; i++)
shm_buf[i] = 0; //初始为 0
}
//共享内存区已经建立,将由 shm_id 标识的共享内存附加给指针 shm_buf
if ((shm_buf = (char *) shmat(shm_id, 0, 0)) < (char *) 0)
{perror("get shareMemory error");
exit(EXIT_FAILURE);
}
return shm_buf;
}
/*
*
set_sem 函数建立一个具有 n 个信号灯的信号量
*
如果建立成功,返回 一个信号量的标识符 sem_id
*
输入参数:
*
sem_key 信号量的键值
*
sem_val 信号量中信号灯的个数
*
sem_flag 信号量的存取权限
*/
int OneWay::set_sem(key_t sem_key, int sem_val, int sem_flg)
{int sem_id;
Sem_uns sem_arg;
//测试由 sem_key 标识的信号量是否已经建立
if ((sem_id = get_ipc_id("/proc/sysvipc/sem", sem_key)) < 0)
{//semget 新建一个信号灯,其标号返回到 sem_id
if ((sem_id = semget(sem_key, 1, sem_flg)) < 0)
{perror("semaphore create error");
exit(EXIT_FAILURE);
}
}
//设置信号量的初值
sem_arg.val = sem_val;
if (semctl(sem_id, 0, SETVAL, sem_arg) < 0)
{perror("semaphore set error");
exit(EXIT_FAILURE);
}
return sem_id;
}
Condition::Condition(Sema *semax1, Sema *semax2)
{sema0 = semax1;
sema1 = semax2;
}
/**
* 看看是否能通过
*/
void Condition::Wait(Lock *lock, int direc)
{if (direc == 0)
{cout << getpid() << "east waiting" << "\n";
lock->open_lock();
sema0->down();
lock->close_lock();
}
else if (direc == 1)
{cout << getpid() << "west waiting" << "\n";
lock->open_lock();
sema1->down();
lock->close_lock();
}
}
int Condition::Signal (int direc)
{int i;
if (direc == 0)
{i = sema0->up();
}
else if (direc == 1)
{i = sema1->up();
}
return i;
}
/*
* get_ipc_id() 从/proc/sysvipc/文件系统中获取 IPC 的 id 号
* pfile: 对应/proc/sysvipc/目录中的 IPC 文件分别为
*
msg-消息队列,sem-信号量,shm-共享内存
* key: 对应要获取的 IPC 的 id 号的键值
*/
Condition::~Condition()
{}
;
/*
*
set_shm 函数建立一个具有 n 个字节 的共享内存区
*
如果建立成功,返回 一个指向该内存区首地址的指针 shm_buf
*
输入参数:
*
shm_key 共享内存的键值
*
shm_val 共享内存字节的长度
*
shm_flag 共享内存的存取权限
*/
OneWay::OneWay (int maxall, int maxcur)
{Sema *sema0;
Sema *sema1;
Sema *semaLock;
int ipc_flg = IPC_CREAT | 0644;
maxCars = (int *) set_shm(100, 1, ipc_flg);
numCars = (int *) set_shm(200, 1, ipc_flg);
currentDire = (int *) set_shm(300, 1, ipc_flg);
eastCount = (int *) set_shm(501, 1, ipc_flg);
westCount = (int *) set_shm(502, 1, ipc_flg);
sumPassedCars = (int *) set_shm(700, 1, ipc_flg);
eastWait = (int *) set_shm(801, 1, ipc_flg);
westWait = (int *) set_shm(802, 1, ipc_flg);
int sema0_id = set_sem(401, 0, ipc_flg);
int sema1_id = set_sem(402, 0, ipc_flg);
int semaLock_id = set_sem(601, maxcur, ipc_flg);
*maxCars = maxcur;
*numCars = 0;
*currentDire = 0;
*eastCount = 0;
*westCount = 0;
*sumPassedCars = 0;
*eastWait = 0;
*westWait = 0;
sema0 = new Sema(sema0_id);
sema1 = new Sema(sema1_id);
semaLock = new Sema(semaLock_id);
lock = new Lock(semaLock);
condition = new Condition(sema0, sema1);
}
void OneWay::Arrive (int direc)
{lock->close_lock();
if ((*currentDire != direc || *numCars >= *maxCars) & *sumPassedCars > 0)
{if (direc == 0)
{*eastWait += 1;
}
else if (direc == 1)
{*westWait += 1;
}
condition->Wait(lock, direc);
}
if (direc == 0) //东 +1
{*eastWait -= 1;
*eastCount = *eastCount + 1;
cout << getpid() << "east entering\n";
}
else if (direc == 1) //西 +1
{*westCount = *westCount + 1;
*westWait -= 1;
cout << getpid() << "west entering\n";
}
*numCars = *numCars + 1;
*currentDire = direc;
*sumPassedCars += 1;
lock->open_lock();
}
void OneWay::Cross (int direc)
{lock->close_lock();
sleep(3);
if (direc == 0)
cout << getpid() << "cross" << *numCars << "\n";
else if (direc == 1)
cout << getpid() << "cross" << *numCars << "\n";
lock->open_lock();
}
void OneWay::Quit (int direc)
{lock->close_lock();
*numCars -= 1;
if (direc == 0)
{cout << getpid() << "leave" << "\n";
}
else if (direc == 1)
{cout << getpid() << "leave" << "\n";
}
if (*numCars == 0)
{if (direc == 0)
{if (*westWait > 0)
{condition->Signal(1);
}
else if (*eastWait > 0)
{condition->Signal(0);
}
}
else if (direc == 1)
{if (*eastWait > 0)
{condition->Signal(0);
}
else if (*westWait > 0)
{condition->Signal(1);
}
}
}
lock->open_lock();
}
OneWay::~OneWay()
{delete condition;
}
int main (int argc, char **argv)
{int maxCars;
int maxSingelDirect;
cout << "请输入总车辆数:";
cin >> maxCars;
cout << "请输入单方向通过的最大车数:";
cin >> maxSingelDirect;
OneWay *oneWay = new OneWay(maxCars, maxSingelDirect);
//建立管程,判断可不可进、决定方向,进入单行道
int i;
int pid[maxCars];
for (i = 0; i < maxCars; i++) //对每一辆车都创建一个子进程
{pid[i] = fork();
if (pid[i] == 0)
{sleep(1);
srand(time(NULL));
int direct = rand() % 2;
direct=*oneWay->sumPassedCars%2;
oneWay->Arrive(direct);
oneWay->Cross(direct);
oneWay->Quit(direct);
exit(EXIT_SUCCESS);
}
}
for (i = 0; i < maxCars; i++)
{waitpid(pid[i], NULL, 0);
}
cout << *(oneWay->eastCount) << "辆列车向东" << *(oneWay->westCount)
<< "辆列车向西,正常通行.\n";
delete oneWay;
return EXIT_SUCCESS;
}

编译

g++ -w -g -c dp.c
g++ dp.o -o dp

编译完成后截图如下:

运行
输入命令

./dp

可以看到运行结果

操作系统实验六、死锁问题实验——单车道问题相关推荐

  1. python大学课程实验六_Python程序设计实验六:函数

    安徽工程大学 Python程序设计 实验报告 班级  物流191   姓名姚彩琴学号3190505129 成绩 日期     2020.5.3     指导老师修宇 [实验目的] 掌握函数的定义与使用 ...

  2. 单片机实验六、计数器实验

    一.实验目的: 1.熟悉MCS-51单片机定时/计数器的外部计数原理: 2.进一步掌握定时/计数器的计数功能的初始化和编程方法. 二.实验内容: 模拟产品包装线上对物品件数的计数,假设每个包装箱10件 ...

  3. 实验六 可变分区存储管理实验

    一.实验目的 通过编写和调试可变分区存储管理的模拟程序以加深对可变分区存储管理方案的理解,熟悉可变分区的分配和回收算法. 通过编写和调试地址转换过程的模拟程序以加强对地址转换过程的了解. 二.实验类型 ...

  4. 计算机网络实验六 综合设计实验

    一.实验目的 1.掌握因特网的配置相关工作 二.实验要求 1.构建一个如下图3所示拓扑图(或简化图,去掉R4): 图3 网络互联拓扑图 2.要求PC1和PC3 是同一单位内部的不同子网(例如B类网络的 ...

  5. 计算机网络实验六,陕师大 计算机网络实验6wireshark

    陕师大抓包实验 计算机网络实验报告 利用WireShark分析IP协议 计科一班 41112009 熊思平 一.实验目的 1. 利用wireshark分析ip协议,学习了解ip数据包. 2. 学习ip ...

  6. c语言实验报告实验六,C语言实验报告六

    实验内容: (1)编写求k!的函数,再调用该函数求C(m,n)=m!/(n! *(m-n)!)并输出. int jch(int x) { int i,s=1; for(i=1;i<=x;i++) ...

  7. 山东财经大学python实验六答案_实验六(带答案)

    一.使用带 IN 谓词的子查询 1. 查询选修了课程名为 ' 计算机网络 ' 的学生的学号和姓名 : SQL Server 中 select 学号 , 姓名 from 学生表 where 学号 in  ...

  8. 【操作系统】实验六 系统内存使用统计

    实验六 一.实验目的 (1)了解Windows内存管理机制,理解页式存储管理技术. (2)熟悉Windows内存管理基本数据结构. (3)掌握Windows内存管理基本API的使用. 二.实验准备 相 ...

  9. 数据结构实验六 综合数据处理

    广州大学学生实验报告 开课实验室:计算机科学与工程实验(电子楼416A)     2019年6月14日 学院 计算机科学与教育软件学院 年级.专业.班 计算机大类 144班 姓名 学号 实验课程名称 ...

  10. python的实验报告大一心理_Python程序设计实验报告: 实验六

    安徽工程大学 Python程序设计 实验报告 班级  物流192班   姓名吕晨学号3190505209  成就 日期    2020.5.4     指导先生修宇 [实验名称]实验六 函数 [实验目 ...

最新文章

  1. 《深入理解计算机系统》第七章读书笔记
  2. python requests post提交数据报错
  3. Luogu P1198 [JSOI2008]最大数 线段树
  4. #ifdef #else #endif 的用法
  5. [密码学基础][每个信息安全博士生应该知道的52件事][Bristol Cryptography][第29篇]什么是UF-CMA数字签名的定义?
  6. Win10 Nodejs搭建http-server注意点
  7. andorid使能ftrace失败
  8. Windows实现微信多开
  9. SQL语句----CASE WHEN 的用法简介
  10. 天玑9000和骁龙888plus哪个好
  11. 身份证读卡器 浏览器插件
  12. ocp|ocm考证系列文章!
  13. 仿抖音右滑清屏,左滑列表功能
  14. 全球及中国地铁行业融资现状与十四五盈利前景分析报告2022版
  15. css设置height无效,CSS中设置height:100%无效的解决方案
  16. TTL和CMOS电平比较
  17. bzoj 2054 并查集
  18. threejs 草场足球运动视角(三)
  19. 树根 Digital root
  20. web练手--抽卡模拟器(1)

热门文章

  1. IMO模型编程思维法(Input-Model-Output)
  2. 夜的钢琴曲五—吉他指弹
  3. Python eval()和exec()函数详解
  4. 海湾主机汉字注释表打字出_海湾消防主机字根表-海湾消防主机
  5. mac 电脑软件安装常见的问题
  6. console口设置登录密码
  7. hive的dual表
  8. java中valueof_JAVA中intValue()和ValueOf()什么意思,还有Value什么意思
  9. java线程池——逐步分析
  10. kubernetes笔记