操作系统真相还原 第十三章 编写硬盘驱动程序

第十三章 编写硬盘驱动程序

硬盘及分区表

创建硬盘分区表

文件系统并不仅仅是文件存储的静态数据结构,还有一套操作的方法。

分区:由多个连续的柱面组成。

分区表:在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 ,需要被唤醒后才能加入

到就绪队列。

  1. 先将当前任务重新加入到就绪队列(队尾)。
  2. 然后将当前任务的 Status 置为 TASK_READY。
  3. 最后调用 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线程

  1. 初始化时创建idle线程。
  2. idle线程阻塞自己。
  3. schedule中如果就绪队列没有就绪线程,唤醒idle线程。
  4. 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();
   }
}
posted @ 2021-11-01 16:38  dev_liufq  阅读(204)  评论(0编辑  收藏  举报