设计时间: 2011-1-5至2011-1-7专业年级:08计科4班:一.设计目的:通过操作系统其中一个子系统的设计和实现,掌握Linux文件系统的基本原理、结构和实现方法,掌握Linux文件系统中文件的建立、打开、读/写、执行、属性等系统调用的使用,学会设计简单的文件系统并实现一组操作,以及学习文件系统的系统调用命令,提高对文件系统实现功能的理解和掌握。
同时,掌握操作系统设计的方法与技巧,增强系统软件设计的实际工作能力。
二.设计容:为LINUX 设计一个简单的二级文件系统。
本文件系统采用类似DOS系统的文件管理方式,每次调用该文件系统时,首先申请一定的存空间,然后对该存空间进行分配。
将申请到的空间划分为目录区,文件区;采用位示图进行空间管理,盘块的分配使用显示(FAT 表)的方式。
每次调用该文件系统时自动为其分配空间,并将上次操作的结果从硬盘上调入存;当结束调用时则将操作的结果重新存入硬盘,以便下次调用。
(每次使用都会自动搜索文件系统,以此确定是否是第一次使用;若是则格式化生成文件系统,否则读取已存在的文件系统。
)三.设计过程1、实现功能该系统具备下列功能:login 用户登录logout 注销mkdir/md 创建目录rmdir/rd 删除目录cd/cd .. 修改目录creat 创建文件open 打开文件dir 显示当前目录和文件write 读文件 delete 删除文件close 关闭文件2、添加功能(1)制作了一个“操作命令符”列表框,说明接下来如何操作,这样有利于更好地阅读、操作和运行程序,使不懂得程序代码的人也可以运行该程序,更好地理解该程序实现的功能。
(2)在命令解释层函数cmdexp()里加了一些选择和操作功能,增加程序实现的功能,如原来程序只有显示当前目录和文件、创建目录和修改目录的功能,把它拓展到系统所要求的全部功能,并在原有的程序的基础上进行相应的修改,使程序更加完善。
3、设计思路(1)要将文件存储在磁盘上,必须为之分配相应的存储空间,并对文件存储空间进行管必须互斥地访问它,故在进入ialloc后,要先检查它是否已上锁,若是则睡眠等待;检查i结点栈空否。
若i结点栈中已无空闲结点编号,则应从盘中再调入一批i结点号进栈。
若盘中已无空闲i结点,则出错处理,返回;从空闲i结点编号栈中分配一i结点,并对它初始化、填写有关文件的属性;分配存i结点;将磁盘i结点总数-1,置超级块修改标志,返回。
回收过程ifree:当删除文件时,应回收其所占用的盘块及相应的磁盘i结点。
具体有:检查超级块上锁否。
若是,直接返回,即不把本次回收的i结点号记入空闲i结点编号栈中;检查i结点编号栈满否。
若已满,无法再装入新回收的i结点号,立即返回,若未满,便将回收的i结点编号进栈,并使当前空闲结点数+1;置超级块修改标志,返回。
⑤存索引结点的分配与回收分配过程iget:虽然iget用在打开文件时为之分配i结点,但由于允许文件被共享,因此,如果一文件已被其他用户打开并有了存i结点,则此时只需将i结点中的引用计数+1。
如果文件尚未被任何用户(进程)打开,则由iget过程为该文件分配一存i结点,并调用bread过程将其磁盘i结点的容拷贝到存i结点中并进行初始化。
回收过程iput:进程要关闭某文件时,须调用iput过程,先对该文件存i结点中的引用计数-1。
若结果为0,便回收该存i结点,再对该文件的磁盘i结点中的连接计数减1,若其结果也为0,便删除此文件,并回收分配给该文件的盘块和磁盘i结点。
(3)主要文件操作的处理过程①打开文件open:检索目录,核调用namei从根目录或从当前目录,沿目录树查找指定的索引结点。
若未找到或该文件不允许存取,则出错处理返回NULL,否则转入下一步;分配存索引结点,如果该文件已被其它用户打开,只需对上一步中所找到的i结点引用计数+1,否则应为被打开文件分配一存i结点,并调用磁盘读过程将磁盘i结点的容拷贝到存i结点中,并设置i.count=1;分配文件表项,为已打开的文件分配一文件表项,使表项中的f.inode 指向存索引结点;分配用户文件描述表项。
②创建文件creat:核心调用namei,从根目录或当前目录开始,逐级向下查找指定的索引结点。
此时有以下二种情况:重写文件,namei找到了指定i结点,调用free释放原有文件的磁盘块。
此时核忽略用户指定的许可权方式和所有者,而保持原有文件的存取权限方式和文件主。
最后打开。
新建,namei未找到。
调用ialloc,为新创建的文件分配一磁盘索引结点,并将新文件名及所分配到的i结点编号,写入其父目录中,建立一新目录项。
利用与open相同的方式,把新文件打开。
③关闭文件close:根据用户文件描述符fd,从相应的用户文件描述符表项中,获得指向文件表项的指针fp,再对该文件表项中的f.count-1。
4、算法和流程图(1)部分主要的算法:①主函数:#include <stdio.h>#include "filsys.h"struct hinode hinode[NHINO]; /* 查找存i节点的hash表 */struct filsys filsys; /* 超级块数据结构 */struct inode * cur_path_inode; /* 文件系统(存i节点)数据结构 */struct user user[USERNUM]; /* 用户打开表数据结构 */struct file sys_ofile[SYSOPENFILE]; /* 系统打开表数据结构 */struct direct cur_direct[NOFILE]; /* 目录数据结构路径 */unsigned short cur_dir_id; /* 当前目录指针 */char cur_path_name[DIRSIZ];FILE *fd; /* 本系统的所有文件指针 */void main(){char reg_or_log; // 注册/登录变量名char buf[50],buf2[50],buf3[50];int i;cur_dir_id = 0;printf( "\n\t\tWelcome to this system!!!\n" );printf( "initializing...\n" );fd = fopen( "filesystem.dat", "r+b" );if( fd != NULL ) /* 文件已经存在,不用格式化 */ {printf( "installing...\n" );init(); /* 读取磁盘数据 */}else /* 文件已经存在,要进行格式化 */ {if( format() == 0 ) /* 格式化 */return; /* 格式化不成功 */printf( "installing...\n" );init(); /* 读取磁盘数据 */}AGAIN:printf( "\nDo you want to register or login? (R/L) " );while( 1 ){reg_or_log = getch();/* 注册新用户 */if(( reg_or_log == 'r' ) || ( reg_or_log == 'R' )){printf( "%c\n", reg_or_log );reg();goto AGAIN;}/* 登陆已有用户 */else if(( reg_or_log == 'l' ) || ( reg_or_log == 'L' )){printf( "%c\n", reg_or_log );if( login() == 0) /* 登陆不成功 */goto AGAIN;break;}}strcpy( cur_direct[cur_dir_id].d_name, "/" );cur_direct[cur_dir_id].d_ino = ROOTDIR;strcpy( cur_path_name, "/" );printf("\n \t操作命令符\n"); for (i=0; i<=35; i++)printf(" *");printf("\n * ");printf(" 1----dir/l/ls 显示当前目录和文件 ");printf("2----cd ..退回到上一级目录 ");printf("*\n");printf("\n * ");printf(" 3----cd 文件名显示文件名目录 ");printf("4----mkdir/md 文件名创建目录 ");printf("*\n");printf("\n * ");printf(" 5----rmdir/rd 文件名删除目录 ");printf("6----mkfile/mf 文件名创建文件 ");printf("*\n");printf("\n * ");printf(" 7----open 文件名打开文件 ");printf("8----write 文件名写文件 ");printf("*\n");printf("\n * ");printf(" 9----read 文件名读文件 ");printf(" 10----close 文件名关闭文件 ");printf("*\n");printf("\n * ");printf(" 11----delete 文件名删除文件 ");printf(" 12----logout 注销 ");printf("*\n");for (i=0; i<=35; i++)printf(" *");printf("\n");while(1){if( cmdexp() == 1 )break;}halt();}②命令解释层函数:#include <stdio.h>#include <string.h>#include "filsys.h"char input_buf[20]; /* 命令行输入缓冲区 */static char str[20];int over; /* 命令行结束标记 */int fd1;int i,j;/* 命令解释层函数 */int cmdexp(){over = 0;printf("[%slocalhost%s]$ ", user[user_id].u_name, cur_path_name);getcmd();/* 显示当前目录 */if(( strcmp( input_buf, "dir" ) == 0 ) || ( strcmp( input_buf, "l" ) == 0 ) || ( strcmp( input_buf, "ls" ) == 0 )){_dir();clearbuf();return 0;}/* 改变当前目录 */if( strcmp( input_buf, "cd" ) == 0 ){getcmd(); /* 取得命令 */chdir ( input_buf ); //改变当前目录用函数clearbuf();return 0;}/* 创建目录(建立子目录) */if(( strcmp( input_buf, "mkdir" ) == 0 ) ||( strcmp( input_buf, "md" ) == 0 )){if( over ){printf( "请在mkdir后输入要创建的目录名\n" );clearbuf();return 0;}getcmd();if( input_buf[0] == '\0' ){printf( "请在mkdir后输入要创建的目录名\n" );clearbuf();return 0;}mkdir( input_buf );while(!over){getcmd();if( input_buf[0] != '\0' )mkdir( input_buf );}clearbuf();return 0;}if(( strcmp( input_buf, "rmdir" ) == 0 ) || ( strcmp( input_buf, "rd" ) == 0 )) {if( over ){printf( "请在rmdir后输入要删除的目录名" );clearbuf();return 0;}getcmd();if( input_buf[0] == '\0' ){printf( "请在rmdir后输入要删除的目录名\n" );clearbuf();return 0;}rmdir( input_buf );while( !over ){getcmd();if( input_buf[0] != '\0' )rmdir( input_buf );}clearbuf();return 0;}if( strcmp( input_buf, "mkfile" ) == 0 || ( strcmp( input_buf, "mf" ) == 0 )) {if( over ){printf( "请在mkfile后输入要创建的文件名\n" );clearbuf();return 0;getcmd();if( input_buf[0] == '\0' ){printf( "请在mkfile后输入要创建的文件名\n" );clearbuf();return 0;}creat( input_buf,01777);while(!over){getcmd();if( input_buf[0] != '\0' )creat( input_buf,01777);}clearbuf();return 0;}if( strcmp( input_buf, "open" ) == 0 ){if( over ){printf( "请在open后输入要打开的文件名\n" );clearbuf();return 0;}getcmd();if( input_buf[0] == '\0' ){printf( "请在open后输入要打开的文件名\n" );clearbuf();return 0;}open( input_buf,0004);while(!over){getcmd();if( input_buf[0] != '\0' )open( input_buf,0004);}clearbuf();return 0;}if( strcmp( input_buf, "write" ) == 0 ){if( over )printf( "请在write后输入要执行“写”操作的文件名\n" );clearbuf();return 0;}getcmd();if( input_buf[0] == '\0' ){printf( "请在write后输入要执行“写”操作的文件名\n" );clearbuf();return 0;}fd1=creat( input_buf,01777);i=write(fd1,str,512);printf("请输入你要写的容:");for(j=0;j<i;i++){scanf("%c",&str[i]);if( str[i] == '\n' )break;}while(!over){getcmd();if( input_buf[0] != '\0' ){fd1=creat( input_buf,01777);i=write(fd1,str,512);printf("请输入你要写的容:");for(j=0;j<i;i++){scanf("%c",&str[i]);if( str[i] == '\n' )break;}}}clearbuf();return 0;}if( strcmp( input_buf, "close" ) == 0 ){if( over ){printf( "请在close后输入要关闭的文件名\n" );clearbuf();return 0;}getcmd();if( input_buf[0] == '\0' ){printf( "请在close后输入要关闭的文件名\n" );clearbuf();return 0;}fd1=creat( input_buf,01777);close(fd1);while(!over){getcmd();if( input_buf[0] != '\0' ){fd1=creat( input_buf,01777);close(fd1);}}clearbuf();return 0;}if( strcmp( input_buf, "delete" ) == 0 ){if(over){printf( "请在delete后输入要删除的文件名\n" );clearbuf();return 0;}getcmd();if( input_buf[0] == '\0' ){printf( "请在delete后输入要删除的文件名\n" );clearbuf();return 0;}_delete(input_buf);while(!over){getcmd();if( input_buf[0] != '\0' )_delete(input_buf);}clearbuf();return 0;}if( strcmp( input_buf, "logout" ) == 0 ){clearbuf();return 1;}/* 找不到该命令 */if( input_buf[0] != '\0' ){printf( "bash: %s command not found\n", input_buf );clearbuf();}return 0;}/* 取得命令 */getcmd(){int i= 0;while( !over ){input_buf[i] = getchar();if( input_buf[i] == ' ' ){if( i == 0 ) /* 命令行的开始是空格,应舍去 */ i--;else{input_buf[i]='\0';break;}}elseif( input_buf[i] == '\n' ){over = 1;input_buf[i]='\0';break;}i++;}}/* 清空缓冲区 */clearbuf(){while( !over ){if( getchar() =='\n' )break;}}③文件系统格式化函数:#include <stdio.h>#include "filsys.h"int format() //文件系统格式化函数{struct filsys aaa;struct inode * inode;struct user tempuser[USERNUM];struct dinode dinode_buf;struct direct dir_buf[BLOCKSIZ / DIRECTSIZ];unsigned int block_buf[BLOCKSIZ / sizeof( int )];char * buf;int i, j, k;fd = fopen( "filesystem.dat", "w+b" ); /* 建立文件 */buf = (char * )malloc( 1024*1024 ); /* 申请1M空间 */if( buf == NULL ) /* 申请不成功,返回 */{printf( "\nThe system file can't be created!\n" );return 0;}/* 申请成功,把其空间写入filesystem.dat,使filesystem.dat为1M */fseek( fd, 0, SEEK_SET );fwrite( buf, 1, 1024*1024, fd );free ( buf );dinode_buf.di_mode = DIEMPTY; /* 设置磁盘i节点缓冲区,DIEMPTY表示空闲*/fseek( fd, DINODESTART, SEEK_SET );for( i = 0; i < DINODEBLK * BLOCKSIZ / DINODESIZ; i++ ){fseek( fd, DINODESTART + DINODESIZ * i, SEEK_SET );fwrite( &dinode_buf, 1, sizeof( dinode_buf ), fd );}/* 1. creat the main directory and its sub dir etc and the file password */inode = iget( 0 );inode->i_mode = DIEMPTY; /* 第0块不用 */iput( inode );inode = iget( 1 ); /* 第1盘块放用户名表 */inode->i_number = 1;inode->i_mode = DIREG; //普通文件inode->i_size = sizeof( struct user ) * USERNUM;inode->i_addr[0] = 1;/* 用户imacih是超级用户 */strcpy( tempuser[0].u_name, "imacih" );strcpy( tempuser[0].password, "dgh123456" );tempuser[0].u_default_mode = SUPERMODE;tempuser[0].u_gid = 1;tempuser[0].u_uid = 1;for( i = 1; i < USERNUM; i++ ){strcpy( tempuser[i].u_name, " " );}fseek( fd, DATASTART + BLOCKSIZ * inode->i_addr[0], SEEK_SET );fwrite( tempuser, 1, inode->i_size, fd );iput( inode );inode = iget( 2 ); /* 第2盘块用于存储根目录 */inode->i_number = 1;inode->i_mode = DEFAULTMODE | DIDIR;inode->i_size = 2 * DIRECTSIZ;inode->i_addr[0] = 2;strcpy( dir_buf[0].d_name, "/" );dir_buf[0].d_ino = 2;strcpy( dir_buf[1].d_name, "/" );dir_buf[1].d_ino = 2;strcpy( dir_buf[2].d_name, "etc" );dir_buf[2].d_ino = 3;fseek( fd, DATASTART + BLOCKSIZ * inode->i_addr[0], SEEK_SET );fwrite( dir_buf, 1, inode->i_size, fd );iput( inode );inode = iget( 2 ); /* 2 etc dir id */inode->i_number = 1;inode->i_mode = DEFAULTMODE | DIDIR;inode->i_size = 2 * DIRECTSIZ;inode->i_addr[0] = 2; /* 第3盘块用于根目录下的子目录 */ strcpy( dir_buf[0].d_name, ".." );dir_buf[0].d_ino = 1;strcpy( dir_buf[1].d_name, "." );dir_buf[1].d_ino = 2;fseek( fd, DATASTART + BLOCKSIZ * inode->i_addr[0], SEEK_SET );fwrite( dir_buf, 1, 2 * DIRECTSIZ, fd );iput( inode );/* 2. 初始化超级块 */filsys.s_ninode = DINODEBLK * BLOCKSIZ / DINODESIZ - 3; /* 空闲磁盘i节点数 */ filsys.s_nfree = FILEBLK - 3; /* 空闲文件块数 *//* 初始化空闲磁盘i节点堆栈 */for( i = 0; i < NICINOD; i++ ){filsys.s_inode[i] = 3 + i; /* 从第3个磁盘i块,前面3个已用 */ }filsys.s_pinode = 0; /* 当前空闲块指针 */filsys.s_rinode = NICINOD + 3; // 下一准备装入空闲盘块号栈的盘块号/* 把第1组空闲盘块放进空闲盘块堆栈 */for( i = 0; i < NICFREE; i++ ){filsys.s_free[i] = 3 + NICFREE - 1 - i;}filsys.s_pfree = NICFREE - 1;for( i = 3 + NICFREE * 2 - 1; i < FILEBLK; i += NICFREE ){for( j = 0; j < NICFREE; j++ ){ /* 往缓冲区写与成组法组织空闲盘块有关的信息:下一组盘块空闲块号与块数 */ block_buf[j] = i - j;}block_buf[NICFREE] = NICFREE; /* 该项记录本组的空闲盘块数 *//* 把缓冲区容写到每组空闲盘块的最后一块中 */bwrite( i - NICFREE, block_buf );}/* 最后一组空闲盘块可能不足NICFREE块,故需单独处理 */i = i - NICFREE;for( j = 0; j < FILEBLK - i + 1; j++ )block_buf[j] = FILEBLK - j;block_buf[NICFREE] = FILEBLK - i + 1; /* 最末组的空闲盘块数 */bwrite( i, block_buf );/* 把超级块写入 block 1# */fseek( fd, BLOCKSIZ, SEEK_SET );fwrite( &filsys, 1, sizeof( struct filsys ), fd );aaa=filsys;return 1;}④空闲盘块分配、回收函数:#include <stdio.h>#include "filsys.h"static unsigned int block_buf[BLOCKSIZ];/* 空闲盘块分配函数 */unsigned int balloc(){unsigned int free_block, free_block_num;int i;if( filsys.s_nfree == 0 ) /* 磁盘已满,无空闲盘块 */ {printf( "Disk has no space\n" );return -1;}free_block = filsys.s_free[filsys.s_pfree];if( filsys.s_pfree == 0 ) /* 已经是栈底 */{/* 读取栈底盘块号所对应的盘块数据 */bread( filsys.s_free[filsys.s_pfree], block_buf );free_block_num = block_buf[NICFREE]; /* 该空闲盘块组的盘块数 *//* 把盘块组放到空闲盘块号栈上 */for( i = 0; i < free_block_num; i++ )filsys.s_free[i] = block_buf[i];filsys.s_pfree = free_block_num - 1; /* 指针指向栈顶 */ }elsefilsys.s_pfree--; /* 栈指针下移一位 */ filsys.s_nfree--; /* 空闲盘块少1 */// filsys.s_fmod = SUPDATE; /* 置超级块修改标志 */ return free_block;}/* 空闲盘块回收函数 */bfree( unsigned int block_num ){int i;filsys.s_pfree++;if( filsys.s_pfree == NICFREE ) /* 空闲盘块堆栈已满 */{block_buf[NICFREE] = NICFREE; /* 空闲盘块堆栈的盘块数NICFREE记入缓冲区 */for( i = 0; i < NICFREE; i++ )block_buf[i] = filsys.s_free[i]; /* 把空闲盘块数据写入缓冲区 */filsys.s_pfree = 0; /* 栈指针指向栈底 */bwrite( block_num, block_buf ); /* 缓冲区容写入新回收的盘块 */ }filsys.s_free[filsys.s_pfree] = block_num; /* 回收盘块 */filsys.s_nfree++; /* 空闲盘块多1 */// filsys.s_fmod = SUPDATE; /* 置超级块修改标志 */}⑤节点分配和释放函数:#include <stdio.h>#include "filsys.h"static struct dinode block_buf[BLOCKSIZ / DINODESIZ];struct inode * ialloc(){ struct filsys aaa;struct inode * temp_inode;unsigned int cur_di,temp;int i;if( filsys.s_ninode == 0 ) /* 没有空闲磁盘i节点 */{printf( "No leisure dinode!\n" );return NULL;}if( filsys.s_pinode == NICINOD ) /* 空闲磁盘i节点栈空 */{cur_di = filsys.s_rinode;if( filsys.s_ninode >= NICINOD ) /* 空闲磁盘i节点数可装满空闲i节点栈*/{filsys.s_pinode = 0;/* 把下一组磁盘i节点读进空闲磁盘i节点栈 */while( filsys.s_pinode < NICINOD ){fseek (fd, DINODESTART + cur_di * DINODESIZ, SEEK_SET);fread (block_buf, 1, BLOCKSIZ, fd);for( i = 0; ( i < BLOCKSIZ / DINODESIZ ) && ( filsys.s_pinode < NICINOD ); ){if( block_buf[i].di_mode == DIEMPTY ) /* 该磁盘i节点空闲 */{filsys.s_inode[filsys.s_pinode] = cur_di;/* 把该i节点装入空闲栈 */filsys.s_pinode++; /* 栈指针下移一位 */}i++;cur_di++;}}filsys.s_pinode = 0;}else /* 剩下的空闲磁盘i节点不能装满栈区 */{/* 计算出空闲栈指针装入i节点的第一个位置 */filsys.s_pinode = NICINOD - filsys.s_ninode;while( filsys.s_pinode < NICINOD ){fseek (fd, DINODESTART + cur_di * DINODESIZ, SEEK_SET);fread (block_buf, 1, BLOCKSIZ, fd);for( i = 0; ( i < BLOCKSIZ / DINODESIZ ) && ( filsys.s_pinode < filsys.s_ninode ); ){if( block_buf[i].di_mode == DIEMPTY ){filsys.s_inode[filsys.s_pinode] = cur_di;filsys.s_pinode++;}i++;cur_di++;}}filsys.s_pinode = NICINOD - filsys.s_ninode;}}/* 初始化磁盘i节点 */// temp_inode = iget( filsys.s_inode[filsys.s_pinode] );// temp_inode->i_number = 1;// temp_inode->i_uid = user[user_id].u_uid;// temp_inode->i_gid = user[user_id].u_gid;// temp_inode->i_size = 0;// temp_inode->i_addr[0] = 0;// iput( temp_inode );/* 分配存i节点 */temp_inode = iget( filsys.s_inode[filsys.s_pinode] );/* 从磁盘i节点读取数据到存i节点 */fseek( fd, DINODESTART + filsys.s_inode[filsys.s_pinode] * DINODESIZ, SEEK_SET );fwrite( &temp_inode->i_number, 1, sizeof(struct dinode), fd );filsys.s_pinode++; /* 栈指针下移一位 */filsys.s_ninode--; /* 空闲磁盘i节点又少了一个 */// filsys.s_fmod = SUPDATE; /* 置超级块修改标志 */aaa=filsys;return temp_inode;}ifree(unsigned int dinodeid){filsys.s_ninode++; /* 空闲磁盘i节点加1 */if( filsys.s_pinode != 0 ) /* 空闲磁盘i节点栈未满 */{/* 直接回收 */filsys.s_pinode--;filsys.s_inode[filsys.s_pinode] = dinodeid;}else /* 空闲磁盘i节点栈已满 */{if( dinodeid < filsys.s_rinode ) /* 磁盘i节点号小于铭记i节点号 */{filsys.s_inode[0] = dinodeid; /* 回收 */filsys.s_rinode = dinodeid; /* 把新回收的i节点作为铭记i节点 */ }}}(2)流程图:①主函数流程图:②数据块的分配流程图:③浏览目录流程图:④创建目录流程图:四.操作界面截图及分析(1)注册用户和登录用户操作界面截图如下:通过login函数注册一个用户,并用该用户登录,可以使用户继续运行接下来的程序,有利于识别用户。