首页 > 编程语言 >XMU《UNIX 系统程序设计》第五次实验报告(编制模拟“五个哲学家”问题的程序)

XMU《UNIX 系统程序设计》第五次实验报告(编制模拟“五个哲学家”问题的程序)

时间:2024-04-28 09:24:50浏览次数:25  
标签:fork 哲学家 int 叉子 void UNIX XMU forks 实验报告

想知道第三、四次实验去哪儿了吗?我也想知道。

实验五 编制模拟“五个哲学家”问题的程序

一、实验内容描述

编制模拟“五个哲学家”问题的程序

目的

学习和掌握并发进程同步的概念和方法。

要求

  1. 程序语法

    philosopher   [ -t  <time> ]
    

    <time> 是哲学家进餐和沉思的持续时间值,缺省值为2秒。

  2. 五个哲学家的编号为0~4,分别用五个进程独立模拟。

  3. 程序的输出要简洁,仅输出每个哲学家进餐和沉思的信息。例如,当编号为3的哲学家在进餐时,就打印:

philosopher 3 is eating

而当他在沉思时,则打印:

philosopher 3 is thinking

除此之外不要输出其他任何信息。
4. 利用课堂已教授的知识而不使用线程或IPC机制进行同步。
5. 程序应该一直运行,直到人为地终止它(按 Ctrl-CCtrl-\)。不允许出现僵尸进程。
6. 12月15日24点为实验完成deadline。

二、设计与实现

哲学家问题是这样的,假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事情之一:吃饭,或者思考。吃东西的时候,他们就停止思考,思考的时候也停止吃东西。餐桌上有五碗意大利面,每位哲学家之间各有一只餐叉。因为用一只餐叉很难吃到意大利面,所以假设哲学家必须用两只餐叉吃东西。他们只能使用自己左右手边的那两只餐叉。

这个实验的目标就是使用并发进程同步的方法,模拟他们就餐的顺序。

程序的框架设计很明确,实验文档中也已经给出来了相关的代码:在主程序中,我们调用 fork 建立五个子进程,每个子进程代表一个哲学家。

在每个子进程中,我们使用一个死循环,重复进行思考、拿起叉子、就餐、放下叉子……其中,思考、就餐两个步骤我们只需要输出一条 philosopher i is eating/thinking,然后进行 sleep 即可。

放下叉子这个时间很容易,没有什么争议,只需要依次方向两只手上的叉子即可,对应在程序中就是解除两个锁。

对于拿起叉子这个事件,实验文档中也给出了设计:先拿起一只手边的叉子,再拿起另一只手的叉子。值得注意的是,对于 \(0\) 到 \(3\) 号哲学家,他们拿起叉子的顺序是先拿起左手边的叉子(forks[i]),再拿起右手边的叉子(forks[i + 1]);而对于 \(4\) 号哲学家,他拿起叉子的顺序是先拿起右手边的叉子(fork[0]),再拿起左手边的叉子(fork[4])。

这样做可以避免出现死锁。考虑到,只有两种情况会出现死锁:在某一时刻,每一个科学家都拿起了左手边的叉子,而没有拿起右手边的叉子;都拿起了右手边的叉子,没有拿起左手边的叉子。这时每一个哲学家都将会去等待另一只手边叉子被解锁,而又不愿意放下自己手中的叉子,导致所有科学家都在不停地等待,从而产生死锁。

上面的设计保证了锁之间的偏序,加锁的顺序不会产生环路,因此不可能存在所有科学家都一只手有叉子,一只手没有叉子的情况。

现在的问题来到怎么实现锁。

因为这个实验只能使用文件作为锁,不能使用线程和 IPC,所以我们考虑到可以以文件被创建和被删除作为锁的状态。

我们规定一个文件 forki 存在,代表叉子 \(i\) 被加了锁;如果不存在,则代表这个叉子没有被加锁。

然后,我们只需要在需要取用锁的时候,轮询判断 forki 是否存在即可。如果存在,我们就继续循环,如果不存在我们就创建文件,然后跳出自旋轮询执行后面的代码。

在我期初的设计中,我使用的是 open(fork, O_RDONLY) 来判断文件是否存在,使用 creat() 系统调用来创建文件。这导致了判断和创建的分离,从而引起了一个竞争问题:如果一个进程 \(i\) 在判断 forki 不存在以后被调度成了进程 \(i + 1\),这个时候进程 \(i + 1\) 也判断 forki 不存在,于是它创建了 forki 文件。如果进程 \(i\) 再次被调度,那么导致其继续执行,于是它重复创建了文件 forki,这就意味着两个进程同时获得了 forki 的锁,从而引起错误。

于是,我发现可以使用 open(fork, O_CREAT | O_EXCL) 来创建文件,O_EXCL 参数使得文件判断和创建合并成了一个原子操作,如果文件已存在则返回错误,如果不存在则自动创建,故而避免了上述问题。

void lock(char* fork) {
    int fd;
    while (1) {
        if ((fd = open(fork, O_CREAT | O_EXCL, 0)) >= 0) {
            close(fd);
            break;
        }
    }
}

void unlock(char* fork) {
    if (access(fork, F_OK) == 0)
        unlink(fork);
}

完整的代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>

#define N 5

static char* forks[N] = { "fork0", "fork1", "fork2", "fork3", "fork4" };
static int nsecs;

void philosopher(int i);
void takeFork(int i);
void putFork(int i);
void thinking(int i, int nsecs);
void eating(int i, int nsecs);
void lock(char* fork);
void unlock(char* fork);

int main(int argc, char* argv[]) {
	if (argc == 1)
		nsecs = 2;
	else if (argc == 3 && strcmp(argv[1], "-t") == 0)
		nsecs = atoi(argv[2]);
	else {
		fprintf(stderr, "Usage: %s [-t <time>]\n", argv[0]);
		exit(1);
	}

    for (int i = 0; i < N; ++i)
        unlock(forks[i]);

	for (int i = 0; i < N; ++i) {
		int pid = fork();
		if (pid == 0) {
			philosopher(i);
			exit(0);
		}
	}
	wait(NULL);

    return 0;
}

void lock(char* fork) {
    int fd;
    while (1) {
        if ((fd = open(fork, O_CREAT | O_EXCL, 0)) >= 0) {
            close(fd);
            break;
        }
    }
}

void unlock(char* fork) {
    if (access(fork, F_OK) == 0)
        unlink(fork);
}

void philosopher(int i) {
	while (1) {
		thinking(i, nsecs);
		takeFork(i);
		eating(i, nsecs);
		putFork(i);
	}
}

void takeFork(int i) {
	if (i == N - 1) {
		lock(forks[0]);
		lock(forks[i]);
	} else {
		lock(forks[i]);
		lock(forks[i + 1]);
	}
}

void putFork(int i) {
	if (i == N - 1) {
		unlock(forks[0]);
		unlock(forks[i]);
	} else {
		unlock(forks[i]);
		unlock(forks[i + 1]);
	}
}

void thinking(int i, int nsecs) {
    printf("philosopher %d is thinking\n", i);
    sleep(nsecs);
}

void eating(int i, int nsecs) {
    printf("philosopher %d is eating\n", i);
    sleep(nsecs);
}

三、实验结果

1. 编译程序

gcc philosopher.c -o philosopher -std=c99

2. 运行截图

image-20231201163053191

image-20231201163142233

四、实验体会

这次的实验让我深入理解了并发进程同步的概念和方法,通过模拟“五个哲学家”问题,我更加熟悉了在进程间进行同步操作的重要性。在实验过程中,我遇到了一些挑战,但通过仔细思考和调试,最终成功完成了任务。

这次实验让我对哲学家问题有了更深入的理解。通过构建一个围坐在圆形餐桌旁的场景,每个哲学家都有思考和进餐两个行为,而叉子则成为同步的关键。这种抽象的问题在计算机科学中很有代表性,通过实际编码实现,我更好地理解了理论概念在实践中的应用。

在实验中,锁的设计成为了一个关键点。由于不能使用线程或 IPC 机制,我采用了文件作为锁的形式。通过 open(fork, O_CREAT | O_EXCL) 的方式,我确保了判断和创建文件的原子性,有效避免了竞争问题。这让我认识到在并发编程中,同步机制的设计和实现需要仔细考虑,以防止出现不确定的行为。

另外,通过实现整个过程,我对操作系统中的进程控制有了更深刻的认识。使用 fork 系统调用创建五个独立的子进程,每个代表一个哲学家,这种并发的设计增加了程序的复杂性,但也提高了系统的效率。

实验的难点主要在于正确处理锁的逻辑,以及保证程序不出现僵尸进程。通过不断地思考和调试,我逐渐解决了这些问题,确保了程序的正确性和稳定性。实验文档中对于程序输出的简洁要求也锻炼了我的编程规范意识,使得输出信息更加清晰明了。

总的来说,通过这次实验,我不仅学到了并发进程同步的具体实现方式,还提高了对操作系统中进程控制的理解。同时,通过遇到问题并逐一解决,我培养了解决实际编程难题的能力。这次实验是我在学术和实践中的一次全面提升,让我更加深入地了解了操作系统的底层原理和并发编程的精髓。

标签:fork,哲学家,int,叉子,void,UNIX,XMU,forks,实验报告
From: https://www.cnblogs.com/hankeke303/p/18162986/XMU-UNIX-Lab5

相关文章

  • XMU《UNIX 系统程序设计》第六次实验报告(信号处理)
    实验六信号处理完整程序可以在这里下载:点击下载。一、实验内容描述实验目的学习和掌握信号的处理方法,特别是sigaction,alarm,sigpending,sigsetjmp和siglongjmp等函数的使用。实验要求编制具有简单执行时间限制功能的shell:myshell[-t<time>]这个测试程序的功......
  • XMU《计算机网络与通信》第二次实验报告
    实验二实验报告一、个人信息姓名:XXX学号:XXXXXXXXXXXXXX二、实验目的学习捕获和分析网络数据包掌握以太网MAC帧、802.11数据帧和IPv4数据包的构成,了解各字段的含义掌握ICMP协议,ping和tracert指令的工作原理掌握ARP协议的请求/响应机理三、实验内容与结果分析......
  • XMU《计算机网络与通信》第四次实验报告
    计算机网络实验四通信这次实验的话,我的报告参考意义不大,毕竟这次实验的主要难点是完成那两个代码,但是我报告中没有任何对于代码的解释。大家如果需要的话,我的两个代码可以在这里下载,仅供参考:点击下载。一、个人信息姓名:XXX学号:XXXXXXXXXXXXXX二、实验目的理解和掌握ARP......
  • XMU《计算机网络与通信》第五次实验报告
    实验五运输层与应用层协议分析如果需要Wireshark捕获到的数据,可以在这里下载,这里面应该还有最后一个任务的两个代码:点击下载。目录实验五运输层与应用层协议分析一、个人信息二、实验目的三、实验内容、步骤与结果任务一TCP正常连接观察任务二异常传输观察分析1.尝试连......
  • 20211317 李卓桐 Exp5 信息搜集与漏洞扫描 实验报告
    Exp5信息搜集与漏洞扫描实验报告1、实践目标掌握信息搜集的最基础技能与常用工具的使用方法。2、实践内容(1)各种搜索技巧的应用(2)DNSIP注册信息的查询(3)基本的扫描技术:主机发现、端口扫描、OS及服务版本探测、具体服务的查点(以自己主机为目标)(4)漏洞扫描:会扫,会看报告,会查漏......
  • inode(index node)是Unix、Linux和类Unix操作系统中的一个重要概念, 在Windows操作系统中
    inode(indexnode)是Unix、Linux和类Unix操作系统中的一个重要概念,用于描述文件系统中的文件或目录。每个文件或目录都与一个inode相关联。inode包含以下信息:文件或目录的权限(读、写、执行)。文件类型(普通文件、目录、符号链接等)。拥有者和所属组。文件的大小。访问、修......
  • delphi Unix时间戳 转yyyy-mm-dd hh:mm:ss 格式字符串
    functionUnixTimeStampToDateTimeStr(UnixTimeStamp:Int64):string;varDateTimeValue:TDateTime;begin//第二个参数默认为true,设置为false,会默认以本地时区来+8小时,因为mysql里村的utc时间秒数DateTimeValue:=UnixToDateTime(UnixTimeStampdiv1000,False......
  • XMU《UNIX 系统程序设计》第二次实验报告
    一、实验内容描述实验目的掌握与文件和目录树有关的系统调用和库函数。实验要求编写程序myfind命令语法myfind<pathname>[-comp<filename>|-name<str>...]命令语义(1)myfind<pathname>的功能除了具有与程序4-7相同的功能外,还要输出在<pathname>目录子树之下,文......
  • XMU《计算机网络与通信》第三次实验报告
    一、个人信息学号:**************姓名:###二、实验目的理解TCP和UDP协议主要特点掌握socket的基本概念和工作原理,编程实现socket通信三、实验任务与结果任务1前置任务开启两个终端窗口,分别编译、运行server_example.c和client_example.c,观察它们实现的功能。......
  • 20231325 贾罗祁 实验三《Python程序设计》实验报告
    20231325贾罗祁2023-2024-2《Python程序设计》实验三报告课程:《Python程序设计》班级:2313姓名:贾罗祁学号:20231325实验教师:王志强实验日期:2024年4月17日必修/选修:公选课1.实验内容创建服务端和客户端,服务端在特定端口监听多个客户请求。客户端和服务端通过Socket套......