RVOS-7.实现抢占式多任务

抢占式环境下,操作系统完全决定任务调度方案,操作系统可以剥夺当前任务对处理器的使用,将处理器提供给其它任务 。

这里就是利用前面设计的硬件定时器中断,每个任务定时运行一段时间,触发中断,切换任务。

同时考虑兼容协作式多任务中,出现任务完成但定时还没到,主动放弃任务的功能。

8.1 抢占式多任务的设计

保存的context中增加了保存epc寄存器(记录任务切换时,当时的下一条指令地址,作为下一次切换回来的标记----因为是当前指令执行完,再跳转到中断处理函数)

c 复制代码
/* task management */
struct context {
	/* ignore x0 */
	reg_t ra;
	reg_t sp;
	reg_t gp;
	reg_t tp;
	reg_t t0;
	reg_t t1;
	reg_t t2;
	reg_t s0;
	reg_t s1;
	reg_t a0;
	reg_t a1;
	reg_t a2;
	reg_t a3;
	reg_t a4;
	reg_t a5;
	reg_t a6;
	reg_t a7;
	reg_t s2;
	reg_t s3;
	reg_t s4;
	reg_t s5;
	reg_t s6;
	reg_t s7;
	reg_t s8;
	reg_t s9;
	reg_t s10;
	reg_t s11;
	reg_t t3;
	reg_t t4;
	reg_t t5;
	reg_t t6;

	/* save the pc(fact is epc) to run in next schedule cycle
	 * offset: 31 * sizeof(reg_t)
	 */
	reg_t pc; 

};

仍然是使用**struct** context ctx_tasks[MAX_TASKS];保存任务上下文;静态变量,并且没有显式初始化,那么它会存储在 BSS 段中。这是因为 BSS 段用于存储未初始化的全局和静态变量。

编译的时候由 ld 分配的,栈区保存函数调用上下文,context区(指struct** context ctx_tasks[MAX_TASKS])固定大小,保存每一个任务的上下文,context区是实现任务调度时,保存原任务执行位置和寄存器信息的.

**注意!!!:**原本是使用ra寄存器返回,现在是使用mepc寄存器。所以随后也是使用mret指令而不是ret指令。

assembly 复制代码
# interrupts and exceptions while in machine mode come here.
# the trap vector base address must always be aligned on a 4-byte boundary
.globl trap_vector
.balign 4

trap_vector:
	# save context(registers).
	csrrw	t6, mscratch, t6	# swap t6 and mscratch
	reg_save t6

	# Save the actual t6 register, which we swapped into
	# mscratch
	mv	t5, t6					# t5 points to the context of current task
	csrr	t6, mscratch		# read t6 back from mscratch
	STORE	t6, 30*SIZE_REG(t5)	# save t6 with t5 as base

	# save the return address to context of current task
	# save mepc to context of current task
	csrr	a0, mepc
	STORE	a0, 31*SIZE_REG(t5)

	# Restore the context pointer into mscratch
	csrw	mscratch, t5

	# call the C trap handler in trap.c
	csrr	a0, mepc
	csrr	a1, mcause
	call	trap_handler

	# trap_handler will return the return address via a0.
	csrw	mepc, a0

	# restore context(registers).
	csrr	t6, mscratch
	reg_restore t6

	# return to whatever we were doing before trap.
	mret

触发中断时不仅保存当前任务的上下文,还保存epc到context中的pc位置:(epc的值是硬件在陷入时候自动赋值)

还有这里,这里错误找了一晚上!!!哎。


还有start.S中下面部分做出更改:


改成下面;

  1. 初始化 BSS 段:将 BSS 段的内存清零。
  2. 设置栈指针:为每个核心的栈指针初始化到栈空间的末尾。
  3. 设置 mstatus 寄存器:确保在切换到第一个任务后,处理器仍然运行在机器模式并启用中断。

这里区分两种方式的代码:

  • 协作式:
assembly 复制代码
.globl switch_to
.balign 4
switch_to:
	csrrw	t6, mscratch, t6	# swap t6 and mscratch
	beqz	t6, 1f				# Note: the first time switch_to() is
	                            # called, mscratch is initialized as zero
								# (in sched_init()), which makes t6 zero,
								# and that's the special case we have to
								# handle with t6
	reg_save t6					# save context of prev task

	# Save the actual t6 register, which we swapped into mscratch
	mv	t5, t6					# t5 points to the context of current task
	csrr	t6, mscratch		# read t6 back from mscratch
	STORE	t6, 30*SIZE_REG(t5)	# save t6 with t5 as base

1:
	# switch mscratch to point to the context of the next task
	csrw	mscratch, a0

	# Restore all GP registers
	# Use t6 to point to the context of the new task
	mv	t6, a0
	reg_restore t6

	# Do actual context switching.
	ret
  • 抢占式:
assembly 复制代码
.globl switch_to
.balign 4
switch_to:
	# switch mscratch to point to the context of the next task
	csrw	mscratch, a0
	# set mepc to the pc of the next task
	LOAD	a1, 31*SIZE_REG(a0)
	csrw	mepc, a1

	# Restore all GP registers
	# Use t6 to point to the context of the new task
	mv	t6, a0
	reg_restore t6

	# Do actual context switching.
	# Notice this will enable global interrupt
	mret

8.2 兼容协作式多任务---- 软中断(software interrupt)

主要目的是考虑兼容协作式多任务中,出现任务完成但定时还没到,主动放弃任务的功能。

c 复制代码
/*
 * DESCRIPTION
 * 	task_yield()  causes the calling task to relinquish the CPU and a new 
 * 	task gets to run.
 */
void task_yield()

这里使用软中断,主动触发,通知Hart进行陷入处理。

在RISC-V架构中,软中断(software interrupt)通常是由写特定的寄存器来触发的,例如MSIP(Machine Software Interrupt Pending)。在你的示例中,通过向MSIP写入1来触发软件中断,写入0来清除中断标志。

为了实现对不同软中断触发原因执行相应的操作,你可以在中断处理程序中检查引发中断的具体原因,并根据这个原因调用不同的处理函数。这通常涉及到对cause寄存器中的值进行解码,以确定是哪个软中断被触发。

以下是如何设计这种机制的步骤:

1. 定义软中断类型

定义不同的软中断类型,这些类型将对应于不同的操作。

c 复制代码
typedef enum {
 INT_SCHEDULE,
 INT_SHUTDOWN,
 INT_REBOOT,
 // 其他软中断类型
} SoftInterruptType;

2. 设置软中断标志

为每种软中断类型设置一个标志,这些标志存储在某个内存位置,中断处理程序将检查这些标志。

c 复制代码
volatile uint32_t software_interrupt_flags = 0;

void trigger_soft_interrupt(SoftInterruptType type) {
 // 假设我们用不同的位来表示不同的软中断类型
 software_interrupt_flags |= (1 << type);
}

3. 编写中断处理程序

编写一个中断处理程序,该程序检查哪些软中断标志被设置,并执行相应的操作。

c 复制代码
void handle_soft_interrupts() {
 if (software_interrupt_flags & (1 << INT_SCHEDULE)) {
     // 执行调度操作
     schedule();
     software_interrupt_flags &= ~(1 << INT_SCHEDULE); // 清除标志
 }
 if (software_interrupt_flags & (1 << INT_SHUTDOWN)) {
     // 执行关机操作
     shutdown();
     software_interrupt_flags &= ~(1 << INT_SHUTDOWN); // 清除标志
 }
 if (software_interrupt_flags & (1 << INT_REBOOT)) {
     // 执行重启操作
     reboot();
     software_interrupt_flags &= ~(1 << INT_REBOOT); // 清除标志
 }
 // 处理其他软中断类型
}

4. 集成到中断处理流程

将软中断处理程序集成到你的中断处理流程中,确保当中断发生时调用该处理程序。

c 复制代码
void trap_handler(reg_t epc, reg_t cause) {
 if (cause & 0x8000000000000000L) { // 检查是否为中断
     if (cause == CAUSE_MACHINE_SOFT) { // 检查是否为软中断
         handle_soft_interrupts();
     }
     // 处理其他类型的中断...
 }
 // 处理异常...
}

通过这种方式,当中断发生时,你的系统可以检查是哪种软中断被触发,并执行相应的操作。这种方法允许你对不同的软中断触发原因做出不同的响应,而不是执行相同的操作。希望这次的解释更加清晰地回答了你的问题!如果还有其他疑问,请继续提问。

相关推荐
野生派蒙5 分钟前
Linux:显示 -bash-4.2$ 问题(CentOS 7)
linux·运维·服务器·centos·bash
槐月杰3 小时前
C语言中冒泡排序和快速排序的区别
c语言·算法·排序算法
清风~徐~来5 小时前
【Linux】环境变量
linux·运维·chrome
YdaMooc7 小时前
STM32-FreeRTOS的详细配置
stm32·单片机·嵌入式硬件
云山工作室7 小时前
基于STM32的智能门禁系统
stm32·单片机·毕业设计·毕设
Willliam_william7 小时前
QEMU学习之路(8)— ARM32通过u-boot 启动Linux
linux·学习·elasticsearch
无处在8 小时前
STM32 四足机器人常见问题汇总
stm32·嵌入式硬件·机器人
yasuniko8 小时前
C复习(主要复习)
c语言·数据结构·算法
橘猫0.o9 小时前
【Linux 并发与竞争实验】
linux·运维·服务器·驱动开发
南梦也要学习9 小时前
STM32江科大-----PWR电源控制
stm32·单片机·嵌入式硬件