操作系统真相还原 第十三章 编写硬盘驱动程序
第十三章 编写硬盘驱动程序
硬盘及分区表
创建硬盘分区表
文件系统并不仅仅是文件存储的静态数据结构,还有一套操作的方法。
分区:由多个连续的柱面组成。
分区表:在MBR中的64位固定大小的表,每个表项是分区的描述信息,大小16字节,所以共有4个表项,即有4个分区。
4个分区:是硬盘最初设计的遗留,最多支持4个分区。后来在此基础上做扩展,3个主分区,1个作为扩展分区。最多只能有1个扩展分区,1个扩展分区理论上可以划分任意多的分区(链表结构),但是硬盘有限制,一般为几十个。
扩展分区:4个分区中最多可以有一个扩展分区。
逻辑分区:扩展分区的子分区。
编写硬盘驱动程序
实现threadyield和idle线程
yield
yield:让步,它能够让当前线程从“运行状态”进入到“就绪状态”,从而让其他等待线程获取执行权,但是不能保证在当前线程调用yield()之后,其他线程就一定能获得执行权,也有可能是当前线程又回到“运行状态”继续运行。
主动把 CPU 使用权让出来,它与出 thread_block 的区别是,thread _yield 执行后任务的状态是 TASK_READY,即让出 CPU 后,它会被加入到就绪队列尾部,下次还能继续被调度器调度执行,而 thread_block 执行后任务的状态是 TASK_BLOCK ,需要被唤醒后才能加入
到就绪队列。
- 先将当前任务重新加入到就绪队列(队尾)。
- 然后将当前任务的 Status 置为 TASK_READY。
- 最后调用 schedule 重新调度新任务。
/* 主动让出cpu,换其它线程运行 */
void thread_yield(void) {
struct task_struct* cur = running_thread();
enum intr_status old_status = intr_disable();
ASSERT(!elem_find(&thread_ready_list, &cur->general_tag));
list_append(&thread_ready_list, &cur->general_tag);
cur->status = TASK_READY;
schedule();
intr_set_status(old_status);
}
idle线程
- 初始化时创建idle线程。
- idle线程阻塞自己。
- schedule中如果就绪队列没有就绪线程,唤醒idle线程。
- idle执行hlt指令。hlt指令的功能是使系统挂起,不是cpu循环空转(cpu使用率100%),此时cpu挂起,使用率为0。
/* 初始化线程环境 */
void thread_init(void) {
put_str("thread_init start\n");
list_init(&thread_ready_list);
list_init(&thread_all_list);
lock_init(&pid_lock);
/* 将当前main函数创建为线程 */
make_main_thread();
/* 创建idle线程 */
idle_thread = thread_start("idle", 10, idle, NULL);
put_str("thread_init done\n");
}
/* 系统空闲时运行的线程 */
static void idle(void* arg UNUSED) {
while(1) {
thread_block(TASK_BLOCKED);
//执行hlt时必须要保证目前处在开中断的情况下
asm volatile ("sti; hlt" : : : "memory");
}
}
/* 实现任务调度 */
void schedule() {
ASSERT(intr_get_status() == INTR_OFF);
struct task_struct* cur = running_thread();
if (cur->status == TASK_RUNNING) { // 若此线程只是cpu时间片到了,将其加入到就绪队列尾
ASSERT(!elem_find(&thread_ready_list, &cur->general_tag));
list_append(&thread_ready_list, &cur->general_tag);
cur->ticks = cur->priority; // 重新将当前线程的ticks再重置为其priority;
cur->status = TASK_READY;
} else {
/* 若此线程需要某事件发生后才能继续上cpu运行,
不需要将其加入队列,因为当前线程不在就绪队列中。*/
}
/* 如果就绪队列中没有可运行的任务,就唤醒idle */
if (list_empty(&thread_ready_list)) {
thread_unblock(idle_thread);
}
ASSERT(!list_empty(&thread_ready_list));
thread_tag = NULL; // thread_tag清空
/* 将thread_ready_list队列中的第一个就绪线程弹出,准备将其调度上cpu. */
thread_tag = list_pop(&thread_ready_list);
struct task_struct* next = elem2entry(struct task_struct, general_tag, thread_tag);
next->status = TASK_RUNNING;
/* 击活任务页表等 */
process_activate(next);
switch_to(cur, next);
}
实现简单的休眠函数
借助thread_yield实现sleep
uint32_t ticks; // ticks是内核自中断开启以来总共的嘀嗒数
/* 以tick为单位的sleep,任何时间形式的sleep会转换此ticks形式 */
static void ticks_to_sleep(uint32_t sleep_ticks) {
uint32_t start_tick = ticks;
/* 若间隔的ticks数不够便让出cpu */
while (ticks - start_tick < sleep_ticks) {
thread_yield();
}
}