The previous analysis of the relevant knowledge of the next cache area, here to analyze the next few important functions;
1, Clear cache: The buffer data and equipment to synchronize
Sys_sync (void) function: First writes the I node from memory to the cache block (writes the I node to the cache block function: sync_inodes (void), scans the modified I node from the memory inode_table[] I node array, The whole block of logic is then read into the cache block according to the I node number. Then, the modified I node in memory is stored in the corresponding location of the cache block, and the cached header structure is scanned, and if the device write request is made if it has been modified, the modified cache block is written to the device disk block;
The Sync_dev (int dev) function is to synchronize data to a specified block device, and the above is to synchronize data for all buffers, since two clear cache functions are similar, so pick a representative analysis:
To synchronize the cache area data and the data on the device for the specified device
//The first time the modified cache data and the device block data are synchronized,
//Then the I node is written to the cache area, and then the buffer and the device block data are synchronized again. //
This is, the first synchronization, you can synchronize the dirty block clean, and then write the I node in the cache area,
//This time synchronization I node becomes simple. Finally synchronizes the I node and the block
int sync_dev (int dev)
{
int i;
struct buffer_head * BH;
BH = start_buffer;//Gets the buffer start address
for (i=0; i<nr_buffers i++,bh++) {//scan all cache headers
if (bh->b_dev!= dev)//To determine if it is The specified equipment
continue;
Wait_on_buffer (BH);//Check for lock
if (Bh->b_dev = = Dev && bh->b_dirt)//Because the last step is likely to be unlocked, during which time the cache block may be modified, So it is best to Judge
Ll_rw_block (WRITE,BH) again,//Invoke write Request function
}
sync_inodes ()//write the changed I node in memory into the buffer.
BH = Start_ The buffer;//step is the same as above 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;
}
2, the cache block inserted into the double linked list/hash linked list, from the double linked list/hash linked list to delete the cache block
static inline void remove_from_queues (struct buffer_head * bh); Remove the cache block from the list, which is not a trick, it is a simple list operation; focus on the Insert list, first adjust the double linked list, This puts the inserted cache block at the end of the list, so there will be: The longer the cache head near the free_list pointer is not used (it will be more efficient to find the idle cache header); and then adjust the hash list, the hash list is not one by one mapped, But the hash list (or hash array table) can be viewed under: http://blog.csdn.net/yuzhihui_no1/article/details/43677663
Inserts the specified buffer into the end of the idle chain and puts it in the hash queue
static inline void insert_into_queues (struct buffer_head * bh) {* *
REE list , put to the tail of the free chain, Free_list is the head pointer/
bh->b_next_free = free_list;
Bh->b_prev_free = free_list->b_prev_free;
Free_list->b_prev_free->b_next_free = BH;
Free_list->b_prev_free = BH;
* Put the "buffer in new hash-queue if it has a device
///If he is a block device, insert the new hash queue
Bh->b_prev = NULL;
Bh->b_next = NULL;
if (!bh->b_dev)//To determine whether the block device return
;
The insert of this hash queue is inserted from the beginning; If you want to insert from the tail, loop to determine if null
Bh->b_next = hash (BH->B_DEV,BH->B_BLOCKNR); Insert into the first block of the resulting hash value queue, that is, the first address
hash (BH->B_DEV,BH->B_BLOCKNR) = Bh;//hash Group pointer to the corresponding hash value queue
bh->b_next- >b_prev = bh;//handles BH First hash item before pointer
}
3. Find Cache block number
The lookup cache block number is basically obtained by using the hash method. Because the hash method is like looking up a dictionary, after several steps can be quickly found, and the double linked list must be a match, just like the first page of the dictionary from the beginning of a page of view, the efficiency significantly lower a lot;
Note: find_buffer (int dev, int block) and get_hash_table (int dev, int block) both functions are looking for cache blocks that already exist (depending on device number and logical block number), and getblk (int Dev, int block) function is to find out whether it has been stored, if it does not exist from the double linked list to obtain a free cache block, and then set up, and put into the hash list;
Find_buffer (int dev, int block) function, this function is to let the device number dev and the logical blocks through the hash () function to get the hash chain header node, and then traverse the entire list to get a specific cache block. Note that the hash () function gets a class of cache blocks, rather than a specific cache block, it is also a hash list rather than a hash table, which is the underlying function (the so-called underlying function is used to be called by other functions), only to find the specific cache block, regardless of whether the cache block is used, or locked.
The get_hash_table (int dev, int block) function is packaged with the Find_buffer () function above. First, you get the cache header through the Find_buff () function, it then references the cache block, waits for the cache block to unlock it, and again determines whether it is the desired cache block (because the process may modify the cache block while waiting for the unlock), and if the cache header structure does not change, return the cache header structure; If you change, decrement the reference, Re-find it again. The function uses a dead loop (not knowing why a dead loop is not used while (1), instead of using for (;;), personal feeling with while (1) easy to understand), put a series of operations in the dead loop, "either die, or find" the function is too persistent.
The following is the ultimate Search cache block function ("Love Apartment" Luzicio Classic lines: The ultimate ... getblk (int dev, int block), this function is designed very well, multiple judgements, and finally get a clean free cache block. And I think it's good that he designed the weights: the weight of the lock is 1, the weight of the change mark is 2, if locked to wait for the next so spend less time, but if modified, and equipment data synchronization (to block equipment to write data is relatively slow), and then have to judge whether the lock , so finally there is no completely clean (cited as 0, clean, unlocked) case, to find a comprehensive weight small is also a good choice.
At the same time to determine the buffer cache modification and lock marks, and define the weight of the change symbol than the lock Mark large #define BADNESS (BH) ((BH)->b_dirt<<1) + (BH)->b_lock)// Gets the cache block specified in the cache/check that the buffer for the specified device number and cache block number is already in the cache. If there is a return of the corresponding buffer head pointer exit;//If it does not exist, you need to set a new item in the cache that corresponds to the device number and block number, and return the corresponding pointer struct buffer_
Head * GETBLK (int dev,int block) {struct Buffer_head * tmp, * BH; Repeat:if (BH = get_hash_table (dev,block)) return bh;//If you're lucky enough to find a cache block in a hash list, thank goodness you don't have to go down the long way. If none of the above is found, it means the desired The cache block is not at all in the hash list//Here's where you need to be aware: in initialization, all cache headers are chained to the loop, so it is called the free double linked list, but the hash list is all null when initialized, and the cache head is added to the hash list when needed. So the following is a double linked list in the loop to find an idle cache header, add to the hash table using TMP = free_list;//from the list header, because this is a LRU list/free block conditional reference count is 0, clean block, not locked do {if (Tmp->b_cou
NT)//This is a hard condition, if there is another process to use, then replace a continue; In fact, the following is looking for clean block, unlocked//below this just or I did not understand thoroughly, and then read it again and again to understand the mystery (perhaps smart you suddenly understand what I call the secret)//if () in the!BH is BH = NULL, do processing; badness (TMP) <badness (BH) In fact, I did not understand the beginning, I think the following is not an if (!
Badness (TMP)) judged it. Actually, if (! Badness (TMP)) This step, if it can exit, is better, that there is no lock, there is no modified free block;//And if the above step does not exit, this badness (TMP) <badness (BH) is useful, This allows for a modified and locked composite weight with the smallest freeBlock if (!BH | |
Badness (TMP) <badness (BH)) {BH = tmp; if (!
Badness (TMP)) break;
}/* and repeat until we find something good/} while (TMP = tmp->b_next_free)!= free_list);
if (!BH) {//If all buffers are found to be in use, then the task will sleep on the buffer_wait queue sleep_on (&buffer_wait);
Goto repeat;//has wake_up () wake up to find the free buffer}///Here has been acquired to the appropriate free cache block, but to check whether the locked Wait_on_buffer (BH);
if (bh->b_count)//If it is occupied by other processes, then you have to find a cache block goto repeat; while (Bh->b_dirt) {//If the buffer has modified data, Sync_dev (Bh->b_dev);//sync to the specified device wait_on_buffer (BH);/wait to unlock if (Bh->b_coun T) Goto repeat;//is also occupied, then look for a suitable free cache block again}/* note!! While we are slept waiting for this block, somebody else might * * already have added "this" blocks to the cache. Check IT///checks if there are other processes in process sleep that put the idle cache block into use if (Find_buffer (dev,block)) Goto repeat;//Re-locate * OK, FINALLY we know that This are the only one of it's kind,/* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */
/Set the last-found free cache block bh->b_count=1; Bh->b_dirt=0;
bh->b_uptodate=0;
Remove_from_queues (BH)//Take the cache header out, because to change the device number and cache block number, so the position in the hash queue to change bh->b_dev=dev;
bh->b_blocknr=block;
Insert_into_queues (BH)//modified dev and block after inserting again to the correct place return BH; }
4, from the device read data to the cache
The bread (int dev,int block) function reads a logical chunk of content from a device onto a cache block, and the procedure is probably to get a cache block through the getblk (dev, block) function, and return the cache block directly if the data is valid. In fact, the cache blocks obtained by get_hash_table (Dev, block) function correspond to block number one by one on the dev device, and even if they are not found, they will request a corresponding cache block from the free list, if the data on the cache block is invalid. Then it is necessary to send a read data request to the device, read the data on the logical block to the cache block, and return the cache block;
Bread_page (unsigned long address, int dev, int b[4]) function is to read four pieces of logical block data to the cache block, and then copy from the cache block to the specified memory;
At the same time read four block of cache block data to the specified memory, each piece is 1024, a page is 4096, so is 4 block of cache block content
//parameter: address is memory addresses; dev device number; b[4]4 device data block number
void Bread_ Page (unsigned long address,int dev,int b[4])
{
struct buffer_head * bh[4];
int i;
For (i=0 i<4 i++)
if (B[i]) {
if (bh[i] = getblk (dev,b[i))//Get the cache block of the specified device and valid block number
if (!bh[i]->b_uptodat e)//If the data in the cache block is invalid, the Read device request
Ll_rw_block (Read,bh[i]);
} else
Bh[i] = null;//block number is invalid, no pipe is required, the corresponding cache block is placed to NULL
The following is a copy of the contents of the 4 cache to the specified memory location for
(i=0 i<4; i++,address + = block_size)
if (Bh[i]) {//cache block valid
Wait_on_ Buffer (bh[i]);//wait for unlock
if (bh[i]->b_uptodate)//If data is valid
copyblk ((unsigned long) bh[i]->b_data,address )//copy content to address
Brelse (bh[i]);//Free Cache
}
}
struct Buffer_head * Breada (int dev,int ...) The function will read several blocks of data in advance to the cache block, but its implementation is similar to the above two functions, the only difference is that the function uses, variable parameter list technology, clever implementation of prefetching data block data;
Similar to the bread function, but the function will read some blocks in advance, the last parameter of the variable argument list
is a variable parameter, a series of specified block numbers, a successful return of the first block's cache size pointer, or null
struct Buffer_head * Breada (int dev,int, ...)
{
va_list args;
struct Buffer_head * bh, *tmp;
Va_start (Args,first);
if (!) ( BH=GETBLK (Dev,first))//view the cache block specified for the first device number and block number
panic ("Bread:getblk returned null\n");
if (!bh->b_uptodate)
ll_rw_block (READ,BH)//If the data in the cache block is invalid, send read data request
//order fetch to other data blocks, as above, but degrade the reference
while ((First=va_arg (args,int)) >=0) {
tmp=getblk (dev,first);
if (TMP) {
if (!tmp->b_uptodate)
ll_rw_block (READA,BH);
tmp->b_count--;
}
}
Va_end (args);
Wait_on_buffer (BH);//wait for the first cache block to unlock if
(bh->b_uptodate)//If the data is valid, return the first cache header returned to
BH;
Brelse (BH);//Otherwise free the cache block, returns null return
(NULL);
}
5. Buffer initialization function
Although in the previous blog to analyze the cache initialization code, but here is still to see him, because the function design is really good.
Buffer initialization function//parameter: Buffer_end point to the end of the buffer memory; for the system has 16MB memory, cache is 4MB;8MB--2MB void Buffer_init (long buffer_end) {struct BUFFER_
Head * h = start_buffer;
void * b;
int i;
if (buffer_end = = 1<<20)//640k~1m is used for video memory and BIOS ROM; The high-end address should be set to b=640kb B = (void *) (640*1024); else B = (void *) buffer_end;//otherwise high address or set buffer_end//The following code used to initialize the buffer, set up the free buffer loop list, and get the number of cache blocks in the system//high-end partition 1k cache block, low end set corresponding cache block head
The result points to the high-end cache block while ((b-= block_size) >= ((void *) (h+1)) {H->b_dev = 0;//the device number using the cache H->b_dirt = 0;//Modify Flag
H->b_count = 0;//reference count H->b_lock = 0;//Lock Flag h->b_uptodate = 0;//valid flag h->b_wait = NULL;
H->b_next = NULL;
H->b_prev = NULL;
H->b_data = (char *) b;//the cache block pointing to the end h->b_prev_free = h-1;
H->b_next_free = h+1; h++; Then to the next cache header nr_buffers++;//cache block Count if (b = = (void *) 0x100000)//if address B decrements to 1M, skip to 640KB because this address is used by BIOS ROM and video memory using B = (void
*) 0xa0000; h--;//points to the last valid cache header structure Free_list = start_buffer;//idle pointer to the first header structure Free_list->b_prev_free = h;//do a circular list h->B_next_free = Free_list;
for (i=0;i<nr_hash;i++)//Initialize HASH table hash_table[i]=null; }
Reprint please indicate the author and original source, original address: http://blog.csdn.net/yuzhihui_no1/article/details/43703153
If there is anything wrong, you are welcome to correct, work together to study together.