欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

操作系统|Linux文件系统

程序员文章站 2022-05-12 13:34:32
...

操作系统|Linux文件系统

一、实验目的

1、掌握文件管理的基本内容和原理;
2、了解文件和目录操作的系统调用用户接口;

二、实验内容

熟悉Linux文件目录管理各种功能命令

三、实验原理

Linux正统的文件系统(如ext2、3等)将硬盘分区时会划分出目录块、inode Table区块和data block数据区域。一个文件由一个目录项、inode和数据区域块组成。Inode包含文件的属性(如读写属性、owner等,以及指向数据块的指针),数据区域块则是文件内容。当查看某个文件时,会先从inode table中查出文件属性及数据存放点,再从数据块中读取数据。

文件存储结构大概如下:
操作系统|Linux文件系统
其中目录项的结构如下(每个文件的目录项存储在改文件所属目录的文件内容里):
操作系统|Linux文件系统
其中文件的inode结构如下(inode里所包含的文件信息可以通过stat filename查看得到):
操作系统|Linux文件系统
内核将文件内容存放在数据区,文件属性存放在i-节点,文件名存放在目录中。

用户数据 (user data) 与元数据 (metadata)。用户数据,即文件数据块 (data block),数据块是记录文件真实内容的地方;而元数据则是文件的附加属性,如文件大小、创建时间、所有者等信息。在 Linux 中,元数据中的 inode 号(inode 是文件元数据的一部分但其并不包含文件名,inode 号即索引节点号)才是文件的唯一标识而非文件名。文件名仅是为了方便人们的记忆和使用,系统或程序通过 inode 号寻找正确的文件数据块。下图展示了程序通过文件名获取文件内容的过程。

操作系统|Linux文件系统
Linux 的文件是没有所谓的扩展名的,一个 Linux文件能不能被执行与它是否可执行的属性有关,只要你的权限中有 x ,比如[ -rwx-r-xr-x ] 就代表这个文件可以被执行,与文件名没有关系。跟在 Windows下能被执行的文件扩展名通常是 .com .exe .bat 等不同。

不过,可以被执行跟可以执行成功不一样。比如在 root 主目彔下的 install.log 是一个文本文件,修改权限成为 -rwxrwxrwx 后这个文件能够真的执行成功吗? 当然不行,因为它的内容根本就没有可以执行的数据。所以说,这个 x 代表这个文件具有可执行的能力, 但是能不能执行成功,当然就得要看该文件的内容了。

四、代码

1、代码目录

  • void startsys()
  • void my_format()
  • void my_cd
  • my_mkdir
  • my_rmdir
  • my_ls
  • my_create

void startsys()

{
    FILE *fp;
    int i;
    myvhard=(unsigned char *)malloc(SIZE);
    memset(myvhard,0,SIZE);
    fp=fopen(filename,"r");
/*
FILE *fopen(char *path,*mode);
如果成功的打开一个文件, fopen()函数返回文件指针,   否则返回空指针 (NULL)。由此可判断文件打开是否成功。
mode:
r:  读打开, 不创建
w: 写打开,创建/清空
a: 写打开, 创建/追加

r+: 读/写打开,不创建
w+:读/写打开,创建/清空
a+:读/写打开, 创建/追加 
*/

  if(fp)
    {
        fread(buffer,SIZE,1,fp);
/*
fread
size_t fread(void * ptr,size_t size,size_t nmemb,FILE * stream);
参数size,表示一次读取的数据单元大小,nmemb表示读取的次数。

成功时fread返回的值与nmemb相等;若小于nmemb但是大于0,则可能是到了文件末尾,不够次数;若返回0,则是文件读取错误,不满一个size的大小。

所以循环读取文件的时候,while(可以判断是否大于等于0)作为条件,里面再基于feof函数判断是否到了文件末尾而退出循环。
*/
  fclose(fp);
        if(buffer[0]==0xaa)//如果最开始是字符串结束符
        {
            printf("myfilesys is not exist,begin to creat the file...\n");
            my_format();
        }
        else
        {
            for(i=0;i<SIZE;i++)
                myvhard[i]=buffer[i];
        }
    }
    else
    {
        printf("myfilesys is not exist,begin to creat the file...\n");
        my_format();
    }


    strcpy(openfilelist[0].filename,"root");
    strcpy(openfilelist[0].exname,"di");
    openfilelist[0].attribute=0x2d;
    openfilelist[0].time=((fcb *)(myvhard+5*BLOCKSIZE))->time;
    openfilelist[0].date=((fcb *)(myvhard+5*BLOCKSIZE))->date;
    openfilelist[0].first=((fcb *)(myvhard+5*BLOCKSIZE))->first;
    openfilelist[0].length=((fcb *)(myvhard+5*BLOCKSIZE))->length;
    openfilelist[0].free=1;
    openfilelist[0].dirno=5;
    openfilelist[0].diroff=0;
    openfilelist[0].count=0;
    openfilelist[0].fcbstate=0;
    openfilelist[0].topenfile=0;
    openfilelist[0].father=0;
	//并填写根目录文件的相关信息
	memset(currentdir,0,sizeof(currentdir));
    strcpy(currentdir,"\\root\\");
    strcpy(openfilelist->dir[0],currentdir);
    startp=((block0 *)myvhard)->startblock;
    ptrcurdir=&openfilelist[0];
    curfd=0;
}

void my_format()

/*
原型声明:  void my_format()
功能描述:  对虚拟磁盘进行格式化,布局虚拟磁盘,建立根目录文件
输入:无
输出:无

函数功能实现算法描述:
虚拟磁盘空间布局
1块  2块    2块 995块
引导块 FAT1  FAT2  数据区
虚拟磁盘一共划分成1000个磁盘块
每块1024个字节,磁盘空间布局如上
将数据区的第一块(即虚拟磁盘的第6块)分配给根目录文件
*/
void my_format()  
{
    FILE *fp;
    fat *fat1,*fat2;
    block0 *b0;
time_t *now;
time_t   时间函数
/*
time_t  类型在time.h中定义:

    #ifndef   __TIME__T

    #define  __TIME_T

    typedef    long  time_t

    #endif

可见, time_t 实际上是一个长整型,其值表示从1970年1月1日00时00分00秒(linux系统的Epoch时间)到当前时刻的秒数,由于time_t类型长度限制;

(long型),它所表示的时间不能晚于2038年1月19日03时14分07秒(UTC),(64位机器不存在此问题,想想why)

使用time()函数获取当前时间的time_t值,使用ctime()函数将time_t  转为当地时间字符串(返回值是字符串);
*/
struct tm *nowtime;
    unsigned char *p;
    fcb *root;
    int i;

    p=myvhard;//虚拟磁盘起始地址
    b0=(block0 *)p;
    fat1=(fat *)(p+BLOCKSIZE);
    fat2=(fat*)(p+3*BLOCKSIZE);//起始地址加三个磁盘块大小
	
	int num=0xaaaa; /*文件系统魔数 10101010*/
	/*魔数通常位于一个对象中,并用来判断对象(如文件)的类型。例如对于a.out格式的执行文件来说,其开始两个字节就是一个魔数,具有特定的值。对于一个文件系统来说,其超级块开始的字节就用来指定文件系统的类型,也是一个魔数。*/
 	b0->root = 5;//根目录起始盘块号为5
    strcpy(b0->information,"My FileSystem Ver 1.0 \n Blocksize=1KB Whole size=1000KB Blocknum=1000 RootBlocknum=2\n");
      /*
    FAT1,FAT2
        前面五个磁盘块已分配,标记为END
    */
    fat1->id=END;
    fat2->id=END;
    fat1++;fat2++;

    fat1->id=END;
    fat2->id=END;
    fat1++;fat2++;

    fat1->id=END;
    fat2->id=END;
    fat1++;fat2++;

    fat1->id=END;
    fat2->id=END;
    fat1++;fat2++;

    fat1->id=END;
    fat2->id=END;
    fat1++;fat2++;

    fat1->id=6;
    fat2->id=6;
    fat1++;fat2++;

    fat1->id=END;
    fat2->id=END;
    fat1++;fat2++;

    /*
    将数据区的标记为空闲状态
    */
    for(i=7;i<SIZE/BLOCKSIZE;i++)
    {
        (*fat1).id=FREE;
        (*fat2).id=FREE;
        fat1++;
        fat2++;
    }//同时初始化两个FAT表

 	/*
    创建根目录文件root,将数据区的第一块分配给根目录区
    在给磁盘上创建两个特殊的目录项:".","..",
    除了文件名之外,其它都相同
    */   
    if(strcmp(p,".")==0)   //cd .  仍在当前目录
        return;
    if(strcmp(p,"..")==0)  //cd .. 去往上层目录
    p+=BLOCKSIZE*5;
    root=(fcb *)p;
    strcpy(root->filename,".");
    strcpy(root->exname,"di");
    root->attribute=40;
    now=(time_t *)malloc(sizeof(time_t));
    time(now);
    nowtime=localtime(now);
    root->time=nowtime->tm_hour*2048+nowtime->tm_min*32+nowtime->tm_sec/2;
    root->date=(nowtime->tm_year-80)*512+(nowtime->tm_mon+1)*32+nowtime->tm_mday;
    root->first=5;
    root->length=2*sizeof(fcb);
    root++;

    strcpy(root->filename,"..");
strcpy(root->exname,"di");

/*
一个Linux文件能否被执行,与它的第一列的十个属性有关,与文件名一点关系也没有。这个跟windows有点不一样,win下的可执行文件扩展名通常是.com、.exe、.bat等,而在Linux下,只要文件具有x就代表这个文件具有可执行权限。

用ls查看文件属性时,一个文件能否被执行成功不仅与是否具有可执行权限有关,还与文件内容是否可执行有关。

在Linux中虽然文件后缀扩展名并没有什么实际的意义,但文件扩展名有利于我们了解文件的类型,是用来干嘛的,所以通常文件还是会加适当的扩展名。
*/
 	root->attribute=40;
    time(now);
    nowtime=localtime(now);
    root->time=nowtime->tm_hour*2048+nowtime->tm_min*32+nowtime->tm_sec/2;
    root->date=(nowtime->tm_year-80)*512+(nowtime->tm_mon+1)*32+nowtime->tm_mday;
    root->first=5;
    root->length=2*sizeof(fcb);
    root++;//初始化两个FCB里所有的值

    for(i=2;i<BLOCKSIZE*2/sizeof(fcb);i++,root++)
    {
        root->filename[0]='\0';
    }
    fp=fopen(filename,"w");
    b0->startblock=p+BLOCKSIZE*4;
    fwrite(myvhard,SIZE,1,fp);
    free(now);
    fclose(fp);

}

void my_cd

void my_cd(char *dirname)  
{
    char *p,text[MAX_TXT_SIZE];
    int fd,i;
	p=strtok(dirname,"\\");  //提取出cd的命令
  	/*.原型 char * strtok(char * s,const char * delim);

功能 对字符串进行分段处理,每次调用strtok,返回由delim中任意一个字符分隔的一段字符串。

       参数说明:@s:输入字符串,且该字符串不能为const类型。

                @delim:所有用于将@s进行分隔的字符

                @return:每调用一次,返回字符串中的一段,以'\0'结束

      使用注意:第一次调用,传s进去,后面的调用都传NULL。

      函数实现原理:将@s中的所有delim字符都替换为‘\0',即原来字符串自动被分为多段,当传入的@s为NULL时,自动调用下一段;@s不为NULL时,返回从@s开始的第一段
*/
	  char s[] = "111:222";
      char *p = strtok (s, ":");
      printf ("%s\r\n", p); //打印出111
      printf ("%s\r\n", s); //同样打印出111,因为原来@s中的":"被替换为'\0',截断了字符串。

	  p = strtok (NULL, ":"); //注意传入参数为NULL。

      printf ("%s\r\n", p); //打印出222,原@s的第二段字符串
if(strcmp(p,".")==0)   //cd .  仍在当前目录
        return;
    if(strcmp(p,"..")==0)  //cd .. 去往上层目录
    {
        fd=openfilelist[curfd].father;
        my_close(curfd);
        curfd=fd;
        ptrcurdir=&openfilelist[curfd];
        return;
    }
    if(strcmp(p,"root")==0)  
    {
        for(i=1;i<MAX_OPEN_FILE;i++)
        {
            if(openfilelist[i].free)
                my_close(i);
        }
        ptrcurdir=&openfilelist[0];
        curfd=0;
        p=strtok(NULL,"\\");
    }

    while(p)  //设置当前目录为该目录
    {
        fd=my_open(p);
        if(fd>0)
        {
            ptrcurdir=&openfilelist[fd];
            curfd=fd;
        }
        else
            return;
        p=strtok(NULL,"\\");
    }
}

my_mkdir

/*
原型声明:  void my_mkdir(char *dirname)
功能描述:  创建子目录函数,在当前目录下创建名为dirname的目录
输入:
dirname  指向新建目录的名字的指针
输出:无

函数功能实现算法描述:

*/
void my_mkdir(char *dirname) 创建子目录
{
    fcb *dirfcb,*pcbtmp;
    int rbn,i,fd;
    unsigned short bknum;
    char text[MAX_TXT_SIZE],*p;
    time_t *now;
    struct tm *nowtime;
    /*
    将当前的文件信息读到text中
    rbn 是实际读取的字节数
    */
    openfilelist[curfd].count=0;
    rbn = do_read(curfd,openfilelist[curfd].length,text); //将当前目录文件读入到内存中
    dirfcb=(fcb *)text;  
    
    //检测是否有相同的目录名
    for(i=0;i<rbn/sizeof(fcb);i++)
    {
        if(strcmp(dirname,dirfcb->filename)==0)
        {
            printf("Error,the dirname is already exist!\n");
            return;
        }
        dirfcb++;
    }

    dirfcb=(fcb *)text;
    for(i=0;i<rbn/sizeof(fcb);i++)
    {
        if(strcmp(dirfcb->filename,"")==0)
            break;
        dirfcb++;
    }
    openfilelist[curfd].count=i*sizeof(fcb);
    
    //寻找一个空闲文件表项
    fd=findFreeO();
    if(fd<0)
    {
        return;
    }

    
    //寻找空闲盘块    
    bknum = findFree();
    if(bknum == END )
    {
        //回收文件占据的用户打开表表项
        openfilelist[fd].attribute=0;
        openfilelist[fd].count=0;
        openfilelist[fd].date=0;
        strcpy(openfilelist[fd].dir[fd],"");
        strcpy(openfilelist[fd].filename,"");
        strcpy(openfilelist[fd].exname,"");
        openfilelist[fd].length=0;
        openfilelist[fd].time=0;
        openfilelist[fd].free=0;
        openfilelist[fd].topenfile=0;

        return -1;
    }

    pcbtmp=(fcb *)malloc(sizeof(fcb));
    now=(time_t *)malloc(sizeof(time_t));

    //在当前目录下新建目录项
    
    pcbtmp->attribute=0x30;  //0x30在ASCII码上代表0,即表示文件属性为0(目录文件)

    //获取时间,调整时间格式
    time(now);
    nowtime=localtime(now);
    pcbtmp->time=nowtime->tm_hour*2048+nowtime->tm_min*32+nowtime->tm_sec/2;
    pcbtmp->date=(nowtime->tm_year-80)*512+(nowtime->tm_mon+1)*32+nowtime->tm_mday;


    strcpy(pcbtmp->filename,dirname);
    strcpy(pcbtmp->exname,"di");
    pcbtmp->first=bknum;
    pcbtmp->length=2*sizeof(fcb);

    openfilelist[fd].attribute=pcbtmp->attribute;
    openfilelist[fd].count=0;
    openfilelist[fd].date=pcbtmp->date;
    strcpy(openfilelist[fd].dir[fd],openfilelist[curfd].dir[curfd]);

    p=openfilelist[fd].dir[fd];
    while(*p!='\0')
        p++;
    strcpy(p,dirname);
    while(*p!='\0') p++;
    *p='\\';p++;
    *p='\0';

    openfilelist[fd].dirno=openfilelist[curfd].first;
    openfilelist[fd].diroff=i;
    strcpy(openfilelist[fd].exname,pcbtmp->exname);
    strcpy(openfilelist[fd].filename,pcbtmp->filename);
    openfilelist[fd].fcbstate=1;
    openfilelist[fd].first=pcbtmp->first;
    openfilelist[fd].length=pcbtmp->length;
    openfilelist[fd].free=1;
    openfilelist[fd].time=pcbtmp->time;
    openfilelist[fd].topenfile=1;

    do_write(curfd,(char *)pcbtmp,sizeof(fcb),2);

    pcbtmp->attribute=0x28;
    time(now);
    nowtime=localtime(now);
    pcbtmp->time=nowtime->tm_hour*2048+nowtime->tm_min*32+nowtime->tm_sec/2;
    pcbtmp->date=(nowtime->tm_year-80)*512+(nowtime->tm_mon+1)*32+nowtime->tm_mday;
    strcpy(pcbtmp->filename,".");
    strcpy(pcbtmp->exname,"di");
    pcbtmp->first=bknum;
    pcbtmp->length=2*sizeof(fcb);

    do_write(fd,(char *)pcbtmp,sizeof(fcb),2);

    pcbtmp->attribute=0x28;
    time(now);
    nowtime=localtime(now);
    pcbtmp->time=nowtime->tm_hour*2048+nowtime->tm_min*32+nowtime->tm_sec/2;
    pcbtmp->date=(nowtime->tm_year-80)*512+(nowtime->tm_mon+1)*32+nowtime->tm_mday;
    strcpy(pcbtmp->filename,"..");
    strcpy(pcbtmp->exname,"di");
    pcbtmp->first=openfilelist[curfd].first;
    pcbtmp->length=openfilelist[curfd].length;

    do_write(fd,(char *)pcbtmp,sizeof(fcb),2);

    openfilelist[curfd].count=0;
    do_read(curfd,openfilelist[curfd].length,text);

    pcbtmp=(fcb *)text;
    pcbtmp->length=openfilelist[curfd].length;
    my_close(fd);

    openfilelist[curfd].count=0;
    do_write(curfd,text,pcbtmp->length,2);
}

my_rmdir

void my_rmdir(char *dirname)
{
    int rbn,fd;
    char text[MAX_TXT_SIZE];
    fcb *fcbptr,*fcbtmp,*fcbtmp2;
    unsigned short bknum;
    int i,j;
    fat *fat1,*fatptr;

    if(strcmp(dirname,".")==0 || strcmp(dirname,"..")==0)
    {
        printf("Error,can't remove this directory.\n");
        return;
    }

    fat1=(fat *)(myvhard+BLOCKSIZE);
    openfilelist[curfd].count=0;
    //
    rbn=do_read(curfd,openfilelist[curfd].length,text);

    fcbptr=(fcb *)text;
    for(i=0;i<rbn/sizeof(fcb);i++)
    {
        if(strcmp(dirname,fcbptr->filename)==0)
        {
            break;
        }
        fcbptr++;
    }
    if(i >= rbn/sizeof(fcb))
    {
        printf("Error,the directory is not exist.\n");
        return;
    }


    bknum=fcbptr->first;
    fcbtmp2=fcbtmp=(fcb *)(myvhard+bknum*BLOCKSIZE);
    for(j=0;j<fcbtmp->length/sizeof(fcb);j++)
    {
        if(strcmp(fcbtmp2->filename,".") && strcmp(fcbtmp2->filename,"..") && fcbtmp2->filename[0]!='\0')
        {
            printf("Error,the directory is not empty.\n");
            return;
        }
        fcbtmp2++;
    }

    while(bknum!=END)
    {
        fatptr=fat1+bknum;
        bknum=fatptr->id;
        fatptr->id=FREE;
    }

    strcpy(fcbptr->filename,"");
    strcpy(fcbptr->exname,"");
    fcbptr->first=END;
    openfilelist[curfd].count=0;
    do_write(curfd,text,openfilelist[curfd].length,2);

}

my_ls

/*
原型声明:  void my_ls()
功能描述:  显示目录函数
输入:   无
输出:   无

函数功能实现算法描述:

*/
void my_ls()  {
    fcb *fcbptr;
    int i;
    char text[MAX_TXT_SIZE];
    unsigned short bknum;
    openfilelist[curfd].count=0;
    do_read(curfd,openfilelist[curfd].length,text);
    fcbptr=(fcb *)text;
    for(i=0;i<(int)(openfilelist[curfd].length/sizeof(fcb));i++)
    {
        if(fcbptr->filename[0]!='\0')
        {
            if(fcbptr->attribute&0x20)
            {
                printf("%s\\\t\t<DIR>\t\t%d/%d/%d\t%02d:%02d:%02d\n",fcbptr->filename,((fcbptr->date)>>9)+1980,((fcbptr->date)>>5)&0x000f,(fcbptr->date)&0x001f,fcbptr->time>>11,(fcbptr->time>>5)&0x003f,fcbptr->time&0x001f*2);
            }
            else
            {
                printf("%s.%s\t\t%dB\t\t%d/%d/%d\t%02d:%02d:%02d\t\n",fcbptr->filename,fcbptr->exname,fcbptr->length,((fcbptr->date)>>9)+1980,(fcbptr->date>>5)&0x000f,fcbptr->date&0x1f,fcbptr->time>>11,(fcbptr->time>>5)&0x3f,fcbptr->time&0x1f*2);
            }
        }
        fcbptr++;
    }
    openfilelist[curfd].count=0;
}

my_create

/*
原型声明:  void my_create(char *filename)
功能描述:  创建文件函数
输入:
filename 指向文件名的指针
输出:   无

函数功能实现算法描述:

*/
void my_create(char *filename)  //ysh
{
    char *fname,*exname,text[MAX_TXT_SIZE];
    int fd,rbn,i;
    fcb *filefcb,*fcbtmp;
    time_t *now;
    struct tm *nowtime;
    unsigned short bknum;
    fat *fat1,*fatptr;

    fat1=(fat *)(myvhard+BLOCKSIZE);
    fname=strtok(filename,".");  //文件名
    exname=strtok(NULL,".");  //
    if(strcmp(fname,"")==0)
    {
        printf("Error,creating file must have a right name.\n");
        return;
    }
    if(!exname)
    {
        printf("Error,creating file must have a extern name.\n");
        return;
    }

    openfilelist[curfd].count=0;
    rbn=do_read(curfd,openfilelist[curfd].length,text);
    filefcb=(fcb *)text;
    for(i=0;i<rbn/sizeof(fcb);i++)
    {
        if(strcmp(fname,filefcb->filename)==0 && strcmp(exname,filefcb->exname)==0)
        {
            printf("Error,the filename is already exist!\n");
            return;
        }
        filefcb++;
    }

    filefcb=(fcb *)text;
    for(i=0;i<rbn/sizeof(fcb);i++)
    {
        if(strcmp(filefcb->filename,"")==0)
            break;
        filefcb++;
    }
    openfilelist[curfd].count=i*sizeof(fcb);

    bknum=findFree();
    if(bknum==END)
    {
        return ;
    }
    fcbtmp=(fcb *)malloc(sizeof(fcb));
    now=(time_t *)malloc(sizeof(time_t));

    fcbtmp->attribute=0x00;
    time(now);
    nowtime=localtime(now);
    fcbtmp->time=nowtime->tm_hour*2048+nowtime->tm_min*32+nowtime->tm_sec/2;
    fcbtmp->date=(nowtime->tm_year-80)*512+(nowtime->tm_mon+1)*32+nowtime->tm_mday;
    strcpy(fcbtmp->filename,fname);
    strcpy(fcbtmp->exname,exname);
    //*fcbtmp->exname=*exname;
    fcbtmp->first=bknum;
    fcbtmp->length=0;

    do_write(curfd,(char *)fcbtmp,sizeof(fcb),2);
    free(fcbtmp);
    free(now);
    openfilelist[curfd].count=0;
    do_read(curfd,openfilelist[curfd].length,text);
    fcbtmp=(fcb *)text;
    fcbtmp->length=openfilelist[curfd].length;
    openfilelist[curfd].count=0;
    do_write(curfd,text,openfilelist[curfd].length,2);
    openfilelist[curfd].fcbstate=1;
    fatptr=(fat *)(fat1+bknum);
    fatptr->id=END;
}
相关标签: 操作系统