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();
     }
     // 处理其他类型的中断...
 }
 // 处理异常...
}

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

相关推荐
XY.散人1 分钟前
初识Linux · 传输层协议TCP · 上
linux·网络·tcp/ip
小郝 小郝7 分钟前
【C语言】五一回归,复习动脑
c语言·开发语言
love530love8 分钟前
好消息!PyCharm 社区版现已支持直接选择 WSL 终端为默认终端
linux·ide·人工智能·windows·python·pycharm
程序员JerrySUN14 分钟前
驱动开发硬核特训 · Day 30(上篇):深入理解 I2C 总线驱动模型(以 at24 EEPROM 为例)
linux·开发语言·驱动开发
KANNIYOU38 分钟前
linux 定时,延时任务
linux·运维·服务器
明晚十点睡1 小时前
Vscode (Windows端)免密登录linux集群服务器
linux·服务器·vscode
努力的搬砖人.2 小时前
maven如何搭建自己的私服(LINUX版)?
java·linux·maven
天夏已微凉2 小时前
1.2 Linux音频系统发展历程与关键技术演进
linux·c语言·驱动开发·音视频
小昭dedug2 小时前
功能安全的关键——MCU锁步核技术全解析(含真实应用方案)
单片机·嵌入式硬件
binary思维2 小时前
cat、more和less的区别
linux·less