Linux IO SchedulerCFQ2Word文件下载.docx
- 文档编号:4768550
- 上传时间:2023-05-04
- 格式:DOCX
- 页数:25
- 大小:25.73KB
Linux IO SchedulerCFQ2Word文件下载.docx
《Linux IO SchedulerCFQ2Word文件下载.docx》由会员分享,可在线阅读,更多相关《Linux IO SchedulerCFQ2Word文件下载.docx(25页珍藏版)》请在冰点文库上搜索。
cfq_activate_request,
10.
.elevator_deactivate_req_fn
cfq_deactivate_request,
11.
.elevator_queue_empty_fn
cfq_queue_empty,
12.
.elevator_completed_req_fn
cfq_completed_request,
13.
.elevator_former_req_fn
elv_rb_former_request,
14.
.elevator_latter_req_fn
elv_rb_latter_request,
15.
.elevator_set_req_fn
cfq_set_request,
16.
.elevator_put_req_fn
cfq_put_request,
17.
.elevator_may_queue_fn
cfq_may_queue,
18.
.elevator_init_fn
cfq_init_queue,
19.
.elevator_exit_fn
cfq_exit_queue,
20.
.trim
cfq_free_io_context,
21.
},
22.
.elevator_attrs
cfq_attrs,
23.
.elevator_name
"
cfq"
24.
.elevator_owner
THIS_MODULE,
25.};
可以看到CFQ调度器涉及到的操作函数还是比较多的,这里我只打算选一些和提交bio以及request相关的函数进行分析。
在提交bio的时候,如果在通用层寻找可以合并bio的途径失败,要通过cfq_merge()来判断是否能够将bio插入到某个request的bio链表首部
request
*
2.cfq_find_rq_fmerge(struct
cfq_data
*cfqd,
bio
*bio)
3.{
task_struct
*tsk
current;
cfq_io_context
*cic;
cfq_queue
*cfqq;
//在进程的io_context中,找到进程特定于块设备的cfq_io_context
cic
cfq_cic_lookup(cfqd,
tsk->
io_context);
if
(!
cic)
return
NULL;
//根据同步还是异步,确定cfq_queue
cfqq
cic_to_cfqq(cic,
cfq_bio_sync(bio));
(cfqq)
sector_t
sector
bio->
bi_sector
+
bio_sectors(bio);
//得到末尾扇区号
//从cfq_queue的红黑树中查找对应的节点
elv_rb_find(&
cfqq->
sort_list,
sector);
}
23.}
cfq_find_rq_fmerge()进行实际的搜索工作,要确定bio的归属request,必须先确定进程的通信对象是谁(因为一个进程有可能和多个块设备通信),也就是要找到进程对应的cfq_io_context结构,其中包含了进程的同步请求队列和异步请求队列的地址,只要找到了相应的cfq_io_context,就可以通过bio的同异步性确定对应的cfq_queue了,最后再判断对应的cfq_queue中是否存在可以容纳bio的request。
推导cfq_io_context的关键在于以块设备CFQ调度器的描述结构cfq_data的地址为关键值,在进程的io_context的基数树中进行搜索
[html]
1.<
span
style="
font-size:
12px;
>
static
24.<
/span>
通过基数树寻找对应设备的cfq_io_context:
2.cfq_cic_lookup(struct
io_context
*ioc)
unsigned
long
flags;
void
*k;
(unlikely(!
ioc))
rcu_read_lock();
/*
we
maintain
a
last-hit
cache,
to
avoid
browsing
over
the
tree
*/
//由于进程很有可能连续访问同一块设备,因此先将cic中的关键值直接与cfqd比较
rcu_dereference(ioc->
ioc_data);
(cic
&
cic->
key
==
cfqd)
rcu_read_unlock();
cic;
do
{//在进程io_context的基数树中寻找对应访问的块设备的cfq_data结构
25.
radix_tree_lookup(&
ioc->
radix_root,
(unsigned
long)
cfqd);
26.
27.
28.
break;
29.
->
must
be
copied
race
with
cfq_exit_queue()
30.
k
key;
31.
k))
32.
cfq_drop_dead_cic(cfqd,
ioc,
cic);
33.
34.
continue;
35.
36.
37.
//保存cic到ioc->
ioc_data
38.
spin_lock_irqsave(&
lock,
flags);
39.
rcu_assign_pointer(ioc->
ioc_data,
40.
spin_unlock_irqrestore(&
41.
42.
while
(1);
43.
44.
45.}<
在通用层__make_request()函数中,如果bio找不到接收对象,那么就要重新创建一个request来接收它。
分配一个request必须进行初始化,需要调用cfq_set_request()函数。
和前面的过程比较类似的是,request要先找到接收它的cfq_queue,然后这里出现了一个问题,如果这个request是进程的第一个同步请求或者异步请求,那么就要分配新的cfq_queue来接收request。
同步请求和异步请求的处理情况有点区别,因为进程独自拥有自己的同步请求队列,而进程的异步请求都是共享cfq_data中的异步请求队列的,所以只有当request是同步请求时才需要进行cfq_queue的分配
int
2.cfq_set_request(struct
request_queue
*q,
*rq,
gfp_t
gfp_mask)
*cfqd
q->
elevator->
elevator_data;
const
rw
rq_data_dir(rq);
bool
is_sync
rq_is_sync(rq);
might_sleep_if(gfp_mask
__GFP_WAIT);
//获取进程特定于块设备的cfq_io_context结构
cfq_get_io_context(cfqd,
gfp_mask);
spin_lock_irqsave(q->
queue_lock,
goto
queue_fail;
21.new_queue:
is_sync);
//根据同异步情况,获取进程中对应的cfq_queue
||
cfqd->
oom_cfqq)
{//如果还没有相应的cfqq则进行分配
cfq_get_queue(cfqd,
is_sync,
//分配cfq_queue
cic_set_cfqq(cic,
cfqq,
//设置cic->
cfqq[is_sync]
else
If
queue
was
seeky
for
too
long,
break
it
apart.
(cfq_cfqq_coop(cfqq)
should_split_cfqq(cfqq))
cfq_log_cfqq(cfqd,
breaking
apart
cfqq"
);
split_cfqq(cic,
cfqq);
cfqq)
new_queue;
Check
see
this
is
scheduled
merge
another,
closely
cooperating
queue.
The
merging
of
queues
happens
here
as
done
in
process
context.
reference
on
new_cfqq
taken
merge_cfqqs.
(cfqq->
new_cfqq)
cfq_merge_cfqqs(cfqd,
cic,
45.
46.
47.
allocated[rw]++;
48.
atomic_inc(&
ref);
49.
50.
spin_unlock_irqrestore(q->
51.
52.
rq->
elevator_private
//保存cfq_io_context到request
53.
elevator_private2
cfqq;
//保存cfq_queue到request
54.
0;
55.
56.queue_fail:
57.
(cic)
58.
put_io_context(cic->
ioc);
59.
60.
cfq_schedule_dispatch(cfqd);
61.
62.
cfq_log(cfqd,
set_request
fail"
63.
1;
64.}
当一个request创建初始化,就要将其插入到相应的队列中,cfq_insert_request()函数完成这个功能,和deadline调度器相似,request会被放入两个队列里,一个是按照起始扇区号排列的红黑树(sort_list),一个是按响应期限排列的链表(fifo)
cfq_insert_request(struct
*rq)
2.{
*cfqq
RQ_CFQQ(rq);
insert_request"
cfq_init_prio_data(cfqq,
RQ_CIC(rq)->
//根据ioc设定cfq_queue的优先级类和优先级
cfq_add_rq_rb(rq);
//将rq添加到cfq_queue的红黑树
//设置期限值
rq_set_fifo_time(rq,
jiffies
cfq_fifo_expire[rq_is_sync(rq)]);
list_add_tail(&
queuelist,
fifo);
//添加至fifo
cfq_rq_enqueued(cfqd,
rq);
16.}
当一个request纳入一个新的bio后,要考虑能否和其他request进行合并,假如是将bio插入到链表的末尾,那么就要在sort_list中获取request后面的一个request(通过函数elv_rb_latter_request()来获取),然后判断前者的结束扇区和前者的起始扇区是否一样,这些工作都是再通用层代码中完成的,当两个request通过了通用层的审核和,并完成合并操作后,将调用cfq_merged_requests进行一些针对队列的额外工作
1.tatic
2.cfq_merged_requests(struct
*next)
4.{
reposition
fifo
next
older
than
rq
list_empty(&
queuelist)
!
next->
time_before(rq_fifo_time(next),
rq_fifo_time(rq)))
list_move(&
queuelist);
rq_fifo_time(next));
cfq_remove_request(next);
15.}
这里主要是考察两个request的期限,因为合并时都是将后者合并入前者,因此当后者的期限时间小于前者时,要进行相应的调整,这一点和deadline调度器是一样的。
最后分析的依然是每个调度器最重要的函数--elevator_dispatch_fn
CFQ调度器在发送request到底层块设备时的流程大致如下:
1.选择一个cfq_queue
2.从cfq_queue中选择一个request进行发送
选择cfq_queue的思路如下:
1.如果当前的cfq_queue的时间片还没用完,则继续当前的cfq_queue
2.如果当前的cfq_queue的时间片消耗完了,则优先在对应的prio_tree中选择一个cfq_queue,该cfq_queue的第一个访问扇区与整个调度器最后处理的扇区之间的差值必须小于一个阈值,如果OK的话就选择这个cfq_queue
3.如果找不到这样的cfq_queue,再从service_tree中调度其他的cfq_queue
cfq_dispatch_requests(struct
force)
elevat
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux IO SchedulerCFQ2
![提示](https://static.bingdoc.com/images/bang_tan.gif)