自己以前没有写过smp os,关于这方面的os也看得比较少。如果有,也是关于linux smp的部分,但是Linux上面会涉及到负载均衡,也很复杂,理解上面有一定的难度。然而是等到自己真正写的时候,发现困难也是可以克服的。抛却均衡负载不谈,自己写smp os也是可以做到的。下面就是我在实践中的一些做法。
(1) 每个任务一旦分配到具体cpu上之后,就不再迁移
// scheduler function
STATUS schedule() {
u8 cpuid;
Task* p_task;
cpuid = get_local_cpu();
if(is_in_irq()) {
return IN_IRQ;
}
if(is_sched_lock()) {
return OS_SCHED_LOCKED;
}
DISABLE_IE();
p_task = get_list_entry(g_ready[cpuid].next, Task, rdy);
sched_task[cpuid] = p_task;
if(current_task[cpuid] != p_task){
p_task-> state = RUNNING;
CONTEXT_SWITCH();
}
ENABLE_IE();
return SUCCESS;
}
(2)因为任务无法迁移,所以如果需要唤醒的时候只能是由核间中断来完成,下面的这个信号量可以说明问题,
// create sem
STATUS create_sem(Sem* p_sem, u32 count){
if(NULL == p_sem) {
return PARAM_ERROR;
}
p_sem-> blk_type = SEM_TYPE;
spin_lock_init(&p_sem-> lock);
list_init(&p_sem-> head);
p_sem-> count = count;
return SUCCESS;
}
// get sem
STATUS get_sem(Sem* p_sem, u8 wait){
u8 cpuid;
cpuid = get_local_cpu();
if(is_in_irq()) {
return IN_IRQ;
}
if(NULL == p_sem) {
return PARAM_ERROR;
}
if(SEM_TYPE != p_sem-> blk_type) {
return WRONG_BLOCK_TYPE;
}
DISABLE_IE();
spin_lock(&p_sem-> lock);
if(p_sem-> count) {
p_sem-> count --;
spin_unlock(&p_sem-> lock);
ENABLE_IE();
return SUCCESS;
}
if(is_sched_lock()) {
spin_unlock(&p_sem-> lock);
ENABLE_IE();
return OS_SCHED_LOCKED;
}
if(!wait) {
spin_unlock(&p_sem-> lock);
ENABLE_IE();
return NOT_WAIT;
}
remove_from_rdy_queue(current_task[cpuid]);
add_to_blk_queue(&p_sem-> head, current_task[cpuid]);
current_task[cpuid]-> state = BLOCKED;
current_task[cpuid]-> blk_data = p_sem;
spin_unlock(&p_sem-> lock);
ENABLE_IE();
schedule();
return SUCCESS;
}
// put sem
STATUS put_sem(Sem* p_sem) {
u8 cpuid;
Task* p_task;
if(NULL == p_sem) {
return PARAM_ERROR;
}
if(SEM_TYPE != p_sem-> blk_type){
return WRONG_BLOCK_TYPE;
}
DISABLE_IE();
spin_lock(&p_sem-> lock);
if(is_list_empty(&p_sem-> head)){
p_sem-> count ++;
spin_unlock(&p_sem-> lock);
ENABLE_IE();
return SUCCESS;
}
p_task = get_list_entry(p_sem->head.next, Task, blk);
remove_from_blk_queue(p_task);
list_init(&p_task-> blk);
cpuid = get_local_cpu();
if(cpuid != p_task-> cpuid) {
// send ipi to target cpu
send_ipi(WAKE_UP_TASK, p_task, p_task-> cpuid);
spin_unlock(&p_sem-> lock);
ENABLE_IE();
return SUCCESS;
}
add_to_rdy_queue(&g_ready[cpuid], p_task);
p_task-> state = READY;
p_task-> blk_data = NULL;
spin_unlock(&p_sem-> lock);
ENABLE_IE();
return SUCCESS;
}
(3)核间中断,重新将任务加入就绪队列
// send ipi
void send_ipi(u32 cmd, void* p_data, u8 cpuid) {
spin_lock(&g_ipi_lock[cpuid]);
g_ipi_cmd[cpuid] = cmd;
g_ipi_data[cpuid] = p_data;
ipi_trigger(cpuid);
}
// ipi isr func, for all cores
void ipi_isr_func() {
u8 cpuid;
u32 cmd;
void* p_data;
Task* p_task;
cpuid = get_local_cpu();
cmd = g_ipi_cmd[cpuid];
p_data = g_ipi_data[cpuid];
// ready to get other ipi cmd
spin_unlock(&g_ipi_lock[cpuid]);
DISABLE_IE();
p_task = (Task*) p_data;
if(BLOCKED == p_task-> state) {
add_to_rdy_queue(&g_ready[cpuid], p_task);
p_task-> blk_data = NULL;
p_task-> state = READY;
}
ENABLE_IE();
}
(4)目前大多数pc都是多核形式,用pc写smp os最好不过了,用vc调试起来也方便,
u8 get_local_cpu() {
s32 CPUInfo[4];
__cpuid(CPUInfo, 1);
if ((CPUInfo[3] & (1 << 9)) == 0)
return -1;
return (u8)(CPUInfo[1] >> 24);
}
其实写代码不用怕,越是大胆写,才越能提高自己的能力,才会进步更大。