Linux-0.11 kernel目录ll_rw_blk.c详解
创始人
2025-05-29 09:15:15
0

Linux-0.11 kernel目录ll_rw_blk.c详解

该模块的作用是处理块设备的读写,其中最重要的函数就是电梯算法add_request函数和ll_rw_block函数。

add_request

static void add_request(struct blk_dev_struct * dev, struct request * req)

该函数的作用是将块设备读写请求插入到电梯队列中。

首先将数据的脏数据标志置为0。

if (req->bh)req->bh->b_dirt = 0;

如果当前设备的请求为空,就将入参中的请求作为设备电梯队列的头节点。并且立即调用request_fn。request_fn对于不同的设备对应不同的回调函数,对于硬盘设备而言,request_fn指的是do_hd_request,关于do_hd_request,将在hd.c中进行讲解。

if (!(tmp = dev->current_request)) {dev->current_request = req;sti();(dev->request_fn)();return;
}

上面的部分处理了请求队列只有一个请求的场景,接下来便是当请求队列有多个请求时,如何处理优先级顺序的逻辑,也就是电梯算法部分,其中最难理解的便是宏定义IN_ORDER。IN_ORDER的定义如下所示。

#define IN_ORDER(s1,s2) \
((s1)->cmd<(s2)->cmd || ((s1)->cmd==(s2)->cmd && \
((s1)->dev < (s2)->dev || ((s1)->dev == (s2)->dev && \
(s1)->sector < (s2)->sector))))

上述代码是比较难懂的,可以使用if-else来帮助理解

bool inorder(request &s1, request &s2)
{if(s1.cmd < s2.cmd>){return true;}else if(s1.cmd == s2.cmd){if(s1.dev < s2.dev){return true;}else if(s1.dev == s2.dev){if(s1.sector < s2.sector){return true;}return false;//s1.sector > s2.sector}return false;//s1.dev > s2.dev}return false;//s1.cmd > s2.cmd
}

展开上面的if-else结构逻辑就清晰了很多,IN_ORDER实际上就是依次对操作类型,设备号, 扇区号作比较,并且操作类型优先级大于设备号,设备号优先级大于扇区号。

对于操作类型而言,读操作优先级大于写操作。对于设备号而言,设备号小的设备优先级大于设备号大的设备的优先级。对于扇区而言,扇区序号小的扇区优先级高于扇区序号大的扇区。

有了这个认识之后,再看下面的语句,就会简单很多,实际上就是根据优先级找到合适的位置插入数据。

for ( ; tmp->next ; tmp=tmp->next)if ((IN_ORDER(tmp,req) || !IN_ORDER(tmp,tmp->next)) &&IN_ORDER(req,tmp->next))break;
req->next=tmp->next;
tmp->next=req;

下面的这一段代码可以用两个if语句进行替代,即定向扫描折返扫描两个场景。

if ((IN_ORDER(tmp,req) || !IN_ORDER(tmp,tmp->next)) &&IN_ORDER(req,tmp->next))

条件1:定向扫描, req在当前扫描的方向上

if(tmp > req && req > tmp->next)
{req->next=tmp->next;tmp->next=req;	
}

条件2: 折返扫描,req在下一轮扫描的方向上

if(tmp < tmp->next && req > tmp->next)
{req->next=tmp->next;tmp->next=req;	
}

这里需要阐明的是,Linux-0.11实际使用的磁盘扫描算法是C-SCAN算法,也是电梯算法(可戏称为跳楼机)。

其思路就是只单向寻道,到头后直接复位再次沿同方向寻道,这样对于所有磁盘位置的请求都是公平的。

我们通过下面的代码实际感受一下这个过程,我们固定cmd和dev, 只让sector号有区别,依次插入50, 80, 60, 30, 20 ,看看最后的结果如何。

#include 
#include #define READ 0
#define WRITE 1struct request {int dev;		/* -1 if no request */int cmd;		/* READ or WRITE */int errors;unsigned long sector;unsigned long nr_sectors;char * buffer;//struct task_struct * waiting;//struct buffer_head * bh;struct request * next;
};#define IN_ORDER(s1,s2) \((s1)->cmd<(s2)->cmd || (s1)->cmd==(s2)->cmd && \((s1)->dev < (s2)->dev || ((s1)->dev == (s2)->dev && \(s1)->sector < (s2)->sector)))// 作为解析,以明白的分支结构重写一个内容一样的inorder函数
bool inorder(struct request *s1,struct request *s2)
{if (s1->cmdcmd){return true;//only when s1->cmd = READ; s2->cmd = WRITE;}else if ( s1->cmd == s2->cmd ){if (s1->dev < s2->dev){return true;// when (s1->cmd==s2->cmd) && (s1->devdev)}else if ( s1->dev == s2->dev ){if (s1->sectorsector){return true;// when when (s1->cmd==s2->cmd) && (s1->sectorsector)}return false;// when when (s1->cmd==s2->cmd) && (s1->sector>=s1->sector)}return false;// when (s1->cmd==s2->cmd) && (s1->dev>s2->dev)}return false;// when s1->cmd>s2->cmd
}void AddRequest(struct request * &head,struct request *req)
{if (!head){head = req;head->next = 0;return ;}struct request * tmp = head;for (;tmp->next;tmp = tmp->next){if ( ( IN_ORDER(tmp,req)||!IN_ORDER(tmp,tmp->next)) &&IN_ORDER(req,tmp->next)){break;}}req->next = tmp->next;tmp->next = req;return;
}void PrintQueen(struct request * n)
{while (n){printf("(%d,%d,%d),",n->cmd,n->dev,n->sector);n = n->next;}printf("\n");
}
int main(int argc,char ** argv)
{struct request s1;struct request * pHead = 0;struct request *req = new struct request;req->cmd = 0;req->dev =  0;req->sector =  50;AddRequest(pHead,req);PrintQueen(pHead);   struct request *req3 = new struct request;req3->cmd = 0;req3->dev =  0;req3->sector =  80;AddRequest(pHead,req3);PrintQueen(pHead);  struct request *req2 = new struct request;req2->cmd = 0;req2->dev =  0;req2->sector =  60;AddRequest(pHead,req2);PrintQueen(pHead);  struct request *req5 = new struct request;req5->cmd = 0;req5->dev =  0;req5->sector =  30;AddRequest(pHead,req5);PrintQueen(pHead);  struct request *req4 = new struct request;req4->cmd = 0;req4->dev =  0;req4->sector =  20;AddRequest(pHead,req4);PrintQueen(pHead);  return 0;
}

上述代码的执行结果如下所示:

(0,0,50),
(0,0,50),(0,0,80),
(0,0,50),(0,0,60),(0,0,80),
(0,0,50),(0,0,60),(0,0,80),(0,0,30),
(0,0,50),(0,0,60),(0,0,80),(0,0,20),(0,0,30),

可以看出最后的顺序是50->60->80->20->30, 实际上效果就是单方向移动到最后一个位置,再复位进行扫描,再次沿同方向扫描。

c-san算法示意图

make_request

static void make_request(int major,int rw, struct buffer_head * bh)

该函数的作用是创建请求项并插入请求队列中。

首先判断命令是否READA或者是WRITEA。READA代表预读取,WRITEA代表预写入。所以当命令是预读取或者是预写入,如果bh块被锁,那么就放弃,直接返回。如果bh块没有被锁,那么就当作普通的READ和WRITE。

	struct request * req;int rw_ahead;/* WRITEA/READA is special case - it is not really needed, so if the */
/* buffer is locked, we just forget about it, else it's a normal read */if ((rw_ahead = (rw == READA || rw == WRITEA))) {if (bh->b_lock)return;if (rw == READA)rw = READ;elserw = WRITE;}

如果命令不是读或者写,那么就是一个致命错误,直接通过panic抛出错误。对命令校验之后,就去锁定该数据块。如果命令是写操作,但是该数据块并没有脏数据,则没有必要去写块设备,就可以对bh块进行解锁。除此以外,如果命令是读操作,但是该bh块中的内容已经是最新的,也没有必要去读块设备,就可以对bh块进行解锁。

if (rw!=READ && rw!=WRITE)panic("Bad block dev command, must be R/W/RA/WA");
lock_buffer(bh);
if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) {unlock_buffer(bh);return;
}

下面需要从request数组中寻找一个位置来创建该请求。对于读请求而言,将会从数组的尾部开始搜索。对于写请求而言,将会从数组的2/3处开始搜索。 如果找到了位置,那么就开始进行创建,如果没有找到位置,就sleep_on进行等待。

	if (rw == READ)req = request+NR_REQUEST;elsereq = request+((NR_REQUEST*2)/3);
/* find an empty request */while (--req >= request)if (req->dev<0)break;
/* if none found, sleep on new requests: check for rw_ahead */if (req < request) {if (rw_ahead) {unlock_buffer(bh);return;}sleep_on(&wait_for_request);goto repeat;}

当找到该位置时,就在该位置上进行构建请求。构建完之后,调用add_request插入到电梯队列中。

/* fill up the request-info, and add it to the queue */req->dev = bh->b_dev;req->cmd = rw;req->errors=0;req->sector = bh->b_blocknr<<1;req->nr_sectors = 2;req->buffer = bh->b_data;req->waiting = NULL;req->bh = bh;req->next = NULL;add_request(major+blk_dev,req);

ll_rw_block

void ll_rw_block(int rw, struct buffer_head * bh)

该函数的作用就是读写数据块。

下面一段代码用于对bh块对应的设备做相应的校验。如果主设备号不存在,或者该设备对应的请求操作函数不存在,就显示出错信息。

if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV ||
!(blk_dev[major].request_fn)) {printk("Trying to read nonexistent block-device\n\r");return;
}

如果校验没有问题就调用make_request建立块设备读写请求。

make_request(major,rw,bh);

blk_dev_init

void blk_dev_init(void)

该函数的作用是初始化块设备

遍历request数组,对request数组中每一项的dev设置为-1, 对next指针设置为NULL。

for (i=0 ; irequest[i].dev = -1;request[i].next = NULL;
}

lock_buffer

static inline void lock_buffer(struct buffer_head * bh)

该函数的作用是锁定指定的缓冲块。

cli();//关中断
while (bh->b_lock)//如果缓冲区已被锁定就睡眠,一直到缓冲区解锁sleep_on(&bh->b_wait);
bh->b_lock=1;//立即锁定缓冲区
sti();//开中断

unlock_buffer

static inline void unlock_buffer(struct buffer_head * bh)

该函数的作用是解锁指定的缓冲块。

if (!bh->b_lock)//如果该缓冲区没有加锁,则打印出错信息printk("ll_rw_block.c: buffer not locked\n\r");
bh->b_lock = 0;//对缓冲区解锁
wake_up(&bh->b_wait);//唤醒等待该缓冲区的任务。

相关内容

热门资讯

“侨连五洲·七彩云南”——第2... 在全球化浪潮奔涌不息、区域合作日益紧密的当下,各国之间的经济交流与文化互鉴正以前所未有的深度和广度展...
“今夜梁宵”江南古运河行浸式光... 中新网江苏新闻5月30日电(唐娟 孙权)华灯初上,运河潋滟。5月29日晚,“今夜梁宵”江南古运河行浸...
端午遇六一乐在其“粽” 青岛机... 青岛新闻网5月30日讯(记者 张晓楠 通讯员 李德鲲)2025年端午节假期(5月31日至6月2日),...
基于LIN通信的诊断概述 文章目录 前言一、帧的类型1.1 诊断帧(Diagnostic Frame) 二、LIN的传输层2....
MySQL数据库的定时备份实践 1.背景介绍也许你会发现本站建站时间久远,但是本站的各类访问量总是很低,...
潮汕三日游,去潮汕旅游大概多少... 潮汕三日游,去潮汕旅游大概多少钱 作为一个对美食和文化充满热情的旅行者,我一直对潮汕这片土地心怀向往...
端午假期安全提示,转给师生家长 01 关注交通安全 1.出行途中注意交通安全,遵守交通规则,乘坐正规运营车辆,不坐“三无”车辆,不坐...
西藏几月是旺季? 我最近和老公商量着要去西藏玩7天,说实话,我对旅行的准备总是不太上心,所以这次特别希望能有一份详尽的...
甘南9日游攻略详细安排,一家三... 甘南,这片被《国家地理》盛赞为“人生必去的50个地方之一”的净土,宛如一颗镶嵌在青藏高原东北边缘的璀...
尝鲜福建四果汤,清凉一夏不是梦 夏日炎炎,来一碗福建四果汤,瞬间驱散暑气,开启清凉模式。作为闽南地区的传统消暑甜品,四果汤历史悠久,...
宜昌三峡三天两晚景点攻略,宜昌... 最近,我有幸和朋友们一起踏上了前往宜昌三峡的旅程。宜昌三峡,那山清水秀、景色宜人的地方,一直是我向往...
泰国启动“萨瓦滴·你好”旅游推... 曼谷5月30日电(李映民 赵婧楠)为庆祝中泰建交50周年,泰国国家旅游局于5月29日在曼谷启动“萨瓦...
智慧景区一体化建设方案 随着2023年文旅部《关于推动智慧旅游发展的指导意见》出台,全国景区掀起数字化转型浪潮。如何在激烈竞...
端午邂逅“六一” 东丽区精心打... 天津北方网讯:即将到来的端午佳节与“六一”国际儿童节不期而遇,当传统文化与童趣时光奇妙邂逅,为文旅注...
中签率100%,茅台机场购酒规... 蓝鲸新闻5月30日讯(记者 朱欣悦)5月29日,遵义茅台机场微信公众号披露,在2025年5月30日、...
新疆昌吉消防深入旅游景区开展端... 图为消防人员在景区民族风情园商户开展消防培训。刘臣 摄 5月29日,新疆昌吉州消防救援支队阜康市消...
17岁中学生成中国首位北坡登顶... 近日,多家媒体报道称北京第八十中学学生、17岁的李浩榕成为中国首位从北坡登顶珠峰的青少年的消息登上热...
甘南扎尕那:藏在石头山里的&q... 甘南扎尕那:藏在石头山里的“神仙村落”,雨后云雾缭绕,仿佛穿越异世界! 在甘南迭部县的群山深处,有一...