preface
File, abstract a collection of disk blocks. This is the third level of abstraction, from file to file system: file system, abstract the whole disk (the fourth level of abstraction)Tip: the following is the main content of this article
1, File system
When using a disk, the disk in the eyes of users is a pile of organized files with tree structure. The file system is to realize the mapping from files to disk blocks
Advantages of directory tree:
But with such a mapping, how can I use the a file in / my/data/a ("/" indicates the root directory)?
Find the file through the file path name, that is, first find the FCB of file a through the file path
Because the file name needs to be compared to find the file, the file name of the subdirectory needs to be stored in the directory, and the file needs to be operated on the disk. Therefore, the FCB of the subdirectory also needs to be stored.
However, in fact, we only need to compare one file name, but read a large number of FCBS. The system efficiency is low. Therefore, we can store the subdirectory name + the FCB address corresponding to the directory in the directory. The file name: index value is stored in the directory. To realize this index structure, the disk needs to be matched. The disk needs to be divided into a continuous area to store FCB blocks, so that the mapping from index value to FCB address can be established
Because the root directory has no upper level directory to save its index value, you need to find a fixed address on the disk to store the root directory
Therefore, the operating system needs to complete the following two tasks:
1. The directory stores the file name of the subdirectory and the index value of the subdirectory FCB
2. The disk block shall be divided into a continuous area for storing FCB blocks, and an initial address shall be defined as the index of the root directory
The disk blocks are as follows:
(1) inode bitmap: which inodes are idle and which are occupied
(2) Disk block bitmap: which disk blocks are free, the size of the hard disk is different, and the size of this figure is also different
(3) Boot block: disk boot and initialization
(4) Super block: record the size of two bitmaps and other information
Idle bitmap (bit vector): 0011110011101
Indicates that disk blocks 2, 3, 4, 5, 8, 9, 10, and 12 are idle
Complete the disk usage process under all mappings
2, File system code implementation
As long as the file system is mapped to the corresponding sector on the disk, plus the previous knowledge, isn't it the implementation of the file system?
Directory resolution:
Find the corresponding file in the open () function and open it, and open () calls sys_open()
stay linux/fs/open.c in int sys_open(const char* filename, int flag) { i=open_namei(filename,flag,&inode); //Resolution path ... } int open_namei(...) { dir=dir_namei(pathname,&namelen,&basename); .... } static struct m_inode *dir_namei() { dir=get_dir(pathname); }
After a series of calls, get completes the directory resolution_ dir
static struct m_inode *get_dir(const char *pathname) { if((c=get_fs_byte(pathname))=='/') //root directory { inode=current->root; pathname++; } else if(c) inode=current->pwd; //Parsing starts here while(1) { if(!c) return inode; //Correct exit of function bh=find_entry(&inode,thisname,namelen,&de); int inr=de->inode; int idev=inode->i_dev; inode=iget(idev,inr); //Read the next inode according to the directory entry } }
(1)root: find the root directory;
(2)find_entry: read directory entries from the directory;
(3)inr: is the index node number in the directory entry;
(4)iget: read the next level directory again
For directory resolution, first find a starting point. If the pathname starts from /, start from the inode of the root directory. Otherwise, start from the inode of the current directory
Resolution of root inode
inode=current->root; void init(void) { setup((void *) &drive_info); ... } sys_setup(void * BIOS)//In kernel/hd.c { hd_info[drive].head = *(2+BIOS); hd_info[drive].sect = *(14+BIOS); mount_root(); ... } void mount_root(void)//In fs/super.c { mi=iget(ROOT_DEV,ROOT_INO)); current->root = mi; ... }
The root of all processes is inherited from process 1. Mount should be executed in the init() function of process 1_ The root () function is used to read the inode of the root directory into memory and associate it with the PCB of process 1
With the inode of the starting directory file, read out the contents of the directory file, then compare the file name on the file path with the directory items in the directory one by one, and continue to parse downward until the path name is completely processed, and finally find the inode of the target file
Read inode (iget() function)
struct m_inode * iget(int dev, int nr) { struct m_inode * inode = get_empty_inode(); inode->i_dev=dev; inode->i_num=nr; read_inode(inode); return inode; } static void read_inode(struct m_inode *inode) { struct super_block *sb=get_super(inode->i_dev);; lock_inode(inode); block=2+sb->s_imap_blocks+sb->s_zmap_blocks+ (inode->i_num-1)/INODES_PER_BLOCK; bh=bread(inode->i_dev,block); inode=bh->data[(inode->i_num-1)%INODES_PER_BLOCK]; unlock_inode(inode); }
The above is realized according to the division of disks
As can be seen from the above figure, the starting position of inode array is behind the boot block, super block and two bitmap arrays
Starting position of inode array on disk = boot block size + super block size + s_imap_blocks size + s_zmap_blocks size
Start parsing Directory find_entry()
Function find_entry reads the directory item array according to the inode of the directory file, and then matches the directory items one by one, that is, while (I < entries) if (match (namelen, name, de))
stay fs/namei.c in static struct buffer_head *find_entry(struct m_inode **dir, char *name, ..., struct dir_entry ** res_dir) { int entries=(*dir)->i_size/(sizeof(struct dir_entry)); int block=(*dir)->i_zone[0]; *bh=bread((*dir)->i_dev, block); struct dir_entry *de =bh->b_data; while(i<entries) //Entries is the number of directory entries { //#define BLOCK_ Size 1024 two sectors if((char*)de> = BLOCK_SIZE+bh->b_data) { brelse(bh); block=bmap(*dir,i/DIR_ENTRIES_PER_BLOCK); bh=bread((*dir)->i_dev,block); de=(struct dir_entry*)bh->b_data; } //Read in the directory entries on the next block and continue to match if(match(namelen,name,de)) { *res_dir=de;return bh; } de++; i++; } }
De: directory entry
#define NAME_LEN 14 struct dir_entry { unsigned short inode; //i node number char name[NAME_LEN]; //file name }
iget is used to read the index node, calculate the disk block number of the inode according to the inode number (iget parameters) and the initial position of the inode array, and then send a request to the "elevator" queue by bread to complete the disk reading and writing.
summary
Tip: here is a summary of the article: