inode

The inode is used to represent a file, which can be anything from a familiar file to a directory to a pipe. The disk’s inode table contains inode information.

The inode structure on disk is

struct d_inode { unsigned short i_mode; unsigned short i_uid; unsigned long i_size; unsigned long i_time; unsigned char i_gid; unsigned char i_nlinks; unsigned short i_zone[9]; // Direct block (0-6), indirect (7), or double indirect (8) logical block number};Copy the code

The inode structure in memory is

Struct m_inode {// unsigned short i_mode; unsigned short i_uid; unsigned long i_size; unsigned long i_mtime; unsigned char i_gid; unsigned char i_nlinks; unsigned short i_zone[9]; /* These are in memory also */ struct task_struct * i_wait; unsigned long i_atime; unsigned long i_ctime; unsigned short i_dev; unsigned short i_num; unsigned short i_count; unsigned char i_lock; unsigned char i_dirt; unsigned char i_pipe; unsigned char i_mount; unsigned char i_seek; unsigned char i_update; };Copy the code

iput

If the inode is a pipe, wake up the process waiting for the pipe and subtract 1 from the number of references to the inode. If the inode is 0, the pipe is free. For block devices, data is synchronized between buffers, inodes, and devices.

fs/inode.c

void iput(struct m_inode * inode) { if (! inode) return; Wait_on_inode (inode); if (! Inode ->i_count) // panic("iput: trying to free free inode"); // If it is a pipe I node, wake up the process waiting for the pipe with the number of references reduced by 1, and return if there are still references. // Otherwise release the memory page occupied by the pipe and reset the reference count, modified flag, and pipe flag for this node, // and return. For pipe nodes, inode->i_size stores this memory as well. if (inode->i_pipe) { wake_up(&inode->i_wait); if (--inode->i_count) return; free_page(inode->i_size); inode->i_count=0; inode->i_dirt=0; inode->i_pipe=0; return; } // If the device number of node I is 0, then the reference count of this node is decreased by 1. For example, the I node used for pipe operation // has the device number 0. If (! inode->i_dev) { inode->i_count--; return; } // If it is the I node of the block device file, the device ID is displayed in logical block field 0(i_zone[0]), and the device is refreshed. // Wait until the I node is unlocked. if (S_ISBLK(inode->i_mode)) { sync_dev(inode->i_zone[0]); wait_on_inode(inode); } // If the reference count of the I node is greater than 1, the value of the reference count of the I node is reduced by 1. If the number of links on node I is 0, the file corresponding to node I is deleted. All logical blocks of the I node are freed, and the I node is freed. The function free_inode() is used to release/release the I node, that is, to reset the bitmap of the I node corresponding to the I node and clear the structure content of the I node. repeat: if (inode->i_count>1) { inode->i_count--; // If the reference count of the I node is greater than 1, then the reference count is reduced by 1. } // the reference count of the I node is 1 if (! Inode ->i_nlinks) {// If the number of links on node I is 0, the file corresponding to node I is deleted. All logical blocks of the I node are freed, and the I node is freed. truncate(inode); free_inode(inode); return; } // If the I node has been modified, write back to update the I node and wait for the I node to be unlocked. Since it is necessary to wait for sleep while writing the I node //, other processes may modify the I node at this time, so the process needs to repeat the above judgment process (repeat) after it is awakened. if (inode->i_dirt) { write_inode(inode); Wait_on_inode (inode); wait_on_inode(inode); goto repeat; } i_count is 1, the link count is not zero, and the contents of the I node have not been modified. So at this point, just decrement the reference count of I by 1 and return. If the i_count of the I node is 0, // the I node is released. inode->i_count--; return; }Copy the code

read_inode

Calculates the block number based on the inode node number, although there may be more than one inode in the block, and reads the specified block number into the buffer.

static void read_inode(struct m_inode * inode) { struct super_block * sb; struct buffer_head * bh; int block; // Lock the I node and obtain the superblock of the device where the node resides. lock_inode(inode); if (! (sb=get_super(inode->i_dev))) panic("trying to read inode without dev"); // Logical block ID of the device where the I node resides = (Startup block + Super block) + Number of blocks occupied by the BITmap of the I node + Number of blocks occupied by the bitmap of the logical block // + (I node ID -1)/Number of I nodes contained in each block. Although node I is numbered from 0, node I 0 is not used, and // the corresponding node structure of node 0 I is not saved on disk. Therefore, the i-th block of the disk block storing the I-node is the i-node structure with the i-node number of 1--16 // instead of 0--15. Therefore, you need to subtract 1 when calculating the disk block where the I node structure corresponds to the I node ID, that is, // B= (I node ID -1)/ Number of structures containing I nodes per block. For example, the i-node structure of node number 16 should be on the // block where B= (16-1) /16 = 0. Here we read the logical block of the I node from the device and copy the contents of the specified I node to the position indicated by the inode pointer. block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +(inode->i_num-1)/INODES_PER_BLOCK; if (! (bh=bread(inode->i_dev,block))) panic("unable to read i-node block"); *(struct d_inode *)inode =((struct d_inode *)bh->b_data)[(inode->i_num-1)%INODES_PER_BLOCK]; // Finally release the buffer block read in and unlock the I node. brelse(bh); unlock_inode(inode); }Copy the code

write_inode

Read the block number of the device into the buffer, then update the corresponding inode information in the buffer, change b_DIRT to 1, and wait for the write back to disk. Wakes up the process waiting on the buffer

static void write_inode(struct m_inode * inode) { struct super_block * sb; struct buffer_head * bh; int block; // Lock the I node. If the I node is not modified or its device ID is zero, unlock the I node and exit. // For unmodified I nodes, the contents are the same as in the buffer or device. Then get the superblock of the I node. lock_inode(inode); if (! inode->i_dirt || ! inode->i_dev) { unlock_inode(inode); return; } if (! (sb=get_super(inode->i_dev))) panic("trying to write inode without device"); // Logical block ID of the device where the I node resides = (Startup block + Super block) + Number of blocks occupied by the BITmap of the I node + Number of blocks occupied by the bitmap of the logical block // + (I node ID -1)/Number of I nodes contained in each block. We read the logical block where node I is located from the device, and copy the I node information // to the item position of node I corresponding to the logical block. block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks + (inode->i_num-1)/INODES_PER_BLOCK; if (! (bh=bread(inode->i_dev,block))) panic("unable to read i-node block"); // Find the block where the inode belongs. ((struct d_inode *)bh->b_data)[(inode->i_num-1)%INODES_PER_BLOCK] =*(struct d_inode *)inode; // Then set the buffer modified flag, and the I node content is consistent with the buffer, so the modified flag is set to zero. Then release the buffer containing the I node and unlock the I node. bh->b_dirt=1; // Wait for write back hard disk inode->i_dirt=0; brelse(bh); Unlock_inode (inode); unlock_inode(inode); }Copy the code
// Each block has several inodes. #define INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct d_inode))) // Number of items in each block #define DIR_ENTRIES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct dir_entry)))Copy the code

When the block size is 1024 bytes, block 0 is reserved and block 1 is the superblock

sync_dev

The data in the buffer is synchronized to the specified device to free up space for inode information synchronization. Then the inode information in the inode_table is synchronized to the device. The buffer that becomes dirty during inode information synchronization is written back to the device. The buffer is up to date and the inode information is up to date.

int sync_dev(int dev) { int i; struct buffer_head * bh; bh = start_buffer; for (i=0 ; i<NR_BUFFERS ; i++,bh++) { if (bh->b_dev ! = dev) // Continue with buffer blocks that are not device dev; wait_on_buffer(bh); if (bh->b_dev == dev && bh->b_dirt) ll_rw_block(WRITE,bh); // write buffer data to disk} sync_inodes(); Bh = start_buffer; bh = start_buffer; bh = start_buffer; for (i=0 ; i<NR_BUFFERS ; i++,bh++) { if (bh->b_dev ! = dev) continue; wait_on_buffer(bh); if (bh->b_dev == dev && bh->b_dirt) ll_rw_block(WRITE,bh); } return 0; }Copy the code

sync_inodes

Synchronize non-piped inode information from inode_table to disk. The inode_table array size is 32, and Linux0.11 can only open 32 files. These 32 inodes are shared by all processes.

void sync_inodes(void) { int i; struct m_inode * inode; inode = 0+inode_table; For (I =0; i<NR_INODE ; i++,inode++) { // NR_INODE = 32 wait_on_inode(inode); if (inode->i_dirt && ! inode->i_pipe) write_inode(inode); }}Copy the code

iget

fs/inode.c

Gets an inode based on the specified node number. Select a free inode from the inode table, which may or may not be used by the inode, and then search the inode table for an inode with the device number and node number specified. If the inode is found, the number of references to the inode will be increased by 1

struct m_inode * iget(int dev,int nr) { struct m_inode * inode, * empty; if (! dev) panic("iget with dev==0"); empty = get_empty_inode(); // Select a free inode from the inode table and scan the I node table. Find the I node whose node number nr is specified. And increments the number of references to this object. If the device ID of the I node is not the specified device ID or the node ID is not the specified node ID, the scan continues. inode = inode_table; while (inode < NR_INODE+inode_table) { if (inode->i_dev ! = dev || inode->i_num ! = nr) { inode++; continue; } // If the device number dev and node number nr are specified, wait for the node to be unlocked. The I node table may change while waiting for the node to unlock //. So make the same judgment again. If there is a change, // rescan the entire I node table again. wait_on_inode(inode); if (inode->i_dev ! = dev || inode->i_num ! = nr) { inode = inode_table; continue; } // Go here to find the corresponding I node. Increment the I node reference count by 1. Then check further to see if it // is the installation point of another file system. If so, find the installed file system root node and return. If // the I node is indeed the installation point of another file system, search the superblock table for the superblock installed on the I node. // If it is not found, an error message is displayed, and the empty node obtained at the beginning of this function is put back, and the pointer to the I node is returned. inode->i_count++; if (inode->i_mount) { int i; for (i = 0 ; i<NR_SUPER ; i++) if (super_block[i].s_imount==inode) break; if (i >= NR_SUPER) { printk("Mounted inode hasn't got sb\n"); if (empty) iput(empty); return inode; } // The file system superblock installed on the inode node has been found. Write the I node back to disk // and take the device number from the file system superblock installed on the sub-i node and set the I node number to ROOT_INO, which is 1. Then rescan the entire I node table to get the root I node information for the installed file system. iput(inode); dev = super_block[i].s_dev; nr = ROOT_INO; inode = inode_table; continue; } // Finally we find the corresponding I node. Therefore, we can discard the free I node temporarily requested at the beginning of this function and return // the pointer to the I node found. if (empty) iput(empty); return inode; } // If we do not find the specified I node in the I node table, we will create the I node in the I node table using the empty I node. The I node information is read from the corresponding device and the I node pointer is returned. if (! empty) return (NULL); inode=empty; inode->i_dev = dev; inode->i_num = nr; read_inode(inode); return inode; }Copy the code

get_empty_inode

Select * from inode_table where i_count = 0, i_dirt = 0 and the unlocked inode.

struct m_inode * get_empty_inode(void) { struct m_inode * inode; static struct m_inode * last_inode = inode_table; Int I; do { inode = NULL; for (i = NR_INODE; i ; i--) { if (++last_inode >= inode_table + NR_INODE) last_inode = inode_table; // Start from the beginning again last_inode->i_count) { inode = last_inode; if (! inode->i_dirt && ! inode->i_lock) break; }} // If no free I node is found (inode=NULL), print out the I node table for debugging and stop. if (! inode) { for (i=0 ; i<NR_INODE ; i++) printk("%04x: %6d\t",inode_table[i].i_dev, inode_table[i].i_num); panic("No free inodes in mem"); } wait_on_inode(inode); While (inode->i_dirt) {write_inode(inode); wait_on_inode(inode); } while (inode->i_count);} while (inode->i_count); Memset (inode,0,sizeof(*inode)) memset(inode,0,sizeof(*inode)); inode->i_count = 1; // The initial number of references is 1. }Copy the code