• 找到合适的缓冲块


    // linux/fs/buffer.c

    205 #define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock)
    206 struct buffer_head * getblk(int dev,int block)
    207 {
    208 struct buffer_head * tmp, * bh;
    209
    210 repeat:
    211 if (bh = get_hash_table(dev,block))
    212 return bh;
    213 tmp = free_list;
    214 do {
    215 if (tmp->b_count)
    216 continue;
    217 if (!bh || BADNESS(tmp)<BADNESS(bh)) {
    218 bh = tmp;
    219 if (!BADNESS(tmp))
    220 break;
    221 }
    222 /* and repeat until we find something good */
    223 } while ((tmp = tmp->b_next_free) != free_list);

    struct buffer_head *tmp,*bh就是缓冲块头结构的两个变量,然后,进入repeat循环。

    在repeat循环中,首先,先搜索hash表,检查是不是所需要的缓冲块已经被读入缓冲区了,即if(bh=get_hash_table(dev,block))  return bh;。那么,我们来看get_hash_table是如何实现的

    //   linux/fs/buffer.c

    183 struct buffer_head * get_hash_table(int dev, int block)
    184 {
    185 struct buffer_head * bh;
    186
    187 for (;;) {
    188 if (!(bh=find_buffer(dev,block)))
    189 return NULL;
    190 bh->b_count++;
    191 wait_on_buffer(bh);
    192 if (bh->b_dev == dev && bh->b_blocknr == block)
    193 return bh;
    194 bh->b_count--;
    195 }
    196 }


    可以看到,这个函数又会调用find_buffer,我们再往下走

    // linux/fs/buffer.c

    166 static struct buffer_head * find_buffer(int dev, int block)                 
    167 {
    168 struct buffer_head * tmp;
    169
    170 for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next)
    171 if (tmp->b_dev==dev && tmp->b_blocknr==block)
    172 return tmp;
    173 return NULL;
    174 }

    这个函数的主体是一个for循环,tmp=hash(dev,block),我们再看hash函数的实现

    //linux/fs/buffer.c

    128 #define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH)
    129 #define hash(dev,block) hash_table[_hashfn(dev,block)]

    可以看到,hash函数其实就是一个宏,hash函数为(dev^block)%NR_HASH,其中NR_HASH为307。这样,也就是说,先从标号为hash(dev,block)的缓冲头开始找,如果往下走,能够找到一个该设备的,且为该数据块的缓冲块已经存在于内存中(即if(tmp->b_dev==dev&&tmp->b_blocknr==block)。

    如果找不到,则返回NULL

    如果找到了,

    187     for (;;) {
    188 if (!(bh=find_buffer(dev,block)))
    189 return NULL;
    190 bh->b_count++;
    191 wait_on_buffer(bh);
    192 if (bh->b_dev == dev && bh->b_blocknr == block)
    193 return bh;
    194 bh->b_count--;
    195 }

    便会将这个缓冲块的引用计数加1,然后如果被锁,则等待其解锁,待锁解开后,if(bh->b_dev==dev&&bh->b_blocknr==block),即再进行一次确认,如果确认成功,将bh返回,否则,将bh的引用计数减1(因为在等待解锁前已经将其加1)。

    这里的再次确认是有必要的,因为在等待其解锁的过程中,有可能这个块已经被别的进程使用了。




    现在再回过头去看getblk函数

    //linux/fs/buffer.c

    210 repeat:
    211 if (bh = get_hash_table(dev,block))
    212 return bh;
    213 tmp = free_list;
    214 do {
    215 if (tmp->b_count)
    216 continue;
    217 if (!bh || BADNESS(tmp)<BADNESS(bh)) {
    218 bh = tmp;
    219 if (!BADNESS(tmp))
    220 break;
    221 }
    222 /* and repeat until we find something good */
    223 } while ((tmp = tmp->b_next_free) != free_list);


    这里,如果没找到,将会进入do while循环。  

    if(tmp->count)   countinue;如果缓冲块正在被使用,则跳过  

    直到找到一个合适的缓冲块。

    如果循环了链表的一圈,还是没有找到合适的缓冲块,那么将这个进程挂起,也就是

    224     if (!bh) {
    225 sleep_on(&buffer_wait);
    226 goto repeat;
    227 }

    待其被唤醒后,重新进入repeat循环,进行合适缓冲块的查找。

    228     wait_on_buffer(bh);
    229 if (bh->b_count)
    230 goto repeat;
    231 while (bh->b_dirt) {
    232 sync_dev(bh->b_dev);
    233 wait_on_buffer(bh);
    234 if (bh->b_count)
    235 goto repeat;

    如果经过了一次repeat循环,找到了合适的缓冲块,那么,等待这个缓冲块的解锁,即wait_on_buffer(bh),注意这个函数并不是真正的等待,我们可以去看它的内部实现,只是在该缓冲块被锁定时,调用schedule函数进入任务切换,否则,函数直接返回。

    同样,在等待的过程中,可能别的进程又将其占用了(这里面的原因个人认为应该和进程调试中,进程被唤醒的方式有关,也就是说,有两个进程同时等待同一个缓冲区,那么,在缓冲区可用时,系统肯定会发送一个信号,至于在schedule函数中,是怎么对task_struct数组进行遍历的,就决定了这里需不需要再进行检查)。

    如果没被占用,但是该缓冲区已经被修改,那么进入while循环

    sync_dev(bh->dev)是将所对应的设备进入写入操作。

    在操作时,会对该块加锁,所以需要等待其解锁。

    如果解锁完毕后,该块又被占用了(道理同上),那么,前功尽弃,只好重新进入repeat循环,再去寻找新的可用的缓冲块。

    237 /* NOTE!! While we slept waiting for this block, somebody else might */
    238 /* already have added "this" block to the cache. check it */
    239 if (find_buffer(dev,block))
    240 goto repeat;

    这几句没看明白,虽然有注释。。。  等下问下同学和老师再回来做解释。

    241 /* OK, FINALLY we know that this buffer is the only one of it's kind, */
    242 /* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */
    243 bh->b_count=1;
    244 bh->b_dirt=0;
    245 bh->b_uptodate=0;
    246 remove_from_queues(bh);
    247 bh->b_dev=dev;
    248 bh->b_blocknr=block;
    249 insert_into_queues(bh);
    250 return bh;

    看注释可以看出来,到这里,”OK,FINALLY we know that this buffer is the only one of it's kind,"

    linus还故意用大写字母,可见他也是松了一口气~~

    于是,将此缓冲区占用(bh->b_count=1),清除其脏标志(bh->dirt=0),以及有效标志(bh->b_uptodate=0)

    既然它已经被占用了,那么便把它移出空闲链表

    remove_from_queues(bh)

    bh->b_dev=dev;

    bh->b_blocknr=blockl;

    移出后,把它放放到一个新的合适的位置。(因为这时候,有可能另一个进程需要使用同样的缓冲块,在hash时,最好能够尽快找到这个块。)

    insert_into_queues(bh);

    现在,一切大功告成,

    return bh;









  • 相关阅读:
    异常详细信息: System.Data.SqlClient.SqlException:过程或函数 需要参数 但未提供该参数。
    silverlight error message,ErrorCode:2254 ErrorCode: 1001
    每日英语 词汇
    每日词汇
    电子商务网站用户体验根据用户的习惯进行推荐
    刚做项目的时候
    silverlight 不可
    在Centos6上安装RabbitMQ的过程(有点坑)
    SpringBoot整合Redis
    在IDEA中创建工程并将工程添加至Git
  • 原文地址:https://www.cnblogs.com/yangce/p/2307997.html
Copyright © 2020-2023  润新知