⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 multi.c

📁 nucleus_arm.rar
💻 C
字号:
// multi.c

// v0.1: Doug Gale
//	- Initial revision.
//	- Rewrite from earlier implementation
// v0.2: Doug Gale
//	- Added critical section reference count to handle disabling of interrupts

#include <datatypes.h>
#include <support.h>
#include <interrupts.h>
#include <string.h>
#include <stdio.h>
#include <multi.h>
#include <pagetbl.h>
#include <drivers/timer.h>
#include <drivers/mem/mem.h>

#define MULTI_DEBUG		defined(DEBUG)

#define THREADS_MAX	200		// should be enough

// This is the "save area" on the stack created by a thread switch
typedef struct {
	// Page directory base register
	unsigned r_cr3;

	// General registers
	unsigned r_eax, r_ebx, r_ecx, r_edx, r_esi, r_edi, r_ebp;

	// Segment registers
	unsigned r_ds, r_es, r_fs, r_gs;

	// FPU/MMX context
	unsigned char r_fpu[108];

	// The following are pushed by the CPU
	unsigned r_eip, r_cs, r_eflags;
} ThreadContext;

// This is the structure created on the stack when creating a thread
typedef struct {
	ThreadContext context;

	// The return address, initialized to thread_exit()
	unsigned tc_ret;

	// Argument passed to thread function
	unsigned tc_arg;
} ThreadCreate;

// =========================================================================
// Thread information
// =========================================================================

typedef enum {
	THST_UNUSED,		// This thread structure is not currently in use
	THST_EXITING,		// Needs stack deallocated
	THST_SUSPENDED,		// The thread is suspended
	THST_ACTIVE,		// Running or ready to run
	THST_MAX			// For bounds-checking enumeration
} ThreadState;

// Thread info block
typedef struct tagThreadInfo {
	// Linked list (circular)
	struct tagThreadInfo *next, *prev;	// Circular linked list pointers

	// Stack bounds
	void *stack_lo, *stack_hi;			

	// esp register to resume
	unsigned r_esp;

	// Current state of the thread
	ThreadState state;					
} ThreadInfo;

// Used to implement other circular lists of ThreadInfo structures
typedef struct {
	ThreadInfo *next, *prev;
} ThreadChain;

// Free list
ThreadChain free_threads;

// Thread identifiers are an index into the following array
// Zero is the main kernel thread.
// Active threads are linked to this thread.
// The kernel thread always has work to do:
//  - Clearing unused pages
//  - Checking page accessed and modified flags (virtual memory)
// It is expected to finish its work early and yield
static unsigned cur_thread_id;
static ThreadInfo thread_table[THREADS_MAX];

// =========================================================================
// Protection for thread chains. Manages interrupt flag.
// =========================================================================

static volatile unsigned critical_count, critical_enab_ints;

// Enter critical section of code.
// Disables interrupts and updates reference count.
// Note that for each call of multi_critical_enter you 
// must call multi_critical_exit.
static void multi_critical_enter()
{
	int ints_were_enabled = interrupts_disable();

	// Remember interrupt-enable flag state if this is the first "entry"
	if (critical_count++ == 0)
		critical_enab_ints = ints_were_enabled;
}

// Expects interrupts disabled on entry
static void multi_critical_exit()
{
	if (!critical_count)
		panic();

	if (--critical_count == 0) {
		if (critical_enab_ints)
			interrupts_enable();
	}
}

// =========================================================================
// Helpers
// =========================================================================

static ThreadInfo *thread_destroy(ThreadInfo *thread)
{
	ThreadInfo *next;

	next = thread->next;

	free(thread->stack_lo);
	thread->stack_lo = 0;
	thread->stack_hi = 0;
	thread->r_esp = 0;
	thread->state = THST_UNUSED;

	// Unlink thread from current chain
	thread->next->prev = thread->prev;
	thread->prev->next = thread->next;

	// Link into end of free chain
	thread->next = (void*)&free_threads;
	thread->prev = free_threads.prev;
	thread->next->prev = thread;
	thread->prev->next = thread;

	return next;
}

// =========================================================================
// Saves stack for current thread and selects next running thread.
// =========================================================================

// Returns esp value for new stack
// Called by IRQ handler
unsigned multi_scheduler(unsigned old_esp)
{
	ThreadInfo *cur_thread;
	int found;

	// Initialized?
	if (thread_table[0].next == 0)
		return old_esp;

	// Calculate pointer to current thread
	cur_thread = thread_table + cur_thread_id;

	// Store last esp in the current thread's ThreadInfo
	cur_thread->r_esp = old_esp;

	// Step to the next active thread
	cur_thread = cur_thread->next;

	// Search for a thread that is ready to run
	// Clean up exited threads when encountered
	found = 0;
	do {
		// Should I destroy this thread?
		switch (cur_thread->state) {
		case THST_EXITING:
			cur_thread = thread_destroy(cur_thread);
			break;
		case THST_ACTIVE:
			found = 1;
			break;
		case THST_SUSPENDED:
			cur_thread = cur_thread->next;
			break;
		default:
			panic();
		}
	} while (!found);

	// Update current thread id
	cur_thread_id = cur_thread - thread_table;

#if MULTI_DEBUG
	// DEBUG:
	// Make sure that the pointer is possible
	if (cur_thread < thread_table || 
			cur_thread >= thread_table + THREADS_MAX)
		panic();

	// DEBUG:
	// Make sure the pointer is aligned properly
	if (cur_thread != thread_table + cur_thread_id)
		panic();

	// DEBUG:
	// Validate stack pointer
	if (cur_thread->r_esp <= (unsigned)cur_thread->stack_lo ||
			cur_thread->r_esp > (unsigned)cur_thread->stack_hi) {
		printf("\nBad stack pointer, switching to thread %d!\n"
				"Old ESP: %08x, New ESP: %08x\n", 
				cur_thread_id, old_esp, cur_thread->r_esp);
		panic();
	}
#endif

	// Return esp for new thread
	return cur_thread->r_esp;
}

#if MULTI_DEBUG
static int multi_tests(void);
#endif

// =========================================================================
// Initialize
// =========================================================================

void multi_init(void)
{
	int i;
	ThreadInfo *p;

	multi_critical_enter();

	// Initialize circular list
	thread_table->next = thread_table;
	thread_table->prev = thread_table;

	// Hard-coded kernel stack (256KB)
	thread_table->stack_hi = (void*)0x00090000;
	thread_table->stack_lo = (void*)0x00050000;

	// Active
	thread_table->state = THST_ACTIVE;

	// Link the remaining structures to the free list
	free_threads.next = (void*)&free_threads;
	free_threads.prev = (void*)&free_threads;

	for (i = 1, p = thread_table + 1; i < THREADS_MAX; i++, p++) {
		// Fill link pointers in this element
		p->prev = (void*)free_threads.prev;
		p->next = (void*)&free_threads;
		// Link into chain
		p->prev->next = p;
		p->next->prev = p;

		p->state = THST_UNUSED;
	}

	multi_critical_exit();

	// Force a thread switch, to get kernel thread info initialized
	// It "switches" back to the same thread, so this function
	// returns immediately.
	// Not strictly necessary, but might be in the future
	multi_yield();

#if MULTI_DEBUG
	// Run debug tests
	multi_tests();
#endif
}

#if MULTI_DEBUG
unsigned delme_test_a;
static int multi_tests(void)
{
	int x, y;

	atomic_inc(&delme_test_a);
	atomic_dec(&delme_test_a);
	atomic_add(&delme_test_a,1);
	atomic_add(&delme_test_a,-1);
	x = atomic_xchg(&delme_test_a,2);
	y = atomic_xchg(&delme_test_a,0);

	printf("a: %08x\nx: %08x\ny: %08x\n", delme_test_a, x, y);

	return 0;
}
#endif		// MULTI_DEBUG

void multi_show(void)
{
	// Print summary of active threads
}

// =========================================================================
// Create a thread
// =========================================================================

// Create a new thread
int thread_create(unsigned stacksize, unsigned priority, 
		void (*execute)(), unsigned thread_arg)
{
	ThreadInfo *p;
	void *stack;
	ThreadCreate *context;
	unsigned stackbytes;

	// Enforce minimum stack size (64KB)
	if (stacksize < 64)
		stacksize = 64;

	// Calculate size of stack in bytes
	stackbytes = stacksize << 10;

	// Allocate stack
	stack = malloc(stackbytes);
	if (!stack)
		return -1;	// fail: out of memory

	// Protect list from corruption by thread switch
	multi_critical_enter();

#ifdef DEBUG
	if (get_eflags() & 0x200)
		panic();
#endif

	// Grab a free thread
	p = free_threads.next;
	if (p == (void*)&free_threads) {
		free(stack);
		multi_critical_exit();
		return -2;	// fail: too many threads
	}

	// Remove from the free list
	p->prev->next = p->next;
	p->next->prev = p->prev;

	// Add to the active list
	p->prev = thread_table[0].prev;
	p->next = thread_table;
	p->prev->next = p;
	p->next->prev = p;

	// Stack bounds
	p->stack_lo = stack;
	p->stack_hi = stack + stackbytes;

	// Calculate initial stack pointer
	context = stack + stackbytes - sizeof(ThreadCreate);

	// Prepare stack
	memset(stack, 0, stackbytes);
	context->context.r_eflags = EFL_DEFAULT;
	context->context.r_eip = (unsigned)execute;
	context->context.r_cs = 0x0008;
	context->context.r_ds = 0x0010;
	context->context.r_es = 0x0010;
	context->context.r_fs = 0x0010;
	context->context.r_gs = 0x0010;

	context->context.r_ebp = 0xFFFFFFFF;	// Terminates stack trace

	context->context.r_cr3 = (unsigned)page_dir;

	// Make it return to thread_exit
	context->tc_ret = (unsigned)thread_exit;

	// Pass specified parameter to thread
	context->tc_arg = thread_arg;

	// Point the initial esp to the beginning of the context
	p->r_esp = (unsigned)context;
	p->state = THST_ACTIVE;

	// Done manipulating thread chains
	multi_critical_exit();

	// Return index into thread_table
	return p - thread_table;
}

// Exit the current thread and put it on the free list
void thread_exit(void)
{
	// The next time this thread is going to be scheduled, remove it
	// This is done because I cannot free the current stack
	thread_table[cur_thread_id].state = THST_EXITING;

	multi_yield();
}

// =========================================================================
// Synchronization primitives
// =========================================================================

// TODO: Spin lock

// TODO: Sleep lock

// TODO: Mutex

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -