1、添加任务管理器
任务管理器其实就是一个大的结构体,然后将一些重要的数据结构集中到一起。重点创建了两个队列ready_list和task_list来管理任务
2、进程主动放弃CPU
目前的系统中,CPU总是被用于安排运行处于就绪队列头部的进程的代码,所以导致了如果该进程总是处于头部,那么其后的任务将迟迟得不到运行的机会,会被活活“饿死”
解决办法:
1)进程将自己移到队列尾部
2)调用task_dispatch()获取下一将要运行的任务并切换过去
3、简单的调度算法:时间片运行
3、让进程按照时间片运行
解决了处于队列头部的任务,如果不主动调用 yield() 时,如何强制其放弃CPU运行,从而给其他进程运行的机会。
因此借用定时器中断剥夺进程的CPU使用权,以周期性的定时中断为基准,给每个任务相同的CPU使用时长,一旦超过时长则被强制剥夺进程的CPU使用权进入队尾部
4、临界资源及简单的保护
临界资源:指在多线程或多进程的环境中,多个线程或进程需要访问和修改的共享资源。由于这些共享资源在同一时间只能被一个线程或进程安全地访问和修改,因此需要使用同步机制来确保它们的操作是原子性的,从而防止数据的不一致和竞态条件(Race Condition)的发生。
一次只让一个进程执行临界区中的代码,目前进程之间的运行是按照时间片运行,由定时器中断处理程序完成,因此可以简单的将中断给禁用就可以防止定时中断处理程序被调用,进而防止任务切换的发生
因此在程序中,增加了两个函数用于实现开关中断功能
/**
* @brief 进入中断保护
*/
irq_state_t irq_enter_protection (void) {
irq_state_t state = read_eflags();
irq_disable_global();
return state;
}
/**
* @brief 退出中断保护
*/
void irq_leave_protection (irq_state_t state) {
write_eflags(state);
}
5、让进程延时运行
延时的原理:将进程从就绪队列中移除,这样操作系统就不用安排CPU运行这个进程的代码。之后等指定的时间结束后,再将进程插入就绪队列。
由于系统中可能会有多个进程同时延时,为了将这些进程有效的管理,额外做了一个延时队列,将需要延时的进程暂时插入到就绪队列中。同时在进程的task_t结构中纪录进程需要延时多长时间。
之后在每个定时中断发生时,扫描这个延时队列。当发现某个进程延时到达时,将其从队列中移除插回延时队列。
存在延时准确性问题:
使用sys_sleep进行延时,实际的延时是有误差的,不会准确的延时指定的时间。因为调用延时函数的时机、延时到达后进程还要继续等待才能有机会运行。
存在的问题:
当系统中所有任务都尝试延时时,系统将崩溃,操作系统不知道做什么,因此考虑增加空闲进程,这个进程就是在其他进程都不需要运行的时候才运行。空闲进程不采用延时。
使用空闲进程后,其不会对其它进程造成任何影响,同时又能有效地让操作系统总是有个进程可以运行。这样就解决了其它任务都延时而导致系统崩溃的问题。