操作系統實驗六、死鎖問題實驗——單車道問題

問題描述

在兩個城市南北方向之間存在一條鐵路,多列火車可以分別從兩個城市的車站排隊等待進入車道向對方城市行駛,該鐵路在同一時間,只能允許在同一方向上行車,如果同時有相向的火車行駛將會撞車。請模擬實現兩個方向行車,而不會出現撞車或長時間等待的情況。您能構造一個管程來解決這個問題嗎?

程序實現

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

可以看到運行結果
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章