报告较大,分成几部分发
目录
5. 运行结果
5.1 运行环境
“生产者-消费者”问题的开发过程使用到了Windows10系统、IntelliJ IDEA 2021.3.1;多用户文件系统的开发过程使用到了Windows10系统、Qt Creator 8.0.1 (Community)。其中系统CPU主频为1GHz,内存为16.0GB。
系统运行环境表见表5-1所示。
表5- 1运行环境表
运行的硬件环境 | X86 指令集CPU 4GB以上内存 |
运行的操作系统 | Xp,Win7,Win8, Win8.1,Win10等 |
可执行文件运行 | ex4j |
5.2 服务模式
“生产者-消费者”问题采用仿Linux操作系统的终端控制服务模式,多用户文件系统采用类Windows操作系统的页面交互服务模式。
多用户文件系统利用多个类之间的包含关系来模拟磁盘、目录、文件等对象的包含关系,建立面向对象的编程思想,通过C++的IO流实现文件的读写功能。并且,利用.csv文件对用户账号、密码进行存储,利用文本对目录和文件的信息进行保存,便于读取和管理。最后.使用QT绘制登陆注册界面和主界面,实现文件系统的可视化,提高可用性和人机交互性。
5.3 运行结果
5.3.1 “生产者-消费者”问题
为更好描述“生产者-消费者”问题,使用现实生活中商品生产与消费的场景模拟该进程问题。用户可以输入生产者和消费者的数量。不同商品生产者有对应的生产者进程的线程编号,同理不同消费者也有对应的消费者进程下的线程编号。同时,对商品编号处理,以更好地区分缓冲区中的生产消费操作。
不同生产者生产商品,并给其编号放入缓冲区中;生产一定数量后生产商停止生产;并行的,消费者购买仓库中的商品,若消费者进程结束停止购买。
5.3.1.1 初始化
用户可以对生产者和消费者的数量、缓冲区的大小进行初始化。
5.3.1.2 运行结果及分析
根据用户初始化定义,生产者对应线程Thread-0 ~ Thread-2,消费者对应线程Thread-3 ~ Thread-4。
时间片0-3时,生产者Thread-0到达,生产产品R2577、R7456、R1507,依次从尾部存入缓冲区。
时间片4时,生产者Thread-0停止生产,进程结束。在时间片4-5,消费者Thread-4到达,从缓冲区头部依次拿取商品R2577、R7456。在时间片6时,消费者Thread-3到达,从缓冲区头部拿取商品R1507。
时间片7时,缓冲区为空,消费者Thread-3进入阻塞状态,等待后续唤醒。时间片7-9时,生产者Thread-1到达,生产产品R33、R5412、R3462,依次从尾部存入缓冲区。
时间片10时,生产者Thread-1停止生产,进程结束;生产者Thread-2到达,生产产品R260,存入缓冲区尾部。
时间片11时,生产者Thread-2仍要继续生产,但此时缓冲区已满,无法存入新的数据,故Thread-2进入阻塞状态,等待后续唤醒。此时唤醒消费者Thread-3,使其退出阻塞状态,购买缓冲区头部商品R33。
时间片12时,消费者Thread-3继续消费,购买缓冲区头部商品R33。时间片13时,消费者Thread-4到达,购买缓冲区头部商品R3462。
时间片14时,消费者Thread-3、消费者Thread-4进程结束,结束消费。生产者Thread-2被唤醒,生产商品R1941并存入缓冲区尾。
时间片15时,生产者Thread-2继续生产,将商品R5844存入缓冲区尾。时间片16时,生产者Thread-2停止生产,进程结束。此时,所有进程结束,程序结束。
5.3.2 多用户文件系统
5.3.2.1 用户注册
用户输入用户名和密码进行注册,当用户名已经被使用过时,提示“用户已存在!”。
图5.1 用户注册-用户已存在 图5.2 用户注册-注册成功
5.3.2.2 用户登录
用户输入用户名和密码进行登录。当输入的用户名没有被注册过时,提示“用户不存在!”;当输入的用户名存在但密码并不匹配时,提示“密码错误!”;当输入的用户名存在且密码正确时,提示“登陆成功!”。
图5.3 用户登录-用户不存在 图5.4 用户登录-密码错误 图5.4 用户登录-登陆成功
5.3.2.3 展示目录列表
用户成功登陆后,进入多用户文件系统主界面。此时,默认展示根目录下的目录列表内容。后续如对系统进行增加目录/文件、删除目录/文件操作等操作后,点击左列表处的刷新按钮,即可对目录列表进行刷新展示。
图5.5 目录列表展示
5.3.2.4 添加目录
点击左列表处的添加目录按钮(上数下第二个按钮),系统弹出创建目录弹窗,用户根据提示输入目录名,此处为“test1”,点击ok按钮,再次刷新目录列表,即可看到新创建的目录。
图5.6 添加目录
图5.7 添加目录结果
5.3.2.5 添加文件
点击右列表处的添加文件按钮(上数下第二个按钮),系统弹出添加文件弹窗,用户根据提示输入文件名和文件模式,此处为“txt1”和“r(只读模式)”,点击ok按钮,再次刷新目录列表,即可看到新创建的文件。
图5.8 添加文件
图5.9 添加文件结果
5.3.2.6 删除目录
选择要删除的目录项所在行,点击左列表处的删除目录按钮(倒数第一个按钮),即可看实现目录删除。此处选择test1目录项删除,效果如下。
5.3.2.7 删除文件
选择要删除的文件项所在行,点击右列表处的删除目录按钮(倒数第一个按钮),即可看实现文件删除。此处选择txt1文件项删除,效果如下。
5.3.2.8 打开文件
选择要打开的文件项所在行,点击左列表处的打开目录按钮(上数下第三个按钮),即可打开该文件查看该目录下的目录列表。此处选择“第一个目录”文件项,点击打开按钮,即可查看该目录下的目录列表。
5.3.2.9 返回上一级
点击左列表处的返回上一级目录按钮(上数下第三个按钮),即可返回当前所在目录项的父目录列表。
5.3.2.10 读文件
选中想要打开的文件所在行,点击右列表处的读写按钮(上数下第一个按钮),即可查看该文件的内容。
(1)可读可写文件
(2)只读文件
为直接便于区分只读文件和可读可写文件,设置了只读文件无内容。
5.3.2.11 写文件
选中想要打开的文件所在行,点击右列表处的读写按钮(上数下第一个按钮),即可查看该文件的内容。直接在文本框区域对内容进行修改,再点击保存,即可实现写文件操作。
6. 调试和改进
6.1 “生产者-消费者”问题
6.1.1 问题一
问题:在进行“生产者——消费者”问题的解决方案设计时,我设计了生产者类Producer、消费者类Consumer、主控类Controller,在主函数内接受输入的指定个数的生产者、消费者个数以及缓冲区大小,而后进行创建进程与线程代码测试。在原先,笔者试图把“生产”和“消费”两个操作都封装到主控类中,由缓冲区自动进行多线程操作,而Producer与Consumer负责从中获取相应的指针、资源数目封装成方法供Main函数调用。
解决方案:虽然代码编写上并没有难度,因为不是笔者自己实现底层操作系统的P()/V()操作而是通过调用Java内部提供的wait()/notifyAll()以及synchronized关键字实现互斥以及进程同步。但是上述设计存在严重问题就是,即使实现了操作,但是逻辑上这是不合理的需求分析设计,Producer和Consumer自己就是生产者和消费者,他们必须拥有“生产”和“消费”两个操作方法,而不是贪图便利统一封装到主控类Controller中,相反,主控类Controller只应负责被封装到生产者和消费者中的对于缓冲区指针读出和信息读出功能,将其封装成方法提供给Main函数,对调代码即可解决设计上的缺陷。
6.1.2 问题二
在该问题中,如果初始化MAX_SIZE设置得太小,很容易出现生产者消费者进程阻塞问题,即过快缓冲区饱和或者过快缓冲区为空。
同时,初始化生产者和消费者个数如果差值过大,也容易出现进程阻塞问题,即生产过多或者消费过快。
故算法中在notifyAll()调用结束唤醒线程后,可以适当加入sleep()函数线程休眠,控制进程运行速度,其代码如下所示。
1.list.notifyAll(); // 释放阻塞的线程
2. try {
3. Thread.sleep(500); // 线程休眠半秒以防速度过快
4. } catch (InterruptedException e) {
5. // TODO: handle exception
6. e.printStackTrace();
7. }
6.2 多用户文件系统
6.2.1 问题一:用户的存储
为实现用户注册后,在程序重新运行后依旧可以登录,本系统利用user.csv文件对用户数据进行存储。
判别输入的账号密码是否正确时,系统先通过access("user.csv",0)函数判断user.csv文件是否存在。若返回0,则文件存在,利用ifstream inFile("user.csv",ios::in); 读取文件,对每行数据利用getline(s,str,’’)进行数据切割,并将用户数据利用vector容器lineArray进行结构体存储。继而,通过遍历lineArray判断新注册账号密码是否正确。
6.2.2 问题二:文件目录的存储
文件和目录属于同一个类,只是type类型不同,每个文件和目录结点对应于两个文件inode和file,目录的inode文件下存放所有者id、模式、文件内容存放位置,目录的file文件下存放目录的拥有者、目录下的文件数、剩余目录项以及父目录,文件的inode文件下存放所有者id、模式、文件内容存放位置,文件的file文件下存放内容。这样把文本控制块内容和索引结点分开,可以在切换目录和多个目录下加快目录检索速度,有利于系统的控制和管理。
7. 心得与体会
7.1 结论和体会
经过本次实验,我对线程的工作原理又重新复习了一遍,对线程的工作机制有了更新的认识,完成了以Java语言为基础的“生产者-消费者”问题。
并且,对Linux文件系统进行了学习,在Linux文件系统中,一个文件除了自身的数据之外,还有一个附属信息,即文件的元数据(metadata)。这个元数据用于记录文件的许多信息,比如文件大小,拥有人,所属的组,修改日期等等。元数据并不包含在文件的数据中,而是由操作系统维护的。我设计的系统就是仿照Linux文件系统进行设计的,每个文件或目录对应两个文件,一个是存放的文本内容,另一个文件记录的是文件的相关属性,包括文件大小、拥有人、存放位置。这样设置不仅逻辑简单,读取文件结点也比较方便,同时文本控制块内容和索引结点分开,可以在切换目录和多个目录下加快目录检索速度,有利于系统的控制和管理。运用此思路,利用C++语言和QT框架完成了多用户文件系统。
7.2 进一步改进方向
对于“生产者-消费者”问题,我没有制作可视化效果来模拟产品的生产。后续可以利用商品进入仓库和离开仓库来模拟生产消费过程,此处需要对界面画笔以及其他组件也进行互斥控制和同步控制,以做出精确的动态效果。
7.3 分析设计方案对系统安全的影响
在多用户文件系统中,我设计了用户账号和密码的验证。不同用户间不能访问其他人的目录和文件,只能对自己的文件块进行操作,即只能查看自己的文件内容、对自己创建的文件进行读写操作等。
主要参考文献
- 张尧学,宋虹,张高.《计算机操作系统教程》[M].清华大学出版社,2013.
- 汤小丹,梁洪兵,哲凤屏.《计算机操作系统》[M].西安电子科技大学出版社,2014.
- 王育勒等.《计算机操作系统》[M],北京交通大学出版社,2004.
- Abraham Silberschatz, Peter Baer Galvin, Greg Gagne.Operating System Concepts[M].高等教育出版社,200.
- 陶永才,史苇杭,张青.《操作系统原理与实践教程》[M].北京:清华大学出版社,2015.
- 陈益.利用JAVA多线程并发机制解决生产者——消费者问题[J].电脑学习,2010(01):147-149.
- 彭娇.经典同步问题中死锁的解决——以生产者——消费者问题为例[J].科技资讯,2017,15(13):73-74.DOI:10.16661/j.cnki.1672-3791.2017.13.073.