1 use core::sync::atomic::compiler_fence;
2
3 use crate::{
4 arch::asm::{current::current_pcb, ptrace::user_mode},
5 arch::{
6 context::switch_process,
7 interrupt::{cli, sti},
8 },
9 include::bindings::bindings::smp_get_total_cpu,
10 include::bindings::bindings::{
11 process_control_block, pt_regs, MAX_CPU_NUM, PF_NEED_MIGRATE, PROC_RUNNING, SCHED_FIFO,
12 SCHED_NORMAL, SCHED_RR,
13 },
14 process::process::process_cpu,
15 syscall::SystemError,
16 };
17
18 use super::cfs::{sched_cfs_init, SchedulerCFS, __get_cfs_scheduler};
19 use super::rt::{sched_rt_init, SchedulerRT, __get_rt_scheduler};
20
21 /// @brief 获取指定的cpu上正在执行的进程的pcb
22 #[inline]
cpu_executing(cpu_id: u32) -> &'static mut process_control_block23 pub fn cpu_executing(cpu_id: u32) -> &'static mut process_control_block {
24 // todo: 引入per_cpu之后,该函数真正执行“返回指定的cpu上正在执行的pcb”的功能
25
26 if cpu_id == process_cpu(current_pcb()) {
27 return current_pcb();
28 } else {
29 todo!()
30 }
31 }
32 // 获取某个cpu的负载情况,返回当前负载,cpu_id 是获取负载的cpu的id
33 // TODO:将获取负载情况调整为最近一段时间运行进程的数量
get_cpu_loads(cpu_id: u32) -> u3234 pub fn get_cpu_loads(cpu_id: u32) -> u32 {
35 let cfs_scheduler = __get_cfs_scheduler();
36 let rt_scheduler = __get_rt_scheduler();
37 let len_cfs = cfs_scheduler.get_cfs_queue_len(cpu_id);
38 let len_rt = rt_scheduler.rt_queue_len(cpu_id);
39 // let load_rt = rt_scheduler.get_load_list_len(cpu_id);
40 // kdebug!("this cpu_id {} is load rt {}", cpu_id, load_rt);
41
42 return (len_rt + len_cfs) as u32;
43 }
44 // 负载均衡
loads_balance(pcb: &mut process_control_block)45 pub fn loads_balance(pcb: &mut process_control_block) {
46 // 对pcb的迁移情况进行调整
47 // 获取总的CPU数量
48 let cpu_num = unsafe { smp_get_total_cpu() };
49 // 获取当前负载最小的CPU的id
50 let mut min_loads_cpu_id = pcb.cpu_id;
51 let mut min_loads = get_cpu_loads(pcb.cpu_id);
52 for cpu_id in 0..cpu_num {
53 let tmp_cpu_loads = get_cpu_loads(cpu_id);
54 if min_loads - tmp_cpu_loads > 0 {
55 min_loads_cpu_id = cpu_id;
56 min_loads = tmp_cpu_loads;
57 }
58 }
59
60 // 将当前pcb迁移到负载最小的CPU
61 // 如果当前pcb的PF_NEED_MIGRATE已经置位,则不进行迁移操作
62 if (min_loads_cpu_id != pcb.cpu_id) && (pcb.flags & (PF_NEED_MIGRATE as u64)) == 0 {
63 // sched_migrate_process(pcb, min_loads_cpu_id as usize);
64 pcb.flags |= PF_NEED_MIGRATE as u64;
65 pcb.migrate_to = min_loads_cpu_id;
66 // kdebug!("set migrating, pcb:{:?}", pcb);
67 }
68 }
69 /// @brief 具体的调度器应当实现的trait
70 pub trait Scheduler {
71 /// @brief 使用该调度器发起调度的时候,要调用的函数
sched(&mut self) -> Option<&'static mut process_control_block>72 fn sched(&mut self) -> Option<&'static mut process_control_block>;
73
74 /// @brief 将pcb加入这个调度器的调度队列
enqueue(&mut self, pcb: &'static mut process_control_block)75 fn enqueue(&mut self, pcb: &'static mut process_control_block);
76 }
77
__sched() -> Option<&'static mut process_control_block>78 fn __sched() -> Option<&'static mut process_control_block> {
79 compiler_fence(core::sync::atomic::Ordering::SeqCst);
80 let cfs_scheduler: &mut SchedulerCFS = __get_cfs_scheduler();
81 let rt_scheduler: &mut SchedulerRT = __get_rt_scheduler();
82 compiler_fence(core::sync::atomic::Ordering::SeqCst);
83
84 let next: &'static mut process_control_block;
85 match rt_scheduler.pick_next_task_rt(current_pcb().cpu_id) {
86 Some(p) => {
87 next = p;
88 // kdebug!("next pcb is {}",next.pid);
89 // 将pick的进程放回原处
90 rt_scheduler.enqueue_front(next);
91
92 return rt_scheduler.sched();
93 }
94 None => {
95 return cfs_scheduler.sched();
96 }
97 }
98 }
99
100 /// @brief 将进程加入调度队列
101 ///
102 /// @param pcb 要被加入队列的pcb
103 /// @param reset_time 是否重置虚拟运行时间
104 #[allow(dead_code)]
105 #[no_mangle]
sched_enqueue(pcb: &'static mut process_control_block, mut reset_time: bool)106 pub extern "C" fn sched_enqueue(pcb: &'static mut process_control_block, mut reset_time: bool) {
107 compiler_fence(core::sync::atomic::Ordering::SeqCst);
108
109 // 调度器不处理running位为0的进程
110 if pcb.state & (PROC_RUNNING as u64) == 0 {
111 return;
112 }
113 let cfs_scheduler = __get_cfs_scheduler();
114 let rt_scheduler = __get_rt_scheduler();
115
116 // 除了IDLE以外的进程,都进行负载均衡
117 if pcb.pid > 0 {
118 loads_balance(pcb);
119 }
120 compiler_fence(core::sync::atomic::Ordering::SeqCst);
121
122 if (pcb.flags & (PF_NEED_MIGRATE as u64)) != 0 {
123 // kdebug!("migrating pcb:{:?}", pcb);
124 pcb.flags &= !(PF_NEED_MIGRATE as u64);
125 pcb.cpu_id = pcb.migrate_to;
126 reset_time = true;
127 }
128 compiler_fence(core::sync::atomic::Ordering::SeqCst);
129
130 if pcb.policy == SCHED_NORMAL {
131 if reset_time {
132 cfs_scheduler.enqueue_reset_vruntime(pcb);
133 } else {
134 cfs_scheduler.enqueue(pcb);
135 }
136 } else if pcb.policy == SCHED_FIFO || pcb.policy == SCHED_RR {
137 rt_scheduler.enqueue(pcb);
138 } else {
139 panic!("This policy is not supported at this time");
140 }
141 }
142
143 /// @brief 初始化进程调度器模块
144 #[allow(dead_code)]
145 #[no_mangle]
sched_init()146 pub extern "C" fn sched_init() {
147 unsafe {
148 sched_cfs_init();
149 sched_rt_init();
150 }
151 }
152
153 /// @brief 当时钟中断到达时,更新时间片
154 /// 请注意,该函数只能被时钟中断处理程序调用
155 #[allow(dead_code)]
156 #[no_mangle]
sched_update_jiffies()157 pub extern "C" fn sched_update_jiffies() {
158 match current_pcb().policy {
159 SCHED_NORMAL => {
160 __get_cfs_scheduler().timer_update_jiffies();
161 }
162 SCHED_FIFO | SCHED_RR => {
163 current_pcb().rt_time_slice -= 1;
164 }
165 _ => {
166 todo!()
167 }
168 }
169 }
170
171 /// @brief 让系统立即运行调度器的系统调用
172 /// 请注意,该系统调用不能由ring3的程序发起
173 #[allow(dead_code)]
174 #[no_mangle]
sys_sched(regs: &'static mut pt_regs) -> u64175 pub extern "C" fn sys_sched(regs: &'static mut pt_regs) -> u64 {
176 cli();
177 // 进行权限校验,拒绝用户态发起调度
178 if user_mode(regs) {
179 return SystemError::EPERM.to_posix_errno() as u64;
180 }
181 // 根据调度结果统一进行切换
182 let pcb = __sched();
183
184 if pcb.is_some() {
185 switch_process(current_pcb(), pcb.unwrap());
186 }
187 sti();
188 return 0;
189 }
190
191 #[allow(dead_code)]
192 #[no_mangle]
sched_set_cpu_idle(cpu_id: usize, pcb: *mut process_control_block)193 pub extern "C" fn sched_set_cpu_idle(cpu_id: usize, pcb: *mut process_control_block) {
194 __get_cfs_scheduler().set_cpu_idle(cpu_id, pcb);
195 }
196
197 /// @brief 设置进程需要等待迁移到另一个cpu核心。
198 /// 当进程被重新加入队列时,将会更新其cpu_id,并加入正确的队列
199 ///
200 /// @return i32 成功返回0,否则返回posix错误码
201 #[allow(dead_code)]
202 #[no_mangle]
sched_migrate_process( pcb: &'static mut process_control_block, target: usize, ) -> i32203 pub extern "C" fn sched_migrate_process(
204 pcb: &'static mut process_control_block,
205 target: usize,
206 ) -> i32 {
207 if target > MAX_CPU_NUM.try_into().unwrap() {
208 // panic!("sched_migrate_process: target > MAX_CPU_NUM");
209 return SystemError::EINVAL.to_posix_errno();
210 }
211
212 pcb.flags |= PF_NEED_MIGRATE as u64;
213 pcb.migrate_to = target as u32;
214 // kdebug!("pid:{} migrate to cpu:{}", pcb.pid, target);
215 return 0;
216 }
217