Skip to content

Commit

Permalink
✨ 080 磁盘调度电梯算法
Browse files Browse the repository at this point in the history
  • Loading branch information
StevenBaby committed Sep 17, 2022
1 parent c391191 commit 69f71e2
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 29 deletions.
40 changes: 40 additions & 0 deletions docs/09 设备驱动/080 磁盘调度电梯算法.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# 磁盘调度电梯算法

由于磁盘性能的主要瓶颈在磁盘的寻道时间,也就是磁头臂的移动时间,所以要尽可能避免磁头臂的移动。电梯算法的作用是让磁头的综合移动距离最小,从而改善磁盘访问时间。

![](../01%20系统引导/images/harddisk_1.jpeg)

![](../01%20系统引导/images/harddisk_7.jpg)

![](./images/elevator.drawio.svg)

## LBA 和 CHS

- LBA (Logical Block Addressing):逻辑块寻址,逻辑上认为磁盘的扇区编号从 0 开始依次递增,处理起来更方便;
- Sector: 扇区,磁盘最小的单位,多个扇区够称一个磁道
- Head: 磁头,用于读写盘面,一个磁盘可能有多个磁头,一个磁道读写完成,就换另一个磁头;
- Cylinder:柱面,或者也可以认为是磁道 (Track),同一个位置的所有磁道共同构成了柱面;当所有磁道都读写完时,就需要切换磁道,也就产生了寻道的问题。因此柱面是磁盘读写最大的单位。

下面是 LBA 和 CHS 的转换公式:

- CYL = LBA / (HPC * SPT)

- HEAD = (LBA % (HPC * SPT)) / SPT

- SECT = (LBA % (HPC * SPT)) % SPT + 1

- LBA = ( ( CYL * HPC + HEAD ) * SPT ) + SECT - 1

其中:

- CYL 表示柱面 (Cylinder)
- HEAD 表示磁头 (Head)
- SECT 表示扇区 (Sector)
- LBA 表示逻辑块地址 (Logical Block Addressing)
- HPC 表示柱面磁头数 (Head Per Cylinder)
- SPT 表示磁道扇区数 (Sector Per Track)

## 参考文献

1. [赵炯 / Linux内核完全注释 / 机械工业出版社 / 2005](https://book.douban.com/subject/1231236/)
2. <https://en.wikipedia.org/wiki/Elevator_algorithm>
16 changes: 16 additions & 0 deletions docs/09 设备驱动/images/elevator.drawio.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/include/onix/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ enum device_cmd_t
#define REQ_READ 0 // 块设备读
#define REQ_WRITE 1 // 块设备写

#define DIRECT_UP 0 // 上楼
#define DIRECT_DOWN 1 // 下楼

// 块设备请求
typedef struct request_t
{
Expand All @@ -55,6 +58,8 @@ typedef struct device_t
dev_t parent; // 父设备号
void *ptr; // 设备指针
list_t request_list; // 块设备请求链表
bool direct; // 磁盘寻道方向

// 设备控制
int (*ioctl)(void *dev, int cmd, void *args, int flags);
// 读设备
Expand Down
5 changes: 5 additions & 0 deletions src/include/onix/list.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

#define element_offset(type, member) (u32)(&((type *)0)->member)
#define element_entry(type, member, ptr) (type *)((u32)ptr - element_offset(type, member))
#define element_node_offset(type, node, key) ((int)(&((type *)0)->key) - (int)(&((type *)0)->node))
#define element_node_key(node, offset) *(int *)((int)node + offset)

// 链表结点
typedef struct list_node_t
Expand Down Expand Up @@ -53,4 +55,7 @@ bool list_empty(list_t *list);
// 获得链表长度
u32 list_size(list_t *list);

// 链表插入排序
void list_insert_sort(list_t *list, list_node_t *node, int offset);

#endif
2 changes: 1 addition & 1 deletion src/include/onix/task.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ typedef struct task_t
list_node_t node; // 任务阻塞节点
task_state_t state; // 任务状态
u32 priority; // 任务优先级
u32 ticks; // 剩余时间片
int ticks; // 剩余时间片
u32 jiffies; // 上次执行时全局时间片
char name[TASK_NAME_LEN]; // 任务名
u32 uid; // 用户 id
Expand Down
48 changes: 44 additions & 4 deletions src/kernel/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ void device_init()
device->write = NULL;

list_init(&device->request_list);
device->direct = DIRECT_UP;
}
}

Expand Down Expand Up @@ -118,6 +119,8 @@ device_t *device_get(dev_t dev)
// 执行块设备请求
static void do_request(request_t *req)
{
LOGK("dev %d do request idx %d\n", req->dev, req->idx);

switch (req->type)
{
case REQ_READ:
Expand All @@ -132,6 +135,38 @@ static void do_request(request_t *req)
}
}

// 获得下一个请求
static request_t *request_nextreq(device_t *device, request_t *req)
{
list_t *list = &device->request_list;

if (device->direct == DIRECT_UP && req->node.next == &list->tail)
{
device->direct = DIRECT_DOWN;
}
else if (device->direct == DIRECT_DOWN && req->node.prev == &list->head)
{
device->direct = DIRECT_UP;
}

void *next = NULL;
if (device->direct == DIRECT_UP)
{
next = req->node.next;
}
else
{
next = req->node.prev;
}

if (next == &list->head || next == &list->tail)
{
return NULL;
}

return element_entry(request_t, node, next);
}

// 块设备请求
void device_request(dev_t dev, void *buf, u8 count, idx_t idx, int flags, u32 type)
{
Expand All @@ -154,11 +189,16 @@ void device_request(dev_t dev, void *buf, u8 count, idx_t idx, int flags, u32 ty
req->type = type;
req->task = NULL;

LOGK("dev %d request idx %d\n", req->dev, req->idx);

// 判断列表是否为空
bool empty = list_empty(&device->request_list);

// 将请求压入链表
list_push(&device->request_list, &req->node);
// list_push(&device->request_list, &req->node);

// 将请求插入链表
list_insert_sort(&device->request_list, &req->node, element_node_offset(request_t, node, idx));

// 如果列表不为空,则阻塞,因为已经有请求在处理了,等待处理完成;
if (!empty)
Expand All @@ -169,13 +209,13 @@ void device_request(dev_t dev, void *buf, u8 count, idx_t idx, int flags, u32 ty

do_request(req);

request_t *nextreq = request_nextreq(device, req);

list_remove(&req->node);
kfree(req);

if (!list_empty(&device->request_list))
if (nextreq)
{
// 先来先服务
request_t *nextreq = element_entry(request_t, node, device->request_list.tail.prev);
assert(nextreq->task->magic == ONIX_MAGIC);
task_unblock(nextreq->task);
}
Expand Down
4 changes: 2 additions & 2 deletions src/kernel/gate.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ static u32 sys_test()
device = device_find(DEV_IDE_PART, 0);
assert(device);

memset(buf, running_task()->pid, 512);
memset(buf, running_task()->uid, 512);

device_request(device->dev, buf, 1, running_task()->pid, 0, REQ_WRITE);
device_request(device->dev, buf, 1, running_task()->uid, 0, REQ_WRITE);

free_kpage((u32)buf, 1);

Expand Down
26 changes: 4 additions & 22 deletions src/kernel/task.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,25 +143,7 @@ void task_sleep(u32 ms)
current->ticks = jiffies + ticks;

// 从睡眠链表找到第一个比当前任务唤醒时间点更晚的任务,进行插入排序
list_t *list = &sleep_list;
list_node_t *anchor = &list->tail;

for (list_node_t *ptr = list->head.next; ptr != &list->tail; ptr = ptr->next)
{
task_t *task = element_entry(task_t, node, ptr);

if (task->ticks > current->ticks)
{
anchor = ptr;
break;
}
}

assert(current->node.next == NULL);
assert(current->node.prev == NULL);

// 插入链表
list_insert_before(anchor, &current->node);
list_insert_sort(&sleep_list, &current->node, element_node_offset(task_t, node, ticks));

// 阻塞状态是睡眠
current->state = TASK_SLEEPING;
Expand Down Expand Up @@ -487,7 +469,7 @@ void task_init()

idle_task = task_create(idle_thread, "idle", 1, KERNEL_USER);
task_create(init_thread, "init", 5, NORMAL_USER);
task_create(test_thread, "test", 5, KERNEL_USER);
task_create(test_thread, "test", 5, KERNEL_USER);
task_create(test_thread, "test", 5, KERNEL_USER);
task_create(test_thread, "test", 5, 1);
task_create(test_thread, "test", 5, 5);
task_create(test_thread, "test", 5, 3);
}
23 changes: 23 additions & 0 deletions src/lib/list.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,26 @@ u32 list_size(list_t *list)
}
return size;
}

// 链表插入排序
void list_insert_sort(list_t *list, list_node_t *node, int offset)
{
// 从链表找到第一个比当前节点 key 点更大的节点,进行插入到前面
list_node_t *anchor = &list->tail;
int key = element_node_key(node, offset);
for (list_node_t *ptr = list->head.next; ptr != &list->tail; ptr = ptr->next)
{
int compare = element_node_key(ptr, offset);
if (compare > key)
{
anchor = ptr;
break;
}
}

assert(node->next == NULL);
assert(node->prev == NULL);

// 插入链表
list_insert_before(anchor, node);
}

0 comments on commit 69f71e2

Please sign in to comment.