The code applies to the X86_64 schema.
Preemption is not supported and the task can only make its own CPU.
Here is the code, which copies all of the code into a. c file and compiles the run.
/*
* This software is free, open source software.
* The copyright of the software (including the source code and the binary release version) is owned by the public.
* You are free to use and distribute the software.
* You may also use the software (including source code and binary release versions) in any form or for any purpose, without any copyright restrictions.
* =====================
* Author: Sun Mingpao
* Email: [Email protected]
*/
#include <stdio.h>
#include <string.h>
#define MAX_TASK_NUM (10)
#define TASK_STACK_SIZE (4096)
#define Dbg_print (FMT, args ...) \
Do \
{ \
printf ("dbg:%s (%d)-%s:\n" FMT "\ n", __file__,__line__,__function__,# #args); \
} while (0)
typedef void * (*TASK_ENTRY_PTR) (void *);
typedef struct
{
Char name[64];
unsigned long state;
TASK_ENTRY_PTR entry;
unsigned long ret;
unsigned long SP;
unsigned long pc;
unsigned long stack[task_stack_size/sizeof (unsigned long)];
} __attribute__ ((packed)) T_task;
#define STATE_INVALID (0)
#define STATE_SLEEPING (1)
#define STATE_RUNNING (2)
#define STATE_FINISHED (3)
T_task G_at_tasks[max_task_num];
int g_task_cnt = 0;
unsigned long process_main_thread_sp;
T_task *pt_cur_running_task;
unsigned long prev_task_sp, prev_task_pc;
unsigned long next_task_sp, next_task_pc;
#define SAVE_MAIN_THREAD_CONTEXT () \
do { \
ASM volatile ("pushfq\n\t"/* Save Flags */ \
"Pushq%%rax\n\t" \
"Pushq%%rdi\n\t" \
"Pushq%%rsi\n\t" \
"Pushq%%rdx\n\t" \
"Pushq%%rcx\n\t" \
"Pushq%%rbx\n\t" \
"Pushq%%rbp\n\t" \
"Movq%%rsp,%[main_thread_sp]\n\t" \
: [main_thread_sp] "=m" (PROCESS_MAIN_THREAD_SP) \
); \
} while (0)
#define RESTORE_MAIN_THREAD_CONTEXT () \
do { \
ASM volatile ("Movq%[main_thread_sp],%%rsp\n\t" \
"Popq%%rbp\n\t" \
"Popq%%rbx\n\t" \
"Popq%%rcx\n\t" \
"Popq%%rdx\n\t" \
"Popq%%rsi\n\t" \
"Popq%%rdi\n\t" \
"Popq%%rax\n\t" \
"Popfq\n\t" \
:: [main_thread_sp] "M" (PROCESS_MAIN_THREAD_SP) \
); \
} while (0)
int i;
int Task_scheduler ()
{
unsigned long ret;
ASM volatile ("Movq%%rax,%[task_ret]\n\t"
: [Task_ret] "=m" (ret)
);
if (Pt_cur_running_task)
{
pt_cur_running_task->state=state_finished;
pt_cur_running_task->ret=ret;
Dbg_print ("Task%s exit with code%lu", Pt_cur_running_task->name, Pt_cur_running_task->ret);
}
Restore_main_thread_context ();
for (i=0;i<max_task_num;i++)
{
Pt_cur_running_task = & (G_at_tasks[i]);
if (pt_cur_running_task->state==state_sleeping)
{
pt_cur_running_task->state=state_running;
NEXT_TASK_SP = pt_cur_running_task->sp;
NEXT_TASK_PC = pt_cur_running_task->pc;
/* Prepare to run the next running task */
Save_main_thread_context ();
ASM volatile ("Movq%[next_sp],%%rsp\n\t"
/* "Movq $0x0,%%rdi\n\t"
"Pushq task_return\n\t" * *
"Popq%%rbp\n\t" \
"Popq%%rbx\n\t" \
"Popq%%rcx\n\t" \
"Popq%%rdx\n\t" \
"Popq%%rsi\n\t" \
"Popq%%rdi\n\t" \
"Popq%%rax\n\t" \
"Popfq\n\t" \
"JMP *%[next_pc]\n\t"
:: [next_sp] "M" (next_task_sp), [next_pc] "M" (NEXT_TASK_PC)
);
}
}
Dbg_print ("==no task to run. So we exit ");
return 0;
}
#define PUSH_TASK_STACK (SP, data) \
Do \
{ \
sp--; \
*SP = data; \
} while (0)
int create_task (const char *name, void *task_entry, void *para)
{
unsigned long *sp;
T_task *pt_task = & (g_at_tasks[g_task_cnt]);
strncpy (pt_task->name, name, sizeof (Pt_task->name));
Pt_task->entry = Task_entry;
PT_TASK->SP = (unsigned long) ((void *) (Pt_task + 1));
Pt_task->state = state_sleeping;
SP = (void *) (PT_TASK->SP);
Push_task_stack (SP, (unsigned long) (void *) &task_scheduler);
PT_TASK->PC = (unsigned long) task_entry;
Push_task_stack (sp, 0);
Push_task_stack (sp, 0);
Push_task_stack (SP, (unsigned long) para);
Push_task_stack (sp, 0);
Push_task_stack (sp, 0);
Push_task_stack (sp, 0);
Push_task_stack (sp, 0);
Push_task_stack (sp, PT_TASK->SP); /* Push BP at last */
PT_TASK->SP = (unsigned long) (void *) SP;
g_task_cnt++;
return 0;
}
#define SWITCH_TO (prev, next) \
do { \
ASM volatile ("pushfq\n\t" \
"Pushq%%rax\n\t" \
"Pushq%%rdi\n\t" \
"Pushq%%rsi\n\t" \
"Pushq%%rdx\n\t" \
"Pushq%%rcx\n\t" \
"Pushq%%rbx\n\t" \
"Pushq%%rbp\n\t" \
"Movq%%rsp,%[prev_sp]\n\t" \
"Movq $1f,%[prev_pc]\n\t" \
"Movq%[next_sp],%%rsp\n\t" \
"Popq%%rbp\n\t"/* Restore EBP */ \
"Popq%%rbx\n\t" \
"Popq%%rcx\n\t" \
"Popq%%rdx\n\t" \
"Popq%%rsi\n\t" \
"Popq%%rdi\n\t" \
"Popq%%rax\n\t" \
"Popfq\n\t" \
"JMP *%[next_pc]\n\t" \
"1:\t"\
"Nop\n\t" \
: [prev_sp] "=m" (PREV->SP), \
[PREV_PC] "=m" (PREV->PC) \
: [next_sp] "M" (next_task_sp), [next_pc] "M" (NEXT_TASK_PC) \
); \
}while (0)
void Schedule ()
{
T_task *prev=pt_cur_running_task, *next=null;
for (i=0;i<max_task_num;i++)
{
Next = & (G_at_tasks[i]);
if (next->state==state_sleeping)
{
pt_cur_running_task->state=state_sleeping;
PREV_TASK_SP = pt_cur_running_task->sp;
PREV_TASK_PC = pt_cur_running_task->pc;
Pt_cur_running_task = Next;
pt_cur_running_task->state=state_running;
NEXT_TASK_SP = pt_cur_running_task->sp;
NEXT_TASK_PC = pt_cur_running_task->pc;
Break
}
}
if (i==max_task_num) return;
Switch_to (prev, next);
}
int start_sched ()
{
/* Prepare to run the initial task Father_of_all_task--g_at_tasks[0] */
Save_main_thread_context ();
Task_scheduler ();
return 0;
}
/* Above is the implementation of the scheduling function, the following is the use of the example */
void * Usr_task1 (void *para)
{
int i;
Dbg_print ("==enter");
for (i=0; i<3; i++)
{
Dbg_print ("==%d", I);
Schedule ();
}
Dbg_print ("==exit");
return (void *) 100UL;
}
void * Usr_task2 (void *para)
{
int i;
Dbg_print ("==enter");
for (i=0; i<3; i++)
{
Dbg_print ("==%d", I);
Schedule ();
}
Dbg_print ("==exit");
return (void *) 200UL;
}
void * Father_of_all_task (void *para)
{
Dbg_print ("==enter");
Create_task ("Usr_task1", Usr_task1, NULL);
Create_task ("Usr_task2", Usr_task2, NULL);
Dbg_print ("==exit");
return (void *) 2015UL;
}
int main (int argc, char *argv[])
{
Dbg_print ("Hello");
Create_task ("Father_of_all_task", Father_of_all_task, NULL);
Start_sched ();
Dbg_print ("Good Bye");
return 0;
}
User-state implementation of thread scheduling (task scheduling)