1.引言
随着计算机技术的快速发展,在计算机操作系统中,如何优化任务处理效率成为一大难题。通过一个简单多用户文件系统设计,加深对文件系统的理解及内部实现。运用多线程的调度尽可能的优化其效率,解决实际问题。
1.1任务要求
B类:用java语言模仿“生产者——消费者”问题
1.通过Java语言中的wait()和notify()命令模拟操作系统中的P/V操作;
2.为每个生产者/消费者产生一个线程,设计正确的同步算法
3.每个生产者和消费者对有界缓冲区进行操作后,即时显示有界缓冲区的 当前全部内容、当前指针位置和生产者/消费者线程的自定义标识符。
4.生产者和消费者各有两个以上。
5.多个生产者或多个消费者之间须共享对缓冲区进行操作的函数代码。
C类:文件系统
设计一个多用户文件系统,理解文件系统的层次结构,完成基本的文件系统create、open、close、read/write等基本功能,并实现文件保护操作。实现以此为基础加入自己设计功能的小型文件系统 。
需要解决的问题:
1.如何让生产者在缓冲区满时不加数据。
2.如何让消费者在缓冲区空时不取数据。
3.如何使多个生产者或消费者线程的处理不发生冲突。
4.如何实现对文件系统的保护。
1.2选题
B类:用java语言模仿“生产者——消费者”问题;
C类:文件系统
当前发展概述
所谓多线程的并发运行,其实是指从宏观上看,各个线程轮流获得CPU的使用权,分别执行各自的任务。在运行池中,会有多个处于就绪状态的线程在等待CPU,JAVA虚拟机的一项任务就是负责线程的调度,线程调度是指按照特定机制为多个线程分配CPU的使用权。Java虚拟机采用抢占式调度模型,是指优先让可运行池中优先级高的线程占用CPU,如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU。
文件系统是操作系统用于明确存储设备或分区上的文件的方法和数据结构;即在存储设备上组织文件的方法。操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。文件系统由三部分组成:文件系统的接口,对对象操纵和管理的软件集合,对象及属性。从系统角度来看,文件系统是对文件存储设备的空间进行组织和分配,负责文件存储并对存入的文件进行保护和检索的系统。具体地说,它负责为用户建立文件,存入、读出、修改、转储文件,控制文件的存取,当用户不再使用时撤销文件等。
可行性分析
对于运用Java语言模仿“生产者——消费者”问题,以及在Java虚拟机上的线程调度问题,以前学习过Java基础语法,掌握了Java中wait(),notify()等线程处理的方法。
对于设计多用户文件系统问题,小文件的高度并发访问性能低。当前很多服务不仅需要大量的大文件访问,而且需要小文件的并发访问。文件系统在访问一个文件时需要多次读写和多次网络来回,因此对低延迟的要求难度较高,需要深入钻研。
选题意义
在Java虚拟机上,通过运用Java语言模仿“生产者——消费者”问题,可以进一步加深我们对多线程方面知识的理解。这不仅可以巩固以前java所学的知识,还能进一步打好有关线程知识的基础。
通过设计一个多用户的“文件系统”,从中我们能更深理解文件系统的层次结构和内部实现,并可以熟悉文件系统create、open、close、read/write等基本操作,实现文件保护的同时,运用操作系统的所学知识加入自己设计的操作系统,由此实现对文件系统的创新。
2.需求分析与设计
2.1 需求分析
“生产者消费者”问题是研究多线程程序时绕不开的经典问题之一,它描述是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品。解决生产者/消费者问题的方法可分为两类:(1)采用某种机制保护生产者和消费者之间的同步;(2)在生产者和消费者之间建立一个管道。第一种方式有较高的效率,并且易于实现,代码的可控制性较好,属于常用的模式。第二种管道缓冲区不易控制,被传输数据对象不易于封装等,实用性不强。
同步问题核心在于:如何保证同一资源被多个线程并发访问时的完整性。常用的同步方法是采用信号或加锁机制,保证资源在任意时刻至多被一个线程访问。Java语言在多线程编程上实现了完全对象化,提供了对同步机制的良好支持。在Java中一共有五种方法支持同步,其中前四个是同步方法,一个是管道方法。
1.wait() / notify()方法
2.await() / signal()方法
3.BlockingQueue阻塞队列方法
4.Semaphore方法
5.PipedInputStream / PipedOutputStream
生产者和消费者在同一时间段内共用同一个存储空间,如下图所示,生产者向空间里存放数据,而消费者取用数据,如果不加以协调可能会出现以下情况:
存储空间已满,而生产者占用着它,消费者等着生产者让出空间从而去除产品,生产者等着消费者消费产品,从而向空间中添加产品。互相等待,从而发生死锁。
要实现多用户文件系统,首先要实现Login用户登录,必须登录成功才能进入文件系统进行相应操作,进入后,通过树状展示文件目录,右侧显示文件夹图标或这文件图标,选中节点鼠标右键弹出菜单,有新建目录,新建文件,删除文件,打开文件,修改文件,并分别实现其功能。
2.2系统框架和流程
“生产者”与”消费者”问题流程图:
文件系统流程图:
用户登录模块:
新建文件模块:
修改文件模块:
删除文件模块:
2.3 系统流程和模块描述
本系统为多用户的文件系统,因为是多用户使用,所以具备登录系统,注册用户的功能,各个用户之间的文件系统互不干扰,同时又要实现对文件增删改查的功能。采用二级目录,第一级为用户账号,第二级对应用户账号下的文件。
系统采用结构体存储用户,文件目录等内容。
系统大致为一个二级文件系统,可以实现以下几个功能::login(用户登录)、view(查看文件内容)、create(新建文件)、delete(删除文件)、modify(修改文件)、dir(列文件目录)。
用户登录模块:
首先,刚进入系统,还不会有文件系统,显示用户登录模块。系统分区后,系统初始化要完成文件系统的建立。验证用户的身份,输入用户名和密码,登录成功后会初始化当前用户文件系统中的信息。如果用户没有通过身份验证,则提示登录失败,退出系统。
创建文件模块:
在用户登录后才具有的功能,查找一个未使用的文件块用来存放用户的文件信息,构建一个文件系统的元素放入找到的文件块中。新建文件时要求输入文件名称,当文件名称不与已存在的文件目录中名称冲突时,同时文件不发生越界,则文件创建成功。
删除文件模块:
在文件目录中选中要删除的文件,若文件存在则继续判断文件是否被锁定。如果文件正使用处于锁定状态则删除失败。若处于非锁定状态,则删除成功。
查看文件模块:
首先要接受查看文件的名称,查询文件是否存在,如果不存在,则无法查看文件。若文件存在,则创建查看文件的结点并复制文件信息,即可查看到用户文件的数据部分,将数据显示到界面。
3.数据结构
文件系统数据结构:
typedef struct UFD //存储文件信息
{
char name[10]; //文件名
int attribute; //属性
int length; //长度
int a[10]; //为文件本身分配10个空间
int *p1; //一级索引,100个空间
int (*p2)[100]; //二级索引,100*100个空间
struct UFD *next;
}UFD;
typedef struct DIR //存储目录信息
{
DIR* above; //上一结点
char name[10];
int length;
DIR *next; //下一结点
UFD *File_head; //此目录下的文件指针
DIR *Dir_head; //此目录下目录链表指针
}DIR;
class test //定义管理用户目录的类
{
DIR *now; //当前目录
UFD *Fhead; //文件的头结点
DIR *Dhead; //根目录的目录链头结点
char code[10]; //密码
char name[10]; //用户名
int length; //用户空间大小
int status; //是否获得空间
public:
void set_status(int);
int dele_user();
int dis_file(); //显示文件所占外存块号
int dis_dir(DIR *d);//当前路径
int get_length();
char const *get_name();
char const *get_code();
int get_status();
int set_user(char *,char *);//设置用户名与密码
DIR *get_now();
int dele_file(UFD *f); //删除文件
int dele_dir(DIR*); //删除目录
Test(); //构造
~test(); //析构
int goback(); //返回上级
int dis_now(); //显示当前目录
int new_file();
int new_dir();
int open_dir();
int open_file();
int first_dele_file(); //删除文件的前部分工作
int first_dele_dir(); //删除目录的前部分工作
int set_code();
};
class Cdisk{
//用户类
public:
Test user[5]; //用户个数最多为5
char code[10];
int dis_disk();
int first_dele_user();
int dele_user(int);
int new_user(); //查看当前用户与外存空间使用情况,后创建新用户
int set_code(); //设置新密码
int login(char); //登陆
Cdisk();
virtual~Cdisk(); //虚函数,析构
};
4.关键技术
Java实现生产者与消费者问题:
生产者消费者问题,描述一组生产者向一组消费者提供产品信息。它们会共享一个有界的缓冲区,生产者向其放入产品信息,消费者从中取出产品信息。只要当前缓冲区未满,生产者就可放入产品消息,只要缓冲区中存有数据,消费者便可提取。
应满足以下两个同步条件:
1.仅仅在有缓冲池中至少有一个缓冲区已存入消息后,消费者才从中提取消息,否则消费者必须要等待。
2.仅仅有缓冲池至少有一个缓冲区为空,生产者才能将消息放入缓冲区,否则生产者必须等待。
使用
1.创建生产者,负责生产产品信息
2.创建消费者,负责消费信息
3.消费者类
4.生产者类
5.核心代码
public class ProductiveConsumption {
private int front=0; //队头
private int next=0; //队尾
private int bufferLength; //缓冲区大小
private String buffer[]; //缓冲区
private int emptyNum;
public ProductiveConsumption(int bufferLength){
this.bufferLength=bufferLength;
buffer=new String[bufferLength];
emptyNum=bufferLength;
}
//生产
public synchronized void produce(String data){
System.out.println("生产前,空缓冲区数目-----------"+emptyNum);
System.out.println("***生产者正在生产"+data);
while(full()){
System.out.println("*****缓冲池已满,生产等待");
try{
this.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
this.notify();
buffer[next]=data;
next=(next+1)%bufferLength;
System.out.println("****生产者成功生产:"+data);
emptyNum--;
System.out.println("生产后,空缓冲区数目-----------"+emptyNum);
}
//消费
public synchronized void consum(){
System.out.println("消费前,空缓冲区数目-----------"+emptyNum);
while (empty()){
System.out.println("*****缓冲池为空,消费等待");
try{
this.wait();
} catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("***消费者正在消费"+buffer[front]);
this.notify();
System.out.println("****消费者成功消费:"+buffer[front]);
front=(front+1)%bufferLength;
emptyNum++;
System.out.println("消费后,空缓冲区数目-----------"+emptyNum);
}
//缓冲区是否已满
public boolean full(){
if(emptyNum==0){
return true;
}
return false;
}
//缓冲区是否为空
public boolean empty(){
if(bufferLength==emptyNum){
return true;
}
return false;
}
}
使用synchronized对存储加锁,然后用objec原生的wait()和notify()做同步。通过加锁,限制同一时刻只能有一个读或写。
Wait()方法:当缓冲区已满或空时,生产者/消费者线程就会停止自己的执行,放弃锁,使自己处于等待状态,让其他线程执行。
Notify()方法:但生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。
6.测试类
文件系统:
空闲空间管理采用位示图法,文件目录采用多级目录使用,采用索引节点结构。
功能模块
1.创建文件
p=new UFD; //首先开辟一个新的文件结构体
在根目录链中检查判断文件是否重名
cout<<"请输入建立的文件名:";
cin>>p->name;
if(now==0) //根目录下的文件链
f=Fhead;
else //当前目录下的文件链
f=now->File_head;
while(f!=0) //检查是否文件重名
{
if(!strcmp(p->name,f->name))
{
cout<<"此文件已存在!"<<endl;
return 0; //退出
}
f=f->next;
}
若空间不足,则无法执行创建操作:
if(p->length>disk_empty) //空间不足
{
cout<<"文件太大,空间不足,当前空间为:"<<disk_empty<<endl;
delete p;
return 0;
}
若空间剩余,则通过位示图法为其分配空间:
disk_empty=disk_empty-p->length; //剩余空闲盘块
//
for(i=0;i<p->length&&i<10;i++) //文件较小时,直接地址,文件数据盘块号
for(j;j<10000;j++) //位示图法
if(disk_block[j]==0) //空闲
{
p->a[i]=j; //得到此空间
disk_block[j]=1; //标记为已分配出去
j++;
break; //跳出寻找,为文件分配下一空间
}
2.删除文件
int test::first_dele_file() //删除文件的前面工作
{
char temp[10],a[5];
cout<<"你要删除的文件名:"<<endl;
cin>>temp;
UFD *f=Fhead; //数据文件头指针
UFD *above=0;
if(now!=0)
f=now->File_head; //当前目录下的数据文件
while(f!=0)
{
if(!strcmp(f->name,temp))
break; //找到,跳出
above=f; //标记第一个文件
f=f->next;
}
if(f==0)
{
cout<<"此文件不存在"<<endl;
return 0;
}
cout<<"确定删除"<<f->name<<"文件吗?按0确定,其他键取消"<<endl;
cin>>a;
if(a[0]!='0')
{
cout<<"已取消删除文件。"<<endl;
return 0;
}
disk_empty+=f->length; //回收此数据文件的空间大小
if(now==0) //根目录
{
if(f==Fhead)
Fhead=Fhead->next;
else
above->next=f->next;
}
else
{
DIR *d=now;
while(d!=0)
{
d->length=f-