代码适用于X86与X86_64架构。
不支持抢占,任务只能自己让出CPU。
下面是代码,将代码全部复制到1个.c文件中,便可编译运行。
/*
* 本软件为免费、开源软件。
* 本软件的版权(包括源码及2进制发布版本)归1切公众所有。
* 您可以自由使用、传播本软件。
* 您也能够以任何情势、任何目的使用本软件(包括源码及2进制发布版本),而不受任何版权限制。
* =====================
* 作者: 孙明保
* 邮箱: sunmingbao@126.com
*/
/*
* 本程序实现了用户态的任务调度。
* 适用于X86及X86_64架构。
* 编译运行方法:
* [root@localhost ~]# gcc sched.c
* [root@localhost ~]# ./a.out
*/
#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:
"fmt"
", __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;
#define task_ptr2idx(ptr) ((t_task *)(ptr)-g_at_tasks)
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;
#if defined( __i386)
#define save_context(mem_var_to_save_sp)
do
{
asm volatile("pushfl
" /* save flags */
"pushl %%eax
"
"pushl %%edi
"
"pushl %%esi
"
"pushl %%edx
"
"pushl %%ecx
"
"pushl %%ebx
"
"pushl %%ebp
"
"movl %%esp,%[mem_var_to_store_sp]
"
: [mem_var_to_store_sp] "=m" (mem_var_to_save_sp)
);
} while (0)
#define restore_context(mem_var_saved_sp)
do
{
asm volatile("movl %[mem_var_contain_sp],%%esp
"
"popl %%ebp
"
"popl %%ebx
"
"popl %%ecx
"
"popl %%edx
"
"popl %%esi
"
"popl %%edi
"
"popl %%eax
"
"popfl
"
:: [mem_var_contain_sp] "m" (mem_var_saved_sp)
);
} while (0)
#elif defined( __x86_64)
#define save_context(mem_var_to_save_sp)
do
{
asm volatile("pushfq
" /* save flags */
"pushq %%rax
"
"pushq %%rdi
"
"pushq %%rsi
"
"pushq %%rdx
"
"pushq %%rcx
"
"pushq %%rbx
"
"pushq %%rbp
"
"movq %%rsp,%[mem_var_to_store_sp]
"
: [mem_var_to_store_sp] "=m" (mem_var_to_save_sp)
);
} while (0)
#define restore_context(mem_var_saved_sp)
do
{
asm volatile("movq %[mem_var_contain_sp],%%rsp
"
"popq %%rbp
"
"popq %%rbx
"
"popq %%rcx
"
"popq %%rdx
"
"popq %%rsi
"
"popq %%rdi
"
"popq %%rax
"
"popfq
"
:: [mem_var_contain_sp] "m" (mem_var_saved_sp)
);
} while (0)
#endif
int i;
int task_scheduler()
{
unsigned long ret;
#if defined( __i386)
asm volatile("movl %%eax,%[task_ret]
"
: [task_ret] "=m" (ret)
);
#elif defined( __x86_64)
asm volatile("movq %%rax,%[task_ret]
"
: [task_ret] "=m" (ret)
);
#endif
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);
}
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;
/* 准备运行下1个可运行任务 */
restore_context(next_task_sp);
asm volatile("jmp *%[next_pc]
"
:: [next_pc] "m" (next_task_pc)
);
}
}
DBG_PRINT("==no task to run. so we exit");
restore_context(process_main_thread_sp);
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);
#if defined( __i386)
push_task_stack(sp, (unsigned long)para);
#endif
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);
#if defined( __i386)
push_task_stack(sp, 0);
#elif defined( __x86_64)
push_task_stack(sp, (unsigned long)para);
#endif
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;
}
#if defined( __i386)
#define switch_context(prev, next)
do
{
asm volatile("pushfl
"
"pushl %%eax
"
"pushl %%edi
"
"pushl %%esi
"
"pushl %%edx
"
"pushl %%ecx
"
"pushl %%ebx
"
"pushl %%ebp
"
"movl %%esp,%[prev_sp]
"
"movl $1f,%[prev_pc]
"
"movl %[next_sp],%%esp
"
"popl %%ebp
" /* restore BP */
"popl %%ebx
"
"popl %%ecx
"
"popl %%edx
"
"popl %%esi
"
"popl %%edi
"
"popl %%eax
"
"popfl
"
"jmp *%[next_pc]
"
"1: "
"nop
"
: [prev_sp] "=m" (prev->sp),
[prev_pc] "=m" (prev->pc)
: [next_sp] "m" (next_task_sp), [next_pc] "m" (next_task_pc)
);
}while (0)
#elif defined( __x86_64)
#define switch_context(prev, next)
do
{
asm volatile("pushfq
"
"pushq %%rax
"
"pushq %%rdi
"
"pushq %%rsi
"
"pushq %%rdx
"
"pushq %%rcx
"
"pushq %%rbx
"
"pushq %%rbp
"
"movq %%rsp,%[prev_sp]
"
"movq $1f,%[prev_pc]
"
"movq %[next_sp],%%rsp
"
"popq %%rbp
" /* restore BP */
"popq %%rbx
"
"popq %%rcx
"
"popq %%rdx
"
"popq %%rsi
"
"popq %%rdi
"
"popq %%rax
"
"popfq
"
"jmp *%[next_pc]
"
"1: "
"nop
"
: [prev_sp] "=m" (prev->sp),
[prev_pc] "=m" (prev->pc)
: [next_sp] "m" (next_task_sp), [next_pc] "m" (next_task_pc)
);
}while (0)
#endif
void schedule()
{
t_task *prev=pt_cur_running_task, *next=NULL;
int cur_task_idx=task_ptr2idx(prev);
for (i=cur_task_idx+1; i!=cur_task_idx; i=(i+1)%MAX_TASK_NUM)
{
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==cur_task_idx) return;
switch_context(prev, next);
}
int start_sched()
{
/* 启动调度,首个运行的任务是 father_of_all_task - g_at_tasks[0] */
save_context(process_main_thread_sp);
task_scheduler();
return 0;
}
/* 以上是调度功能的实现,下面是使用示例 */
int task_1_para=1001;
void * usr_task1(void *para)
{
int i;
DBG_PRINT("==enter, para=%d", *(int *)para);
for (i=10; i<13; i++)
{
DBG_PRINT("==%d", i);
schedule();
}
DBG_PRINT("==exit");
return (void *)100UL;
}
int task_2_para=2001;
void * usr_task2(void *para)
{
int i;
DBG_PRINT("==enter, para=%d", *(int *)para);
for (i=20; i<23; i++)
{
DBG_PRINT("==%d", i);
schedule();
}
DBG_PRINT("==exit");
return (void *)200UL;
}
int task_3_para=3001;
void * usr_task3(void *para)
{
int i;
DBG_PRINT("==enter, para=%d", *(int *)para);
for (i=30; i<33; i++)
{
DBG_PRINT("==%d", i);
schedule();
}
DBG_PRINT("==exit");
return (void *)300UL;
}
int root_task_para=1234;
void * father_of_all_task(void *para)
{
DBG_PRINT("==enter, para=%d", *(int *)para);
create_task("usr_task1", usr_task1, &task_1_para);
create_task("usr_task2", usr_task2, &task_2_para);
create_task("usr_task3", usr_task3, &task_3_para);
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, &root_task_para);
start_sched();
DBG_PRINT("good bye");
return 0;
}
软件运行效果:
[root@localhost ~]# gcc sched.c
[root@localhost ~]# ./a.out
DBG:sched.c(371)-main:
hello
DBG:sched.c(361)-father_of_all_task:
==enter, para=1234
DBG:sched.c(365)-father_of_all_task:
==exit
DBG:sched.c(145)-task_scheduler:
task father_of_all_task exit with code 2015
DBG:sched.c(320)-usr_task1:
==enter, para=1001
DBG:sched.c(323)-usr_task1:
==10
DBG:sched.c(334)-usr_task2:
==enter, para=2001
DBG:sched.c(337)-usr_task2:
==20
DBG:sched.c(348)-usr_task3:
==enter, para=3001
DBG:sched.c(351)-usr_task3:
==30
DBG:sched.c(323)-usr_task1:
==11
DBG:sched.c(337)-usr_task2:
==21
DBG:sched.c(351)-usr_task3:
==31
DBG:sched.c(323)-usr_task1:
==12
DBG:sched.c(337)-usr_task2:
==22
DBG:sched.c(351)-usr_task3:
==32
DBG:sched.c(326)-usr_task1:
==exit
DBG:sched.c(145)-task_scheduler:
task usr_task1 exit with code 100
DBG:sched.c(340)-usr_task2:
==exit
DBG:sched.c(145)-task_scheduler:
task usr_task2 exit with code 200
DBG:sched.c(354)-usr_task3:
==exit
DBG:sched.c(145)-task_scheduler:
task usr_task3 exit with code 300
DBG:sched.c(167)-task_scheduler:
==no task to run. so we exit
DBG:sched.c(374)-main:
good bye
[root@localhost ~]#