一、实现内核线程
1. 执行流
调度器:在操作系统中用于把任务轮流调度上CPU运行的一个软件模块。 任务并行:用软件来切换任务,模拟出任务并行的假象。 流:CPU中程序计数器的航向。 执行流的定义:
执行流对应于代码,大到可以是整个程序文件,即进程;小到可以是一个功能独立的代码块,即函数,而线程的本质就是函数。 执行流是独立的,每个执行流都有自己的栈、一套自己的寄存器映像和内存资源。这是Intel处理器在硬件上规定的,是执行流的上下文环境。 任何代码块,无论大小,都可以独立成为执行流。只要在它运行前,给它准备好上下文环境就行。 执行流的本质是进程和线程。 执行流的作用:
在任务调度器眼中,只有执行流才是调度单元。 CPU上的每个任务都是调度器给分配的执行流,只要成为执行流就可以独立上CPU运行了。
2. 线程到底是什么
线程的本质是执行流,是一段引导CPU执行的、具有能动性的代码。 例子:thead.test
# include <stdio.h>
# include <pthread.h>
void * thread_func ( void * _arg) {
unsigned int * arg = _arg;
printf ( " new thread : my tid is %u\n" , * arg) ;
}
void main ( ) {
pthread_t new_thread_id;
pthread_create ( & new_thread_id, NULL , thread_func, & new_thread_id) ;
printf ( "main thread : my tid is %u\n" , pthread_self ( ) ) ;
usleep ( 100 ) ;
}
int pthread_create ( pthread_t * __restrict __newthread, _const pthread__attr_t * __restrict __attr, void * ( * __start_routine) ( void * ) , void * __restrict __arg) __THROW __nonnull ( ( 1 , 3 ) ) ;
参数名称 作用 __newthread 用于存储新线程的id,即uid __attr 用于指定线程的类型,默认类型为NULL __start_routine 函数指针,用于指向线程中所调用的函数的地址 __arg 给在线程中运行的函数__start_routine的参数,如果有2个及以上的参数,则封装为一个结构体
若pthread_create函数返回值为0,则线程创建成功。 线程的实际功能:调用函数。 线程和函数调用的区别:
执行流上CPU前,要准备好上下文环境。 函数调用是随着该函数所在的调度单元(执行流)一起上CPU的,是被顺便处理的。 CPU不是把线程中调用的函数和其他指令混合在一起执行的,而是专门、单独地执行了函数(线程)。
3. 进程与线程的关系
程序与进程
程序是指静态的、存储在文件系统上、尚未运行的指令代码。 进程是指正在运行的程序,程序必须在获得运行所需要的各类资源后才能成为进程。资源包括进程所使用的栈和寄存器等。 进程分为单线程进程和多线程进程。如果未显示创建线程,则为单线程进程。 进程和线程
对于CPU而言,进程是一种控制流集合,集合中至少包含一条执行流,执行流之间是相互独立的,但它们共享进程的所有资源,它们是CPU的执行单元,调度单位,即线程。 进程有独立的虚拟地址空间,有资源。各进程无法访问对方内部,这是由操作系统的分页机制保证的。 线程没有独立的地址空间,没有资源,所有线程共用进程的地址空间和资源。 进程和线程都是执行流,都具备独立寄存器资源和独立栈空间。因此线程可以调用函数。 在CPU上运行的执行流都是人为划分的逻辑上独立的代码段,本质都是一段代码区域,只不过线程是纯粹的执行部分。它运行所需要的资源都在进程中。进程 = 线程 + 资源。 若显示创建了线程,则任务调度器会将它对应的代码块从进程中分离出来单独调度上CPU执行;否则调度器会将整个进程当作一个大的执行流,从头到尾去执行。 程序员写程序时会将整个任务划分为几个独立的部分,每一部分就用线程完成,各部分是独立的,可以并行完成。(A部分依赖用户输入,B部分不依赖) 书后面提到的进程,都是指单线程进程。
4. 进程、线程的状态
状态名称 状态 运行态 正在CPU上运行的进程的状态 就绪态 外界条件成立,进程可以随时准备运行的状态 阻塞态 需要等待外界条件的状态
调度器的调度单位是执行流,所以状态是对于执行流而言的,状态是描述线程的。
5. 进程的身份证——PCB
PCB:程序控制块,用来记录与进程有关的信息,如进程状态、PID、优先级等。 进程表:所有PCB放到一张表中维护,调度器可以根据此表选择上CPU运行的进程。PCB故又称为进程表项。
PCB结构 功能 进程状态 保存进程的状态 时间片 时间片=0时下CPU 页表 代表进程的地址空间 寄存器映像 保存进程的现场,进程在CPU上运行时,所有寄存器的值保存在此 栈指针 寄存器映像的位置不固定,栈指针记录0级栈栈顶的位置,借此找到进程的“寄存器映像”
寄存器映像的位置不固定,因为寄存器映像存储在内核栈中:
当进程/线程中断时,CPU自动在TSS中获取内核栈指针,。因此通常情况下,寄存器映像位于PCB顶端。 当在内核态下工作时,栈指针已经发生了变化时才向栈中保存寄存器映像,例如线程主动让出CPU。此时寄存器映像必然不在PCB顶端了。
6. 实现线程的两种方式——内核或用户进程
线程的分类:
在0特权级的内核空间中实现线程:线程机制由内核提供,并不是说线程所运行的代码也必须是0特权级的内核级代码。内核毕竟是为用户进程提供服务的。 在3特权级的内核空间中实现线程:线程机制由用户进程自己提供,相当于用户进程除了负责业务外,还要在进程中实现线程调度器。所以标准库提供了用户级线程库,程序员直接使用标准线程库即可。 用户特权级是3,线程中只能运行自己进程内的代码,只能同级调用,不能调用0特权级的内核代码。 线程仅仅是个执行流,在哪里实现取决于线程表在哪里,由谁来调度它上CPU。如果线程在用户空间中实现,则线程表在用户进程中,用户进程就要专门写个线程用作线程调度器;如果线程是在内核空间中实现的,线程表在内核中,该线程就会由OS的调度器统一调度。
在用户空间中实现线程
线程调度器:开发人员在用户进程中调用线程库来创建线程、结束线程等。线程库中一定存在线程调度器,线程库中的方法都会与此线程调度器有调度关系。当有新线程产生/退出时,线程调度器才会被调用,从而在内部维护的线程表中找出下一个线程上CPU。 优点:
可移植性强,由于是用户级的实现,所以在不支持线程的OS上也可以写出支持线程的用户程序。 有用户实现,可以根据实际情况为某线程加权调度。 将线程的寄存器映像装载到CPU时,可以在用户空间完成,不用陷入到内核态,免去了进入内核时的入栈和出栈操作。(恢复上下文) 缺点:
若进程中某线程阻塞,OS不知道进程中存在线程,会将该进程挂起。 线程未在内核空间中实现,调度器的调度单元是整个进程,不是线程。所以时钟中断只能影响进程一级的执行流,但凡进程中的某个线程开始在CPU上执行,只要该线程不主动退出CPU,该进程中的其他线程就没机会运行。 用户级线程只在内部调度时少了陷入内核的代价,但是整个进程的时间片有限,有限的时间片要分出时间给进程内线程调度器维护线程表、运行调度算法等时间开销,反而会降速。 在内核空间中实现线程
优点:
内核级线程相当于让进程多占了CPU资源。 当一个线程阻塞时,只会阻塞这一个线程。
二、在内核空间实现线程
1. 简单的PCB及线程栈的实现
--------------------------代码----------------------------
“/home/lily/OS/boot/thread/thread.h”
# ifndef __THREAD_THREAD_H
# define __THREAD_THREAD_H
# include "stdint.h"
typedef void thread_func ( void * ) ;
enum task_status {
TASK_RUNNING,
TASK_READY,
TASK_BLOCKED,
TASK_WAITING,
TASK_HANGING,
TASK_DIED
} ;
struct intr_stack {
uint32_t vec_no;
uint32_t edi;
uint32_t esi;
uint32_t ebp;
uint32_t esp_dummy;
uint32_t ebx;
uint32_t edx;
uint32_t ecx;
uint32_t eax;
uint32_t gs;
uint32_t fs;
uint32_t es;
uint32_t ds;
uint32_t err_code;
void ( * eip) ( void ) ;
uint32_t cs;
uint32_t eflags;
void * esp;
uint32_t ss;
} ;
struct thread_stack {
uint32_t ebp;
uint32_t ebx;
uint32_t edi;
uint32_t esi;
void ( * eip) ( thread_func* func, void * func_arg) ;
void ( * unused_retaddr) ;
thread_func* function;
void * func_arg;
} ;
struct task_struct {
uint32_t * self_kstack;
enum task_status status;
uint8_t priority;
char name[ 16 ] ;
uint32_t stack_magic;
} ;
# endif
2. 线程的实现
--------------------------代码----------------------------
“/home/lily/OS/boot/thread/thread.h”
# ifndef __THREAD_THREAD_H
# define __THREAD_THREAD_H
# include "stdint.h"
typedef void thread_func ( void * ) ;
enum task_status {
TASK_RUNNING,
TASK_READY,
TASK_BLOCKED,
TASK_WAITING,
TASK_HANGING,
TASK_DIED
} ;
struct intr_stack {
uint32_t vec_no;
uint32_t edi;
uint32_t esi;
uint32_t ebp;
uint32_t esp_dummy;
uint32_t ebx;
uint32_t edx;
uint32_t ecx;
uint32_t eax;
uint32_t gs;
uint32_t fs;
uint32_t es;
uint32_t ds;
uint32_t err_code;
void ( * eip) ( void ) ;
uint32_t cs;
uint32_t eflags;
void * esp;
uint32_t ss;
} ;
struct thread_stack {
uint32_t ebp;
uint32_t ebx;
uint32_t edi;
uint32_t esi;
void ( * eip) ( thread_func* func, void * func_arg) ;
void ( * unused_retaddr) ;
thread_func* function;
void * func_arg;
} ;
struct task_struct {
uint32_t * self_kstack;
enum task_status status;
uint8_t priority;
char name[ 16 ] ;
uint32_t stack_magic;
} ;
void thread_create ( struct task_struct * pthread, thread_func function, void * func_arg) ;
void init_thread ( struct task_struct * pthread, char * name, int prio) ;
struct task_struct * thread_start ( char * name, int prio, thread_func function, void * func_arg) ;
# endif
“/home/lily/OS/boot/thread/thread.c”
# include "thread.h"
# include "stdint.h"
# include "string.h"
# include "global.h"
# include "memory.h"
# define PG_SIZE 4096
static void kernel_thread ( thread_func* funcion, void * func_arg) {
funcion ( func_arg) ;
}
void thread_create ( struct task_struct * pthread, thread_func function, void * func_arg) {
pthread-> self_kstack -= sizeof ( struct intr_stack ) ;
pthread-> self_kstack -= sizeof ( struct thread_stack ) ;
struct thread_stack * kthread_stack = ( struct thread_stack * ) pthread-> self_kstack;
kthread_stack-> eip = kernel_thread;
kthread_stack-> function = function;
kthread_stack-> func_arg = func_arg;
kthread_stack-> ebp = kthread_stack-> ebx = kthread_stack-> edi = kthread_stack-> esi = 0 ;
}
void init_thread ( struct task_struct * pthread, char * name, int prio) {
memset ( pthread, 0 , sizeof ( * pthread) ) ;
strcpy ( pthread-> name, name) ;
pthread-> status = TASK_RUNNING;
pthread-> priority = prio;
pthread-> self_kstack = ( uint32_t * ) ( ( uint32_t ) pthread + PG_SIZE) ;
pthread-> stack_magic = 0x19870916 ;
}
struct task_struct * thread_start ( char * name, int prio, thread_func function, void * func_arg) {
struct task_struct * thread = get_kernel_pages ( 1 ) ;
init_thread ( thread, name, prio) ;
thread_create ( thread, function, func_arg) ;
asm volatile ( "movl %0,%%esp; \
pop %%ebp;pop %%ebx;pop %%edi;pop %%esi; \
ret" : : "g" ( thread-> self_kstack) : "memory" ) ;
return thread;
}
“/home/lily/OS/boot/kernel/main.c”
# include "print.h"
# include "init.h"
# include "thread.h"
void k_thread_a ( void * ) ;
int main ( void ) {
put_str ( "I am kernel\n" ) ;
init_all ( ) ;
thread_start ( "k_thread_a" , 31 , k_thread_a, "argA " ) ;
while ( 1 ) ;
return 0 ;
}
void k_thread_a ( void * arg) {
char * para = arg;
while ( 1 )
{
put_str ( para) ;
}
}
“/home/lily/OS/boot/makefile”
BUILD_DIR = . / build
##用来存储生成的所有目标文件
ENTRY_POINT = 0xc0001500
AS = nasm
CC = gcc
LD = ld
LIB = - I lib/ - I lib/ kernel/ - I lib/ user/ - I kernel/ - I device/ - I thread/
ASFLAGS = - f elf
CFLAGS = - Wall - m32 - fno- stack- protector $( LIB) - c - fno- builtin - W - Wstrict- prototypes - Wmissing- prototypes
##- fno- builtin是告诉编译器不要采用内部函数 - Wstrict- prototypes是要求函数声明中必须有参数类型
## - Wmissing- prototypes要求函数必须有声明
LDFLAGS = - m elf_i386 - Ttext $( ENTRY_POINT) - e main - Map $( BUILD_DIR) / kernel. map
OBJS = $( BUILD_DIR) / main. o $( BUILD_DIR) / init. o $( BUILD_DIR) / interrupt. o \
$( BUILD_DIR) / timer. o $( BUILD_DIR) / kernel. o $( BUILD_DIR) / print. o $( BUILD_DIR) / debug. o \
$( BUILD_DIR) / string. o $( BUILD_DIR) / memory. o $( BUILD_DIR) / bitmap. o $( BUILD_DIR) / thread. o
## OBJS用来存储所有目标文件名,不要用% . o,因为不能保证链接顺序
########## c代码编译 ##########
$( BUILD_DIR) / main. o: kernel/ main. c lib/ kernel/ print. h lib/ kernel/ stdint. h kernel/ init. h kernel/ memory. h \
thread/ thread. h
$( CC) $( CFLAGS) $< - o $@
$( BUILD_DIR) / init. o: kernel/ init. c kernel/ init. h lib/ kernel/ print. h lib/ kernel/ stdint. h \
kernel/ interrupt. h device/ timer. h
$( CC) $( CFLAGS) $< - o $@
$( BUILD_DIR) / interrupt. o: kernel/ interrupt. c kernel/ interrupt. h lib/ kernel/ stdint. h \
kernel/ global. h lib/ kernel/ io. h lib/ kernel/ print. h
$( CC) $( CFLAGS) $< - o $@
$( BUILD_DIR) / timer. o: device/ timer. c device/ timer. h lib/ kernel/ stdint. h lib/ kernel/ io. h lib/ kernel/ print. h
$( CC) $( CFLAGS) $< - o $@
$( BUILD_DIR) / thread. o: thread/ thread. c thread/ thread. h lib/ kernel/ stdint. h lib/ string. h kernel/ global. h kernel/ memory. h
$( CC) $( CFLAGS) $< - o $@
$( BUILD_DIR) / debug. o: kernel/ debug. c kernel/ debug. h lib/ kernel/ print. h lib/ kernel/ stdint. h kernel/ interrupt. h
$( CC) $( CFLAGS) $< - o $@
$( BUILD_DIR) / string. o: lib/ string. c lib/ string. h kernel/ debug. h kernel/ global. h lib/ kernel/ stdint. h
$( CC) $( CFLAGS) $< - o $@
$( BUILD_DIR) / memory. o: kernel/ memory. c kernel/ memory. h lib/ kernel/ stdint. h lib/ kernel/ bitmap. h kernel/ debug. h lib/ string. h \
lib/ kernel/ print. h kernel/ global. h
$( CC) $( CFLAGS) $< - o $@
$( BUILD_DIR) / bitmap. o: lib/ kernel/ bitmap. c lib/ kernel/ bitmap. h lib/ string. h kernel/ interrupt. h lib/ kernel/ print. h \
kernel/ debug. h lib/ kernel/ stdint. h
$( CC) $( CFLAGS) $< - o $@
###########汇编代码编译############
$( BUILD_DIR) / kernel. o: kernel/ kernel. S
$( AS) $( ASFLAGS) $< - o $@
$( BUILD_DIR) / print. o: lib/ kernel/ print. S
$( AS) $( ASFLAGS) $< - o $@
##########链接所有目标文件#############
$( BUILD_DIR) / kernel. bin: $( OBJS)
$( LD) $( LDFLAGS) $^ - o $@
. PHONY: mk_dir hd clean all
mk_dir:
if [ ! - d $( BUILD_DIR) ] ; then mkdir $( BUILD_DIR) ; fi
###fi为终止符
hd:
dd if = $( BUILD_DIR) / kernel. bin of= / home/ lily/ bochs/ hd60M. img bs= 512 count= 200 seek= 9 conv= notrunc
clean: ##将build目录下文件清空
cd $( BUILD_DIR) && rm - f .
编译运行
三、核心数据结构,双向链表
--------------------------代码----------------------------
“/home/lily/OS/boot/lib/kernel/list.h”
# ifndef __LIB_KERNEL_LIST_H
# define __LIB_KERNEL_LIST_H
# include "global.h"
# define offset ( struct_type, member) ( int ) ( & ( ( struct_type* ) 0 ) -> member)
# define elem2entry ( struct_type, struct_member_name, elem_ptr) ( struct_type* ) ( ( int ) elem_ptr - offset ( struct_type, struct_member_name) )
struct list_elem {
struct list_elem * prev;
struct list_elem * next;
} ;
struct list {
struct list_elem head;
struct list_elem tail;
} ;
typedef bool ( function) ( struct list_elem * , int arg) ;
void list_init ( struct list * ) ;
void list_insert_before ( struct list_elem * before, struct list_elem * elem) ;
void list_push ( struct list * plist, struct list_elem * elem) ;
void list_iterate ( struct list * plist) ;
void list_append ( struct list * plist, struct list_elem * elem) ;
void list_remove ( struct list_elem * pelem) ;
struct list_elem * list_pop ( struct list * plist) ;
bool list_empty ( struct list * plist) ;
uint32_t list_len ( struct list * plist) ;
struct list_elem * list_traversal ( struct list * plist, function func, int arg) ;
bool elem_find ( struct list * plist, struct list_elem * obj_elem) ;
# endif
“/home/lily/OS/boot/lib/kernel/list.c”
# include "list.h"
# include "interrupt.h"
# include "stdint.h"
# include "string.h"
void list_init ( struct list * list) {
list-> head. prev = NULL ;
list-> head. next = & list-> tail;
list-> tail. prev = & list-> head;
list-> tail. next = NULL ;
}
void list_insert_before ( struct list_elem * before, struct list_elem * elem) {
enum intr_status old_status = intr_disable ( ) ;
before-> prev-> next = elem;
elem-> prev = before-> prev;
elem-> next = before;
before-> prev = elem;
intr_set_status ( old_status) ;
}
void list_push ( struct list * plist, struct list_elem * elem) {
list_insert_before ( plist-> head. next, elem) ;
}
void list_append ( struct list * plist, struct list_elem * elem) {
list_insert_before ( & plist-> tail, elem) ;
}
void list_remove ( struct list_elem * pelem) {
enum intr_status old_status = intr_disable ( ) ;
pelem-> prev-> next = pelem-> next;
pelem-> next-> prev = pelem-> prev;
intr_set_status ( old_status) ;
}
struct list_elem * list_pop ( struct list * plist) {
struct list_elem * elem = plist-> head. next;
list_remove ( elem) ;
return elem;
}
bool elem_find ( struct list * plist, struct list_elem * obj_elem) {
struct list_elem * elem = plist-> head. next;
while ( elem != & plist-> tail) {
if ( elem == obj_elem)
return true;
elem = elem-> next;
}
return false;
}
struct list_elem * list_traversal ( struct list * plist, function func, int arg) {
if ( list_empty ( plist) )
return NULL ;
struct list_elem * elem = plist-> head. next;
while ( elem != & plist-> tail) {
if ( func ( elem, arg) )
return elem;
elem = elem-> next;
}
return NULL ;
}
uint32_t list_len ( struct list * plist) {
uint32_t length = 0 ;
struct list_elem * elem = plist-> head. next;
while ( elem != & plist-> tail) {
length++ ;
elem = elem-> next;
}
return length;
}
bool list_empty ( struct list * plist) {
return ( plist-> head. next == & plist-> tail ? true : false) ;
}
四、多线程调度
1. 简单优先级调度的基础
--------------------------代码----------------------------
“/home/lily/OS/boot/thread/thread.h”
# ifndef __THREAD_THREAD_H
# define __THREAD_THREAD_H
# include "stdint.h"
# include "list.h"
typedef void thread_func ( void * ) ;
enum task_status {
TASK_RUNNING,
TASK_READY,
TASK_BLOCKED,
TASK_WAITING,
TASK_HANGING,
TASK_DIED
} ;
struct intr_stack {
uint32_t vec_no;
uint32_t edi;
uint32_t esi;
uint32_t ebp;
uint32_t esp_dummy;
uint32_t ebx;
uint32_t edx;
uint32_t ecx;
uint32_t eax;
uint32_t gs;
uint32_t fs;
uint32_t es;
uint32_t ds;
uint32_t err_code;
void ( * eip) ( void ) ;
uint32_t cs;
uint32_t eflags;
void * esp;
uint32_t ss;
} ;
struct thread_stack {
uint32_t ebp;
uint32_t ebx;
uint32_t edi;
uint32_t esi;
void ( * eip) ( thread_func* func, void * func_arg) ;
void ( * unused_retaddr) ;
thread_func* function;
void * func_arg;
} ;
struct task_struct {
uint32_t * self_kstack;
enum task_status status;
uint8_t priority;
char name[ 16 ] ;
uint8_t ticks;
uint32_t elapsed_ticks;
struct list_elem general_tag;
struct list_elem all_list_tag;
uint32_t * pgdir;
uint32_t stack_magic;
} ;
void thread_create ( struct task_struct * pthread, thread_func function, void * func_arg) ;
void init_thread ( struct task_struct * pthread, char * name, int prio) ;
struct task_struct * thread_start ( char * name, int prio, thread_func function, void * func_arg) ;
# endif
“/home/lily/OS/boot/thread/thread.c”
# include "thread.h"
# include "stdint.h"
# include "string.h"
# include "global.h"
# include "memory.h"
# define PG_SIZE 4096
struct task_struct * main_thread;
struct list thread_ready_list;
struct list thread_all_list;
static struct list_elem * thread_tag;
extern void switch_to ( struct task_struct * cur, struct task_struct * next) ;
struct task_struct * running_thread ( ) {
uint32_t esp;
asm ( "mov %%esp,%0" : "=g" ( esp) ) ;
return ( struct task_struct * ) ( esp & 0xfffff000 ) ;
}
static void kernel_thread ( thread_func* funcion, void * func_arg) {
intr_enable ( ) ;
funcion ( func_arg) ;
}
void thread_create ( struct task_struct * pthread, thread_func function, void * func_arg) {
pthread-> self_kstack -= sizeof ( struct intr_stack ) ;
pthread-> self_kstack -= sizeof ( struct thread_stack ) ;
struct thread_stack * kthread_stack = ( struct thread_stack * ) pthread-> self_kstack;
kthread_stack-> eip = kernel_thread;
kthread_stack-> function = function;
kthread_stack-> func_arg = func_arg;
kthread_stack-> ebp = kthread_stack-> ebx = kthread_stack-> edi = kthread_stack-> esi = 0 ;
}
void init_thread ( struct task_struct * pthread, char * name, int prio) {
memset ( pthread, 0 , sizeof ( * pthread) ) ;
strcpy ( pthread-> name, name) ;
if ( pthread == main_thread)
pthread = TASK_RUNNING;
else
pthread = TASK_READY;
pthread-> self_kstack = ( uint32_t * ) ( ( uint32_t ) pthread + PG_SIZE) ;
pthread-> priority = prio;
pthread-> ticks = prio;
pthread-> elapsed_ticks = 0 ;
pthread-> pgdir = NULL ;
pthread-> stack_magic = 0x19870916 ;
}
struct task_struct * thread_start ( char * name, int prio, thread_func function, void * func_arg) {
struct task_struct * thread = get_kernel_pages ( 1 ) ;
init_thread ( thread, name, prio) ;
thread_create ( thread, function, func_arg) ;
ASSERT ( ! elem_find ( & thread_ready_list, & thread-> general_tag) ) ;
list_append ( & thread_ready_list, & thread-> general_tag) ;
ASSERT ( ! elem_find ( & thread_all_list, & thread-> all_list_tag) ) ;
list_append ( & thread_all_list, & thread-> all_list_tag) ;
return thread;
}
static void make_main_thread ( void ) {
main_thread = running_thread ( ) ;
init_thread ( main_thread, "main" , 31 ) ;
ASSERT ( ! elem_find ( & thread_all_list, & main_thread-> all_list_tag) ) ;
list_append ( & thread_all_list, & main_thread-> all_list_tag) ;
}
2. 任务调度器和任务切换
调度器的主要任务是读写就绪队列,增删里面的结点。结点是pcb中的general_tag,相当于线程的pcb,从队列中将其取出时一定要还原成pcb才行。 调度方式:RR,轮询调度。 调度器按照先进先出的顺序,把就绪队列中的第一个结点作为下一个要运行的新线程,将该线程的状态设置为TASK_RUNNING,之后通过函数switch_to将新线程的寄存器环境恢复,使新线程开始执行。 完整的调度过程需要三部分的配合:
时钟中断处理函数。 调度器schedule 任务切换函数switch_to 用户态和内核态:
程序所做的完整工作可以分为两部分,一部分是“重要工作”,这由OS代码实现;另一部分是“普通工作”,这由用户代码完成。 完整的程序 = 用户代码 + 内核代码。 任务在执行的过程中会执行用户代码和内核代码,当CPU处于低特权级下执行用户代码时我们称为用户态;当CPU进入高特权级执行内核代码时,我们称为内核态。 当CPU从用户代码所在的低特权级->内核代码所在的高特权级时,这称为陷入内核。 无论是执行用户代码还是内核代码,这些代码都属于一个完整的程序,并不是说当前任务由用户态进入内核态后当前任务就切换成内核了。 任务与任务的区别在于执行流一整套的上下文资源,包括寄存器映像、地址空间、IO位图等,拥有这些资源才称得上是任务。因此,CPU只有被新的上下文资源重新装载后,当前任务才被替换成新的任务,这叫任务切换。 当程序执行代码时,它需要通过系统调用让内核帮忙完成。 保护任务上下文
进入中断时的保护:保护任务的全部寄存器映像。当恢复寄存器后,如果此任务是用户进程,任务就恢复为用户程序继续在用户态下执行;如果该任务是内核线程,任务就恢复为另一段被中断执行的内核代码,在内核态执行。 保护内核环境的上下文:除esp外,只保护esi、edi、ebx和ebp。即恢复为在内核的中断处理程序中继续执行的状态,并不是让任务恢复到中断前,依然还在内核中。这几个寄存器会让CPU把程序执行到内核代码的结束处,在那里可以用第一部分中保护的全部寄存器映像来恢复任务,从而退出中断,使任务彻底恢复为进入中断前的状态。 中断发生时,当前运行的任务(线程/用户进程)被打断,随后会去执行中断处理程序,不管当前任务在中断前的特权级是什么,执行中断处理程序时肯定是0特权级。因此进入中断后所执行的一切内核代码也依然属于当前任务,只是由内核来提供这一部分罢了。
--------------------------代码----------------------------
“/home/lily/OS/boot/kernel/interrupt.c”
# include "interrupt.h"
# include "stdint.h"
# include "global.h"
# include "io.h"
# include "print.h"
# define IDT_DESC_CNT 0x21
# define PIC_M_CTRL 0x20
# define PIC_M_DATA 0x21
# define PIC_S_CTRL 0xa0
# define PIC_S_DATA 0xa1
# define EFLAGS_IF 0x00000200
# define GET_FLAGS ( EFLAG_VAR) asm volatile ( "pushfl;popl %0" : "=g" ( EFLAG_VAR) )
struct gate_desc {
uint16_t func_offset_low_word;
uint16_t selector;
uint8_t dcount;
uint8_t attribute;
uint16_t func_offset_high_word;
} ;
static void make_idt_desc ( struct gate_desc * p_gdesc, uint8_t attr, intr_handler function) ;
static struct gate_desc idt[ IDT_DESC_CNT] ;
extern intr_handler intr_entry_table[ IDT_DESC_CNT] ;
char * intr_name[ IDT_DESC_CNT] ;
intr_handler idt_table[ IDT_DESC_CNT] ;
static void make_idt_desc ( struct gate_desc * p_gdesc, uint8_t attr, intr_handler function) {
p_gdesc-> func_offset_low_word = ( uint32_t ) function & 0x0000ffff ;
p_gdesc-> selector = SELECTOR_K_CODE;
p_gdesc-> dcount = 0 ;
p_gdesc-> attribute = attr;
p_gdesc-> func_offset_high_word = ( ( uint32_t ) function & 0xffff0000 ) >> 16 ;
}
static void idt_desc_init ( void ) {
int i;
for ( i = 0 ; i < IDT_DESC_CNT; i++ ) {
make_idt_desc ( & idt[ i] , IDT_DESC_ATTR_DPL0, intr_entry_table[ i] ) ;
}
put_str ( " idt_desc_init done\n" ) ;
}
static void pic_init ( void ) {
outb ( PIC_M_CTRL, 0x11 ) ;
outb ( PIC_M_DATA, 0x20 ) ;
outb ( PIC_M_DATA, 0x04 ) ;
outb ( PIC_M_DATA, 0x01 ) ;
outb ( PIC_S_CTRL, 0x11 ) ;
outb ( PIC_S_DATA, 0x28 ) ;
outb ( PIC_S_DATA, 0x02 ) ;
outb ( PIC_S_DATA, 0x01 ) ;
outb ( PIC_M_DATA, 0xfe ) ;
outb ( PIC_S_DATA, 0xff ) ;
put_str ( " pic_init done\n" ) ;
}
static void general_intr_handler ( uint8_t vec_nr) {
if ( vec_nr == 0x27 || vec_nr == 0x2f ) {
return ;
}
set_cursor ( 0 ) ;
int cursor_pos = 0 ;
while ( cursor_pos < 320 ) {
put_char ( ' ' ) ;
cursor_pos++ ;
}
set_cursor ( 0 ) ;
put_str ( "!!!!! exception message begin !!!!!\n" ) ;
set_cursor ( 88 ) ;
put_str ( intr_name[ vec_nr] ) ;
if ( vec_nr == 14 ) {
int page_fault_vaddr = 0 ;
asm ( "movl %%cr2, %0" : "=r" ( page_fault_vaddr) ) ;
put_str ( "\npage fault addr is " ) ;
put_int ( page_fault_vaddr) ;
}
put_str ( "\n!!!!! exception message end !!!!!\n" ) ;
while ( 1 ) ;
}
static void exception_init ( void ) {
int i;
for ( i = 0 ; i < IDT_DESC_CNT; i++ ) {
idt_table[ i] = general_intr_handler;
intr_name[ i] = "unknown" ;
}
intr_name[ 0 ] = "#DE Divide Error" ;
intr_name[ 1 ] = "#DB Debug Exception" ;
intr_name[ 2 ] = "NMI Interrupt" ;
intr_name[ 3 ] = "BP Breakpoint Exception" ;
intr_name[ 4 ] = "#OF Overflow Exception" ;
intr_name[ 5 ] = "#BR BOUND Range Exceeded Exception" ;
intr_name[ 6 ] = "#UD Invalid Opcode Exception" ;
intr_name[ 7 ] = "#NM Device Not Available Exception" ;
intr_name[ 8 ] = "#DF Double Fault Exception" ;
intr_name[ 9 ] = "Coprocessor Segment Overrun" ;
intr_name[ 10 ] = "#TS Invalid TSS Exception" ;
intr_name[ 11 ] = "#NP Segment Not Present" ;
intr_name[ 12 ] = "#SS Stack Fault Exception" ;
intr_name[ 13 ] = "#GP General Protection Exception" ;
intr_name[ 14 ] = "#PF Page-Fault Exception" ;
intr_name[ 16 ] = "#MF x87 FPU Floating-Point Error" ;
intr_name[ 17 ] = "#AC Alignment Check Exception" ;
intr_name[ 18 ] = "#MC Machine-Check Exception" ;
intr_name[ 19 ] = "#XF SIMD Floating-Point Exception" ;
}
void idt_init ( ) {
put_str ( "idt_init start\n" ) ;
idt_desc_init ( ) ;
exception_init ( ) ;
pic_init ( ) ;
uint64_t idt_operand = ( ( sizeof ( idt) - 1 ) | ( ( uint64_t ) ( uint32_t ) idt << 16 ) ) ;
asm volatile ( "lidt %0" : : "m" ( idt_operand) ) ;
put_str ( "idt_init done\n" ) ;
}
enum intr_status intr_enable ( ) {
enum intr_status old_status;
if ( INTR_ON == intr_get_status ( ) ) {
old_status = INTR_ON;
return old_status;
} else {
old_status = INTR_OFF;
asm volatile ( "sti" ) ;
return old_status;
}
}
enum intr_status intr_disable ( ) {
enum intr_status old_status;
if ( INTR_ON == intr_get_status ( ) ) {
old_status = INTR_ON;
asm volatile ( "cli" : : : "memory" ) ;
return old_status;
} else {
old_status = INTR_OFF;
return old_status;
}
}
enum intr_status intr_set_status ( enum intr_status status) {
return status & INTR_ON ? intr_enable ( ) : intr_disable ( ) ;
}
enum intr_status intr_get_status ( ) {
uint32_t eflags = 0 ;
GET_FLAGS ( eflags) ;
return ( EFLAGS_IF & eflags) ? INTR_ON : INTR_OFF;
}
void register_handler ( uint8_t vector_no, intr_handler function) {
idt_table[ vector_no] = function;
}
“/home/lily/OS/boot/kernel/interrupt.h”
# ifndef __KERNEL_INTERRUPT_H
# define __KERNEL_INTERRUPT_H
# include "stdint.h"
typedef void * intr_handler;
enum intr_status {
INTR_OFF = 0 ,
INTR_ON = 1
} ;
void idt_init ( void ) ;
enum intr_status intr_enable ( void ) ;
enum intr_status intr_disable ( void ) ;
enum intr_status intr_set_status ( enum intr_status status) ;
enum intr_status intr_get_status ( void ) ;
void register_handler ( uint8_t vector_no, intr_handler function) ;
# endif
“/home/lily/OS/boot/lib/kernel/print.S”
TI_GDT equ 0
RPL0 equ 0
SELECTOR_VIDEO equ ( 0x0003 << 3 ) + TI_GDT + RPL0
section . data
put_int_buffer dq 0 ; 定义8 字节缓冲区用于数字到字符的转换
[ bits 32 ]
section . text
; -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
; put_str通过put_char来打印以0 字符结尾的字符串
; -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
; 输入:栈中参数为打印的字符串
; 输出:无
global put_str
put_str:
; 由于函数只用到了ebx和ecx两个寄存器,所以只备份这两个
push ebx
push ecx
xor ecx, ecx ; 准备用ecx存储参数,清空
mov ebx, [ esp+ 12 ] ; 从栈中得到待打印的字符串地址( 传入的参数)
. goon:
mov cl, [ ebx]
cmp cl, 0 ; 如果处理到了字符串尾,则跳到结束时返回
jz . str_over
push ecx ; 为put_char传递参数, 把ecx的值入栈
call put_char ; call时会把返回地址入栈4
add esp, 4 ; 回收参数的栈空间
inc ebx ; 使ebx指向下一个字符
jmp . goon
. str_over:
pop ecx
pop ebx
ret
; -- -- -- -- -- - put_char-- -- -- -- -- -- -- -
; 功能描述:把栈中的1 个字符写入光标所在处
; -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
global put_char ; 将put_char导出为全局符号
put_char:
pushad ; 备份32 位寄存器环境, push all double ,将8 个32 位寄存器都备份了。它们入栈的顺序为:EAX-> ECX-> EDX-> EBX-> ESP-> EBP-> ESI-> EDI
mov ax, SELECTOR_VIDEO ; 需要保证gs中为正确的视频段选择子,为保险起见,每次打印都为gs赋值
mov gs, ax ; 不能直接把立即数送入段寄存器
; ; ; ; ; 获取当前光标的位置; ; ; ; ;
; 先获取高8 位
mov dx, 0x03d4 ; 索引寄存器,03 d4为Address Register,用于索引寄存器。
mov al, 0x0e ; 用于提供光标位置的高8 位
out dx, al
mov dx, 0x03d5 ; 03 d5是Data Register;可以写数据和读数据。通过读写数据端口0x3d5 来获取/ 设置光标的位置
in al, dx ; 得到光标位置的高8 位
mov ah, al ; 将得到的光标高8 位放入ah中
; 再获取低8 位
mov dx, 0x03d4
mov al, 0x0f ; 用于提供光标位置的低8 位
out dx, al
mov dx, 0x03d5
in al, dx
; 将光标位置存入bx,bx寄存器习惯性作为基址寻址。此时bx是下一个字符的输出位置。
mov bx, ax
; 获取栈中压入字符的ASCII码
mov ecx, [ esp + 36 ] ; pushad压入8 * 32 b= 32 字节,加上主调函数4 B的返回地址。故栈顶偏移36 字节。
; 判断字符是什么类型
cmp cl, 0xd ; CR是0x0d ,回车键
jz . is_carriage_return
cmp cl, 0xa ; LF是0x0a ,换行符
jz . is_line_feed
cmp cl, 0x8 ; BS是0x08 ,退格键
jz . is_backspace
jmp . put_other
. is_backspace: ; 理论上将光标移到该字符前即可,但怕下个字符为回车等,原字符还留着当地,所以用空格/ 空字符0 替代原字符
dec bx ; bx值- 1 ,光标指向前一个字符
shl bx, 1 ; 左移一位等于乘2 ,表示光标对应显存中的偏移字节
mov byte [ gs: bx] , 0x20 ; 0x20 表示空格
inc bx ; bx+ 1
mov byte [ gs: bx] , 0x07 ; 0x07 表示黑屏白字,这是显卡默认的前景色和背景色,不加也行。
shr bx, 1 ; 右移一位表示除以2 取整,bx由显存的相对地址恢复到光标位置
jmp . set_cursor ; 设置光标位置
. put_other: ; 处理可见字符
shl bx, 1 ; 光标左移1 位等于乘2 ,表示光标位置
mov [ gs: bx] , cl ; 将ASCII字符放入光标位置中
inc bx ; bx+ 1
mov byte [ gs: bx] , 0x07 ; 字符属性,黑底白字
shr bx, 1 ; 右移一位表示除以2 取整,bx由显存的相对地址恢复到光标位置
inc bx ; bx+ 1 ,下一个光标值
cmp bx, 2000 ; 看是否需要滚屏
jl . set_cursor ; "JL" 是"jump if less" (如果小于则跳转):若光标值<= 2000 ,表示未写到。显存的最后,则去设置新的光标值,若超过屏幕字符数大小(2000 ),则换行(滚屏)。
. is_line_feed: ; 是换行符LF ( \n)
. is_carriage_return: ; 是回车键CR ( \r) , \n和\r在Linux中都是\n的意思。
xor dx, dx ; dx是被除数的高16 位,清零
mov ax, bx ; ax是被被除数的低16 位,bx是光标位置
mov si, 80 ; si = 80 为除数
div si ; 对80 取模,( dx + ax) / si = ax ( 商) + dx ( 余数) 即bx/ 80 = 几行( ax) + 第几列( dx)
; 如果除数是16 位,被除数就是32 位,位于dx和ax(高16 位,低16 位)中;结果的商放在ax中,余数放入dx中
sub bx, dx ; bx- dx表示将bx放在行首,实现了回车的功能。
. is_carriage_return_end: ; 回车符处理结束,判断是否需要滚屏
add bx, 80
cmp bx, 2000
. is_line_feed_end: ; 若是LF,则光标移+ 80 即可
jl . set_cursor
. roll_screen: ; 若超过屏幕大小,开始滚屏:屏幕范围是0 ~ 23 ,滚屏原理是把1 ~ 24 -> 0 ~ 23 ,再将24 行用空格填充
cld
mov ecx, 960 ; 2000 - 80 = 1920 个字符,共1920 * 2 = 3840 字节,一次搬运4 字节,一共要搬运3840 / 4 = 960 次
mov esi, 0xc00b _80a0 ; 第1 行行首,源索引地址寄存器
mov edi, 0xc00b _8000 ; 第0 行行首,目的索引地址寄存器
rep movsd ; repeat move string doubleword,以32 b为单位进行移动,直到ecx= 0
; 将最后一行填充为空白
mov ebx, 3840 ; 最后一行从3840 开始
mov ecx, 80 ; 一行80 字符,每次清空1 字符(2 B),一行要移动80 次
. cls:
mov word [ gs: ebx] , 0x0720 ; 0x0720 是黑底白字的空格键, 一次清空一个字符(2 B)
add ebx, 2 ; ebx移动到下一个字符处
loop . cls ; 循环. cls,直到ecx= 0
mov bx, 1920 ; bx存放下一个字符的光标位置,即3840 / 2 = 1920
. set_cursor: ; 将光标设置为bx值
; 先设置高8 位
mov dx, 0x03d4 ; 索引寄存器,通过0x3d4 写入待操作寄存器的索引
mov al, 0x0e ; 用于提供光标的高8 位
out dx, al
mov dx, 0x03d5 ; 通过数据端口0x3d5 来设置光标位置
mov al, bh ; 将bx的光标位置的高8 位放入al中,通过al输入到dx = 0x3d5 端口
out dx, al ; [ 0x3d5 端口] = bx高8 位 = bh
; 再设置低8 位
mov dx, 0x03d4
mov al, 0x0f ; 用于提供光标的低8 位
out dx, al
mov dx, 0x03d5 ; 通过数据端口0x3d5 来设置光标位置
mov al, bl ; 将bx的光标位置的低8 位放入al中,通过al输入到dx = 0x3d5 端口
out dx, al ; [ 0x3d5 端口] = bx低8 位 = bl
. put_char_done:
popad ; 将之前入栈的8 个32 b的寄存器出栈
ret
; -- -- -- -- -- -- 将小端字节序的数字变成对应的ASCII码后,倒置-- -- -- -- -- -- --
; 输入:栈中参数为待打印的数字
; 输出:在屏幕上打印16 进制数字,并不会打印前缀0 x
; -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
global put_int
put_int:
pushad
mov ebp, esp
mov eax, [ ebp+ 4 * 9 ] ; 将参数写入eax中,call返回地址占4 B+ pushad的8 个4 B
mov edx, eax ; eax存储的是参数的备份,edx为每次参与位变换的参数,当转换为16 进制数字后,eax将下一个参数给edx
mov edi, 7 ; 指定在put_int_buffer中初始的偏移量,表示指向缓冲区的最后一个字节
mov ecx, 8 ; 32 位数字中,每4 位表示一个16 进制数字。所以32 位可以表示8 个16 进制数字,位数为8 。
mov ebx, put_int_buffer ; ebx为缓冲区的基址
; 将32 位数字按照16 进制的形式从低到高逐个处理,共处理8 个16 进制数字
.16 based_4bits:
; 将32 位数字按照16 进制形式从低到高逐字处理
and edx, 0x0000000F ; 解析16 进制数字的每一位,and后edx只有低4 位有效(最低位的16 进制数字)
cmp edx, 9 ; 数字0 ~ 9 和a~ f需要分别处理成对应的字符
jg . is_A2F ; jg:Jump if Greater 若大于9 ,则跳转. is_A2F
add edx, '0' ; 如果是0 ~ 9 ,则加上'0' 的ASCII码
jmp . store
. is_A2F:
sub edx, 10 ; A~ F减去10 所得的差,10 的ASCII码为1
add edx, 'A' ; 加上10 的ASCII码得到字符的ASCII码
; 将每个数字转换成对应的字符后,按照类似大端的顺序存储到缓冲区put_int_buffer中。
; 高位字符放在低地址,低位字符放在高地址,这样和大端字符序类似。
. store:
; 此时dl中应该是对应数字的ASCII码
mov [ ebx+ edi] , dl
dec edi
shr eax, 4 ; 右移4 位,去掉最低4 位
mov edx, eax
loop .16 based_4bits
; 现在把put_int_buffer中已全是字符,打印之前把高位连续的字符去掉。
; 例如:000123 -> 123
. ready_to_print:
inc edi ; 此时edi为- 1 ( 0xffff _ffff) ,加1 使其为0
. skip_prefix_0:
cmp edi, 8 ; 若以及比较到第9 个字符,表示待打印的字符都是0
je . full0 ; Jump if Equal
; 找出连续的0 字符,edi作为非0 的最高位字符的偏移
. go_on_skip:
mov cl, [ put_int_buffer+ edi]
inc edi
cmp cl, '0' ; 判断下一位字符是否为0
je . skip_prefix_0
dec edi ; 若当前字符不为'0' ,则使edi减1 恢复当前字符
jmp . put_each_num ; 若下一位不为0 ,则从这一位开始遍历
. full0:
mov cl, '0' ; 当输入字符都是0 时,只打印0
. put_each_num:
push ecx ; 此时ecx中为可打印字符,作为参数传递入put_char中
call put_char
add esp, 4 ; 覆盖掉ecx,清理栈参数,相当于pop ecx
inc edi ; 使edi指向下个字符
mov cl, [ put_int_buffer+ edi] ; 将下个字符放入cl中
cmp edi, 8
jl . put_each_num
popad
ret
; 对应函数 void set_cursor ( uint32_t cursor_pos) ;
global set_cursor
set_cursor:
pushad
mov bx, [ esp + 36 ]
; 1. 先设置高8 位
mov dx, 0x03d4 ; 索引寄存器
mov al, 0x0e ; 光标高8 位
out dx, al
mov dx, 0x03d5 ; 通过读写数据端口0x3d5 来获得或设置光标位置
mov al, bh
out dx, al
; 2. 再设置低8 位
mov dx, 0x03d4
mov al, 0x0f
out dx, al
mov dx, 0x03d5
mov al, bl
out dx, al
popad
ret
“/home/lily/OS/boot/lib/kernel/print.h”
# ifndef __LIB_KERNEL_PRINT_H
# define __LIB_KERNEL_PRINT_H
# include "stdint.h"
void put_char ( uint8_t char_asci) ;
void put_str ( char * message) ;
void put_int ( uint32_t num) ;
void set_cursor ( uint32_t cursor_pos) ;
# endif
“/home/lily/OS/boot/device/timer.c”
# include "timer.h"
# include "io.h"
# include "print.h"
# include "thread.h"
# include "debug.h"
# include "interrupt.h"
# define IRQ0_FREQUENCY 100
# define INPUT_FREQUENCY 1193180
# define COUNTER0_VALUE INPUT_FREQUENCY/ IRQ0_FREQUENCY
# define COUNTER0_PORT 0x40
# define COUNTER0_NO 0
# define COUNTER_MODE 2
# define READ_WRITE_LATCH 3
# define PIT_CONTROL_PORT 0x43
uint32_t ticks;
static void frequency_set ( uint8_t coutner_port, uint8_t counter_no, uint8_t rw1, uint8_t counter_mode, uint16_t counter_value) {
outb ( PIT_CONTROL_PORT, ( uint8_t ) ( counter_no << 6 | rw1 << 4 | counter_mode << 1 ) ) ;
outb ( coutner_port, ( uint8_t ) counter_value) ;
outb ( coutner_port, ( uint8_t ) ( counter_value >> 8 ) ) ;
}
static void intr_timer_handler ( void ) {
struct task_struct * cur_thread = running_thread ( ) ;
ASSERT ( cur_thread-> stack_magic == 0x19870916 ) ;
cur_thread-> elapsed_ticks++ ;
ticks++ ;
if ( cur_thread-> ticks == 0 )
schedule ( ) ;
else
cur_thread-> ticks-- ;
}
void timer_init ( ) {
put_str ( "timer_init start\n" ) ;
frequency_set ( COUNTER0_PORT, COUNTER0_NO, READ_WRITE_LATCH, COUNTER_MODE, COUNTER0_VALUE) ;
register_handler ( 0x20 , intr_timer_handler) ;
put_str ( "timer_init done\n" ) ;
}
“/home/lily/OS/boot/device/timer.h”
# ifndef __DEVICE_TIMER_H
# define __DEVICE_TIMER_H
# include "stdint.h"
void timer_init ( void ) ;
# endif
“/home/lily/OS/boot/thread/thread.c”
# include "thread.h"
# include "stdint.h"
# include "string.h"
# include "global.h"
# include "memory.h"
# include "interrupt.h"
# include "list.h"
# include "debug.h"
# include "print.h"
# define PG_SIZE 4096
struct task_struct * main_thread;
struct list thread_ready_list;
struct list thread_all_list;
static struct list_elem * thread_tag;
extern void switch_to ( struct task_struct * cur, struct task_struct * next) ;
struct task_struct * running_thread ( void ) {
uint32_t esp;
asm ( "mov %%esp,%0" : "=g" ( esp) ) ;
return ( struct task_struct * ) ( esp & 0xfffff000 ) ;
}
static void kernel_thread ( thread_func* funcion, void * func_arg) {
intr_enable ( ) ;
funcion ( func_arg) ;
}
void thread_create ( struct task_struct * pthread, thread_func function, void * func_arg) {
pthread-> self_kstack -= sizeof ( struct intr_stack ) ;
pthread-> self_kstack -= sizeof ( struct thread_stack ) ;
struct thread_stack * kthread_stack = ( struct thread_stack * ) pthread-> self_kstack;
kthread_stack-> eip = kernel_thread;
kthread_stack-> function = function;
kthread_stack-> func_arg = func_arg;
kthread_stack-> ebp = kthread_stack-> ebx = kthread_stack-> edi = kthread_stack-> esi = 0 ;
}
void init_thread ( struct task_struct * pthread, char * name, int prio) {
memset ( pthread, 0 , sizeof ( * pthread) ) ;
strcpy ( pthread-> name, name) ;
if ( pthread == main_thread)
pthread-> status = TASK_RUNNING;
else
pthread-> status = TASK_READY;
pthread-> self_kstack = ( uint32_t * ) ( ( uint32_t ) pthread + PG_SIZE) ;
pthread-> priority = prio;
pthread-> ticks = prio;
pthread-> elapsed_ticks = 0 ;
pthread-> pgdir = NULL ;
pthread-> stack_magic = 0x19870916 ;
}
struct task_struct * thread_start ( char * name, int prio, thread_func function, void * func_arg) {
struct task_struct * thread = get_kernel_pages ( 1 ) ;
init_thread ( thread, name, prio) ;
thread_create ( thread, function, func_arg) ;
ASSERT ( ! elem_find ( & thread_ready_list, & thread-> general_tag) ) ;
list_append ( & thread_ready_list, & thread-> general_tag) ;
ASSERT ( ! elem_find ( & thread_all_list, & thread-> all_list_tag) ) ;
list_append ( & thread_all_list, & thread-> all_list_tag) ;
return thread;
}
static void make_main_thread ( void ) {
main_thread = running_thread ( ) ;
init_thread ( main_thread, "main" , 31 ) ;
ASSERT ( ! elem_find ( & thread_all_list, & main_thread-> all_list_tag) ) ;
list_append ( & thread_all_list, & main_thread-> all_list_tag) ;
}
void schedule ( ) {
ASSERT ( intr_get_status ( ) == INTR_OFF) ;
struct task_struct * cur = running_thread ( ) ;
if ( cur-> status == TASK_RUNNING) {
ASSERT ( ! elem_find ( & thread_ready_list, & cur-> general_tag) ) ;
list_append ( & thread_ready_list, & cur-> general_tag) ;
cur-> ticks = cur-> priority;
cur-> status = TASK_READY;
} else {
}
ASSERT ( ! list_empty ( & thread_ready_list) ) ;
thread_tag = NULL ;
thread_tag = list_pop ( & thread_ready_list) ;
struct task_struct * next = elem2entry ( struct task_struct , general_tag, thread_tag) ;
next-> status = TASK_RUNNING;
switch_to ( cur, next) ;
}
void thread_init ( void ) {
put_str ( "thread_init start\n" ) ;
list_init ( & thread_ready_list) ;
list_init ( & thread_all_list) ;
make_main_thread ( ) ;
put_str ( "thread_init done\n" ) ;
}
“/home/lily/OS/boot/thread/thread.h”
# ifndef __THREAD_THREAD_H
# define __THREAD_THREAD_H
# include "stdint.h"
# include "list.h"
typedef void thread_func ( void * ) ;
enum task_status {
TASK_RUNNING,
TASK_READY,
TASK_BLOCKED,
TASK_WAITING,
TASK_HANGING,
TASK_DIED
} ;
struct intr_stack {
uint32_t vec_no;
uint32_t edi;
uint32_t esi;
uint32_t ebp;
uint32_t esp_dummy;
uint32_t ebx;
uint32_t edx;
uint32_t ecx;
uint32_t eax;
uint32_t gs;
uint32_t fs;
uint32_t es;
uint32_t ds;
uint32_t err_code;
void ( * eip) ( void ) ;
uint32_t cs;
uint32_t eflags;
void * esp;
uint32_t ss;
} ;
struct thread_stack {
uint32_t ebp;
uint32_t ebx;
uint32_t edi;
uint32_t esi;
void ( * eip) ( thread_func* func, void * func_arg) ;
void ( * unused_retaddr) ;
thread_func* function;
void * func_arg;
} ;
struct task_struct {
uint32_t * self_kstack;
enum task_status status;
uint8_t priority;
char name[ 16 ] ;
uint8_t ticks;
uint32_t elapsed_ticks;
struct list_elem general_tag;
struct list_elem all_list_tag;
uint32_t * pgdir;
uint32_t stack_magic;
} ;
struct task_struct * running_thread ( void ) ;
void thread_create ( struct task_struct * pthread, thread_func function, void * func_arg) ;
void init_thread ( struct task_struct * pthread, char * name, int prio) ;
struct task_struct * thread_start ( char * name, int prio, thread_func function, void * func_arg) ;
void schedule ( void ) ;
void thread_init ( void ) ;
# endif
“/home/lily/OS/boot/thread/switch.S”
[ bits 32 ]
section . text
global switch_to
switch_to:
; 栈中此时是返回地址
push esi
push edi
push ebx
push ebp
mov eax, [ esp + 20 ] ; 得到栈中的参数cur,cur= [ esp+ 20 ]
mov [ eax] , esp ; 保存栈顶指针esp, task_struct的self_kstack字段,self_kstack在task_struct中的偏移量为0
; -- -- -- -- -- -- - 以上是备份当前线程的环境,下面是恢复下一个线程的环境 -- -- -- -- -- -- -- -- -- -- -- -
mov eax, [ esp + 24 ] ; 得到栈中参数next, next = [ esp + 24 ]
mov esp, [ eax] ; pcb的第一个成员是self_kstack成员,它用来记录0 级栈,0 级栈中保存了进程/ 线程所有的信息,包括3 级指针
pop ebp
pop ebx
pop edi
pop esi
ret ; 返回到上面switch_to下面的那句注释的返回地址;如果未由中断进入,第一次执行时会返回kernel_thread
“/home/lily/OS/boot/kernel/init.c”
# include "init.h"
# include "print.h"
# include "interrupt.h"
# include "../device/timer.h"
# include "memory.h"
# include "../thread/thread.h"
void init_all ( ) {
put_str ( "init_all\n" ) ;
idt_init ( ) ;
mem_init ( ) ;
thread_init ( ) ;
timer_init ( ) ;
}
“/home/lily/OS/boot/kernel/main.c”
# include "print.h"
# include "init.h"
# include "thread.h"
# include "interrupt.h"
void k_thread_a ( void * ) ;
void k_thread_b ( void * ) ;
int main ( void ) {
put_str ( "I am kernel\n" ) ;
init_all ( ) ;
thread_start ( "k_thread_a" , 31 , k_thread_a, "argA " ) ;
thread_start ( "k_thread_b" , 8 , k_thread_b, "argB " ) ;
intr_enable ( ) ;
while ( 1 ) {
put_str ( "Main " ) ;
}
return 0 ;
}
void k_thread_a ( void * arg) {
char * para = arg;
while ( 1 ) {
put_str ( para) ;
}
}
void k_thread_b ( void * arg) {
char * para = arg;
while ( 1 ) {
put_str ( para) ;
}
}
“/home/lily/OS/boot/makefile”
BUILD_DIR = . / build
##用来存储生成的所有目标文件
ENTRY_POINT = 0xc0001500
AS = nasm
CC = gcc
LD = ld
LIB = - I lib/ - I lib/ kernel/ - I lib/ user/ - I kernel/ - I device/ - I thread/
ASFLAGS = - f elf
CFLAGS = - Wall - m32 - fno- stack- protector $( LIB) - c - fno- builtin - W - Wstrict- prototypes - Wmissing- prototypes
##- fno- builtin是告诉编译器不要采用内部函数 - Wstrict- prototypes是要求函数声明中必须有参数类型
## - Wmissing- prototypes要求函数必须有声明
LDFLAGS = - m elf_i386 - Ttext $( ENTRY_POINT) - e main - Map $( BUILD_DIR) / kernel. map
OBJS = $( BUILD_DIR) / main. o $( BUILD_DIR) / init. o $( BUILD_DIR) / interrupt. o \
$( BUILD_DIR) / timer. o $( BUILD_DIR) / kernel. o $( BUILD_DIR) / print. o $( BUILD_DIR) / debug. o \
$( BUILD_DIR) / string. o $( BUILD_DIR) / memory. o $( BUILD_DIR) / bitmap. o $( BUILD_DIR) / thread. o \
$( BUILD_DIR) / switch . o $( BUILD_DIR) / list. o
## OBJS用来存储所有目标文件名,不要用% . o,因为不能保证链接顺序
########## c代码编译 ##########
$( BUILD_DIR) / main. o: kernel/ main. c lib/ kernel/ print. h lib/ kernel/ stdint. h kernel/ init. h kernel/ memory. h \
thread/ thread. h kernel/ interrupt. h
$( CC) $( CFLAGS) $< - o $@
$( BUILD_DIR) / init. o: kernel/ init. c kernel/ init. h lib/ kernel/ print. h lib/ kernel/ stdint. h \
kernel/ interrupt. h device/ timer. h kernel/ memory. h thread/ thread. h
$( CC) $( CFLAGS) $< - o $@
$( BUILD_DIR) / interrupt. o: kernel/ interrupt. c kernel/ interrupt. h lib/ kernel/ stdint. h \
kernel/ global. h lib/ kernel/ io. h lib/ kernel/ print. h
$( CC) $( CFLAGS) $< - o $@
$( BUILD_DIR) / timer. o: device/ timer. c device/ timer. h lib/ kernel/ stdint. h lib/ kernel/ io. h lib/ kernel/ print. h thread/ thread. c \
kernel/ debug. h kernel/ interrupt. h
$( CC) $( CFLAGS) $< - o $@
$( BUILD_DIR) / thread. o: thread/ thread. c thread/ thread. h lib/ kernel/ stdint. h lib/ string. h kernel/ global. h kernel/ memory. h \
kernel/ interrupt. h lib/ kernel/ list. h kernel/ debug. h lib/ kernel/ print. h
$( CC) $( CFLAGS) $< - o $@
$( BUILD_DIR) / debug. o: kernel/ debug. c kernel/ debug. h lib/ kernel/ print. h lib/ kernel/ stdint. h kernel/ interrupt. h
$( CC) $( CFLAGS) $< - o $@
$( BUILD_DIR) / string. o: lib/ string. c lib/ string. h kernel/ debug. h kernel/ global. h lib/ kernel/ stdint. h
$( CC) $( CFLAGS) $< - o $@
$( BUILD_DIR) / memory. o: kernel/ memory. c kernel/ memory. h lib/ kernel/ stdint. h lib/ kernel/ bitmap. h kernel/ debug. h lib/ string. h \
lib/ kernel/ print. h kernel/ global. h
$( CC) $( CFLAGS) $< - o $@
$( BUILD_DIR) / bitmap. o: lib/ kernel/ bitmap. c lib/ kernel/ bitmap. h lib/ string. h kernel/ interrupt. h lib/ kernel/ print. h \
kernel/ debug. h lib/ kernel/ stdint. h
$( CC) $( CFLAGS) $< - o $@
$( BUILD_DIR) / list. o: lib/ kernel/ list. c lib/ kernel/ list. h \
kernel/ interrupt. h lib/ kernel/ stdint. h kernel/ debug. h
$( CC) $( CFLAGS) $< - o $@
###########汇编代码编译############
$( BUILD_DIR) / kernel. o: kernel/ kernel. S
$( AS) $( ASFLAGS) $< - o $@
$( BUILD_DIR) / print. o: lib/ kernel/ print. S
$( AS) $( ASFLAGS) $< - o $@
$( BUILD_DIR) / switch . o: thread/ switch . S
$( AS) $( ASFLAGS) $< - o $@
##########链接所有目标文件#############
$( BUILD_DIR) / kernel. bin: $( OBJS)
$( LD) $( LDFLAGS) $^ - o $@
. PHONY: mk_dir hd clean all
mk_dir:
if [ ! - d $( BUILD_DIR) ] ; then mkdir $( BUILD_DIR) ; fi
###fi为终止符
hd:
dd if = $( BUILD_DIR) / kernel. bin of= / home/ lily/ bochs/ hd60M. img bs= 512 count= 200 seek= 9 conv= notrunc
clean: ##将build目录下文件清空
cd $( BUILD_DIR) && rm - f .