Experiment purpose and requirements
Understand the file system composition and basic principle of the operating system, use these knowledge to simulate a FAT file system in memory, complete the file creation and indexing functions, and realize the following command interfaces:
(1) New file, format: mkfile filename filecontent
filename: file name
filecontent: file content (characters)
Write FAT table, directory table and file content in FAT format.
(2) List files, format: dir
List all file information and virtual disk information in the directory.
(3) Display the file content in the format of type filename
filename: file name
Find the block number of the file name in the directory item, and print the file content on the screen.
(4) Delete file: del filename
filename: file name
Delete files and reclaim virtual disk space.
(5) Exiting the file system: quit
Experimental principle and content
Experimental principle:
File system refers to the physical space where files exist. Each partition in Linux system is a file system and has its own directory hierarchy. Linux will form the overall directory hierarchy of these separate file systems belonging to different partitions in a certain way. The operation of an operating system is inseparable from the operation of files, so it is necessary to own and maintain its own file system.
The Linux file system uses index nodes to record file information, which acts like the file allocation table of windows.
An index node is a structure that contains information such as the length of a file, creation and modification time, permissions, ownership, and location on disk. A file system maintains an array of inodes, and each file or directory corresponds to a unique element in the inode array. The system assigns a number to each index node, that is, the index number of the node in the array, which is called the index node number.
The Linux file system saves the file inode number and file name in the directory at the same time. A directory is just a table that combines the name of a file with its index node number. Each pair of file names and index node numbers in the directory are called a connection.
For a file, there is a unique inode number corresponding to it, but for an inode number, there can be multiple file names corresponding to it. Therefore, the same file on disk can be accessed through different paths.
The use case to be used in this experiment - fat. As a file name, each entry in fat (File Allocation Table) corresponds to the allocation status of each block in the disk. The number of entries in fat determines the maximum representable disk capacity. FAT16 indicates that up to 216 blocks (or clusters) are supported. The possible values of fat table entries are:
0x00: indicates that the corresponding block is idle
0xFF: indicates that the corresponding block is the last block of the file
Other values: indicates the block number of the next block of the file
The file control information is recorded in the file directory. Each directory item (FCB) corresponds to a file. When users want to operate the file, they always query the directory to find out the FCB, and then they can read the file content.
Simple file system demonstration:
(1) Virtual disk structure
#define MAX_DISK_SIZE 256
u_char g_disk[MAX_DISK_SIZE]
If the size of each disk block is defined as 16 bytes, the system can store up to 16 disk block contents.
It is specified that the FAT table always occupies Block 0 and uses 8-bit FAT table entries, so there are 16 FAT table entries in Block 0, the directory table occupies blocks 1 to 4, and each directory entry (FCB) occupies 16 bytes. Therefore, the system supports up to 4 files. Since 0-5 blocks are occupied by the system, the 0th to 5th bytes of FAT should be 0xFF, so the maximum support capacity of the system is 10 blocks.
(2) System data structure
/*************************************************************
FAT structure
One byte (8 bits) is used to represent a block number
cluster value:
0x00: idle
0xFF: last block of file
Other values: the next block number of the file
*************************************************************/
typedef struct _tagfat {
u_char cluster;
} FAT,*PFAT,**PPFAT;
/***********************************************************
File control block structure (20 bytes)
f_name : The maximum length of the file name is 10 characters. Note that one character should be left to save '\ 0', so the maximum length of the file name is 9 characters
f_size : file length
f_firstblock: the starting block number of the file
********************************************************/
typedef struct _tagfcb {
char f_name[10];
int f_size;
int f_firstblock;
} FCB,*PFCB,**PPFCB;
(3) Principle of file deletion. When deleting a file, you need to do two things:
① Clear the FAT item corresponding to the block number occupied by the file 0x00. For example, if the deleted file occupies the 7th physical block, set the 7th byte of FAT to 0x00 (originally 0xFF)
② Set the first byte of the file name in the corresponding entry in the directory table to 0xE5
It can be seen that the file system using FAT does not clear the physical block content occupied by the file when deleting the file, which also makes it possible to de delete the file. In the directory item, the first byte of the file name is very important. If it is 0xE5, it indicates that the directory item is idle and can store a file information. Otherwise, it indicates that it has been occupied.
1. The source code of the core algorithm is given and detailed comments are added;
#include <stdio.h> #include <stdlib.h> #include <string.h> //Define return value #define SUCCESS 1 #define DELETE_FAIL 0xE0 #define ALLOC_FAIL 0xE1 #ifndef _FILESYS #define _FILESYS //For type definition, some types to be used below can be directly replaced in the form of reduction typedef unsigned short u_short; typedef unsigned short* pu_short; typedef unsigned char u_char; typedef unsigned char* pu_char; typedef unsigned int u_int; typedef unsigned int* pu_int; typedef char* pchar; typedef int* pint; typedef short* pshort; //File control block structure (20 bytes) typedef struct _tagfcb { char f_name[10]; //file name int f_size; //file length int f_firstblock; //Starting block number }FCB, *PFCB, **PPFCB; /******************************************************************** FAT structure One byte (8 bits) is used to represent a block number, and up to 256 blocks can be represented cluster Value: 0x00 : free 0xFF : The last piece of the file Other values: the next block number of the file *********************************************************************/ typedef struct _tagfat { u_char cluster; }FAT, *PFAT, **PPFAT; typedef struct tagNode { int iValue; struct tagNode *pNext; }NODE, *PNODE; /************************************************************************* Simulated disk space Regulations: each block occupies 16 bytes. The virtual space used by the system is 1024 bytes, so the disk space is very large Divided into 64 blocks Block 0 stores the FAT table, with a maximum of 16 FAT table entries, which also means that the system can only support a maximum of 16 blocks Blocks 1 ~ 4 store directories, and each directory item occupies 1 block (16 bytes), indicating that the system supports up to 4 files Block 5 start storing file contents **************************************************************************/ #define MAX_DISK_SIZE 1024 #define BLOCKSIZE 16 #define MAX_FAT 16 extern u_char g_disk[MAX_DISK_SIZE]; //Simulated disk space //Gets the first address of the specified block void* getBlockAddr(int blockno); //Virtual disk initialization void InitDisk(); //create a file int CreateFile(pchar f_name, pchar f_content, int f_size); //List files int ListFile(); //Display file int displayFile(pchar filename); //Delete file int initList(PNODE* pListHead); //Create a header node, iValue = 0 int insert(PNODE pListHead,int iValue); //Insert a node with the value of iValue to the end of the linked list int deleteList(PNODE pListHead); //Delete a linked list space with a specified header pointer void printList(PNODE pListHead); //Print the entire linked list #endif int initList(PNODE* pListHead) { //Create a head node space and assign a value of 0 if ((*pListHead = (PNODE)malloc(sizeof(NODE))) == NULL) { return ALLOC_FAIL; } (*pListHead)->iValue = 0; (*pListHead)->pNext = NULL; return SUCCESS; } //Inserts a new node at the end of the linked list of the specified head node int insert(PNODE pListHead,int iValue) { PNODE pNode; PNODE pLastNode = pListHead; if ((pNode = (PNODE)malloc(sizeof(NODE))) == NULL) { return ALLOC_FAIL; } pNode->iValue = iValue; pNode->pNext = NULL; while(1) { if (pLastNode->pNext == NULL) { pLastNode->pNext = pNode; break; } pLastNode = pLastNode->pNext; } return SUCCESS; } //Delete the entire linked list space, which is necessary at the end of the program int deleteList(PNODE pListHead) { PNODE pReadyToDelete = pListHead; while(pListHead == NULL) { pListHead = pListHead->pNext; free(pReadyToDelete); pReadyToDelete = pListHead; } return SUCCESS; } void printList(PNODE pListHead) { PNODE pNode = pListHead; if (pNode->pNext == NULL) { printf("This LinkerList has no NODE except Header!\n"); return; } printf("HEAD->"); pNode = pNode->pNext; while(pNode != NULL) { printf("%d->",pNode->iValue); pNode = pNode->pNext; } printf("NULL\n"); return; } u_char g_disk[MAX_DISK_SIZE];//Disk space simulated with memory const int g_freedisksize = MAX_DISK_SIZE; //Free space of virtual disk const char g_freeflag = (char)0xE5; /******************************************************************** Gets the first address of the specified block blockno : Specifies the analog disk block number Therefore, the first address of the FAT table is getBlockAddr(0); The first address of the directory is getBlockAddr(1); *********************************************************************/ void * getBlockAddr(int blockno) { return (g_disk + blockno * BLOCKSIZE); } //Initialize virtual disk space void InitDisk() { PFCB pFCB = NULL; int i, j; memset(g_disk,0,MAX_DISK_SIZE); for (i = 1 ; i <= 4 ; i++) //Set 4 directory entries to idle { pFCB = (PFCB)getBlockAddr(i); pFCB->f_name[0] = g_freeflag; } for (j = 0 ; j <= 4 ; j++) //The first 5 bytes of the FAT table are set to FF, indicating that they have been occupied { ((PFAT)(g_disk+j))->cluster = (u_char)0xFF; } } /******************************************************************** create a file f_name : File name pointer f_content : Pointer to the contents of the file f_size : file length Return value: 0: normal 10: By looking up the FAT table, not enough blocks were found to create the file 11: All directory entries are used up. New files cannot be added. (By checking whether the first byte of the file name is E5, yes means idle) 12: Error creating linked list *********************************************************************/ int CreateFile(pchar f_name, pchar f_content, int f_size) { PFCB pFCB = NULL; //Directory entry (FCB) pointer PFAT pFAT = (PFAT)getBlockAddr(0); //First address of FAT table int blockcount = 0; for(int i=1;i <= 4;i++) { pFCB = (PFCB)getBlockAddr(i); if(pFCB->f_name[0] == g_freeflag) //There are free directory entries { blockcount = (f_size / BLOCKSIZE) + 1; //Find out if there are enough free blocks in FAT, and use a linked list to record the number of free blocks to be occupied int k = blockcount; PNODE pListHead,pNode; if(initList(&pListHead) != SUCCESS) { return 12; //list initialize errno } for (int j=5; j < MAX_FAT ; j++) { if(((PFAT)(pFAT + j))->cluster == (u_char)0) { insert(pListHead,j); if(--k == 0) break; } } printList(pListHead); if (k > 0) return 10; //file size error no //Next, start writing the file contents to the block and change the contents of the directory entry FCB strcpy(pFCB->f_name,f_name); //Write file name pFCB->f_size = f_size; //Write file size pFCB->f_firstblock = pListHead->pNext->iValue; //File start block number int remainbyte = f_size; //How many bytes left to write into the block pNode = pListHead->pNext; for (k = 0 ; k < blockcount ; k++) { if (remainbyte <= BLOCKSIZE) //Less than one block { memcpy(getBlockAddr(pNode->iValue), f_content + (BLOCKSIZE * k), remainbyte); } else { memcpy(getBlockAddr(pNode->iValue), f_content + (BLOCKSIZE * k), BLOCKSIZE); remainbyte -= BLOCKSIZE; } pNode = pNode->pNext; } //Change the FAT table according to the free block number occupied by the file pNode = pListHead->pNext; while (1) { if (pNode->pNext == NULL) { ((PFAT)(pFAT + pNode->iValue))->cluster = (u_char)0xFF; break; } ((PFAT)(pFAT + pNode->iValue))->cluster = pNode->pNext->iValue; pNode = pNode->pNext; } //Reclaim linked list space deleteList(pListHead); return 0; } } return 11;//directory full error no } /******************************************************************** List catalog files No parameters Return value: <0:Abnormal (this will not happen now) Other values: the number of files in the current directory *********************************************************************/ int ListFile() { PFCB pFCB = NULL; int filecount = 0; //File count int bytecount = 0; //Cumulative file size printf("My file system list:\n"); printf("----------------------------------------------\n"); printf("filename\t\t\t filesize\n"); printf("----------------------------------------------\n"); for (int i=1 ; i<=4 ; i++) { pFCB = (PFCB)getBlockAddr(i); if (pFCB->f_name[0] != g_freeflag) { //display file info filecount++; bytecount += pFCB->f_size; printf("%s\t\t\t\t %d Bytes\n",pFCB->f_name,pFCB->f_size); } } if (filecount == 0) { printf("no file in this system now!\n"); } printf("----------------------------------------------\n"); printf("total files : %d \t total bytes : %d Bytes \n\t\t\t remain bytes : %d Bytes\n", filecount,bytecount,g_freedisksize-bytecount); return filecount; } /******************************************************************** Prints the characters of the specified byte filecontent : File content pointer size : Length of characters to print Return value: None *********************************************************************/ void printc(pchar filecontent,int size) { for(int i=0 ; i<size ; i++) { printf("%c",*filecontent); filecontent++; } } /******************************************************************** Show file contents filename : file name Return value: 0: Successful execution <0:Abnormal (this will not happen now) *********************************************************************/ int displayFile(pchar filename) { PFCB pFCB = NULL; PFAT pFAT = (PFAT)getBlockAddr(0); int remainbytes = 0; for (int i=1 ; i<=4 ; i++) { pFCB = (PFCB)getBlockAddr(i); if (!strcmp(pFCB->f_name,filename)) { remainbytes = pFCB->f_size; //Get the location of the first block of the file according to the starting block number in the FCB //Print out the contents of the first block printc((pchar)getBlockAddr(pFCB->f_firstblock),BLOCKSIZE); remainbytes -= BLOCKSIZE; //Then go to FAT to find the block number of subsequent blocks of the file until the end of FF description file is displayed in FAT int firstblock = pFCB->f_firstblock; while ( ((PFAT)(pFAT+firstblock))->cluster != (u_char)0xFF ) { //It indicates that the file has subsequent blocks. Follow this instruction to print the contents of subsequent blocks printc((pchar)getBlockAddr(((PFAT)(pFAT+firstblock))->cluster),BLOCKSIZE); firstblock = ((PFAT)(pFAT+firstblock))->cluster; remainbytes -= BLOCKSIZE; } //Print last block printc((pchar)getBlockAddr(((PFAT)(pFAT+firstblock))->cluster),remainbytes); printf("\n"); return 0; //Successful execution } } return -1;//not found } /******************************************************************** Delete file filename : file name Return value: 0: Successful execution <0:Abnormal (this will not happen now) *********************************************************************/ int deleteFile(pchar filename) { PFCB pFCB = NULL; PFAT pFAT = (PFAT)getBlockAddr(0); for (int i=1 ; i<=4 ; i++) { pFCB = (PFCB)getBlockAddr(i); if (!strcmp(pFCB->f_name,filename)) { //Set the first byte of the file name of the directory entry FCB to E5 pFCB->f_name[0] = g_freeflag; //Then set the block number occupied by the file in FAT to 00 int firstblock = pFCB->f_firstblock; int nextblock = pFCB->f_firstblock; if (((PFAT)(pFAT+firstblock))->cluster == (u_char)0xFF) { //This file only occupies one piece. Clear this item to 0 ((PFAT)(pFAT+firstblock))->cluster = (u_char)0x00; } else //It indicates that the file occupies several blocks. Clear these FAT block indications to 0 { while ( ((PFAT)(pFAT+nextblock))->cluster != (u_char)0xFF ) { nextblock = ((PFAT)(pFAT+firstblock))->cluster; ((PFAT)(pFAT+firstblock))->cluster = (u_char)0x00; firstblock = nextblock; } //Recycle the last piece of file ((PFAT)(pFAT+nextblock))->cluster = (u_char)0x00; } printf("file deleted!\n"); return 0; //Successful execution } } return -1;//not found } int main() { InitDisk(); //Initialize virtual disk space char filename[10]; char filecontent[176];//The file content cannot exceed 176 B char command[10]; int filesize = 0; int err_no = 0; printf("--------------------------------Tips--------------------------------\n"); printf(" The commonds of filesystem\n"); printf(" Create file: mkfile\n"); printf(" File directory: dir\n"); printf(" Display content: type\n"); printf(" Delete file: del\n"); printf(" Exit the system: quit\n"); printf("---------------------------------------------------------------------\n"); while(1) { printf("filesys>"); //Command prompt line scanf("%s",command); if (!strcmp(command,"mkfile")) //To create a file command, you need to provide a file name { printf("\ninput file name(<10 chars):"); scanf("%s",filename); printf("\nReady to create file. Please input file content end with ENTER:\n"); scanf("%s",filecontent); filesize = strlen(filecontent); if((err_no = CreateFile(filename,filecontent,filesize)) == 0) { printf("File created ! \n"); } else { printf("Create failed! err_no=%d\n",err_no); } } else if (!strcmp(command,"dir")) //Column directory command { printf("The stored files are:\n"); ListFile(); } else if(!strcmp(command,"type")) //Show file contents command { printf("Please enter the file you want to view:\n"); scanf("%s",filename); if (displayFile(filename) != 0 ) printf("can't find the file you given!\n"); } else if(!strcmp(command,"del")) //Delete file command { printf("Please enter the file you want to delete:\n"); scanf("%s",filename); if (deleteFile(filename) != 0 ) printf("can't find the file you given!\n"); } else if (!strcmp(command,"quit")) //Exit this system command { break; } else { printf("Unknown command!\n"); } } return 0; }
2. The test data, operation results and experimental conclusions are given.
Run the file simulation system program to display several common options, namely file creation, directory display, content display, file deletion and exit from the system.
1. File creation: enter the mkfile command. At this time, the system will prompt you to enter the file name and file content, and then create a test file and enter the content: hello. At the same time, write this file to the disk.
2. Display directory: enter the dir command, the file system displays the directory in the corresponding simulated disk, and you can see the corresponding file information stored in the disk.
3. Display content: enter the type command and prompt for the name of the file you want to view. You can see that entering test shows that the content stored in the test file is hello.
4. Delete file: enter del to prompt the name of the file you want to delete. If you enter the test file name, you can delete the test file in the file simulation system. You can see that there are no files in the file system by entering dir again.
The main functions of the whole file simulation system are as described above. After use, you can exit the system through the quit command.