Threads and Processes
Kernel threading and user process management is a key part of driver development
Kernel Threads
Kernel threads are small processes in the kernel that are able to block/signal/etc.
They are extremely useful for poller processes or cache flushing processes.
Creating kernel threads in Ethereal is extremely easy, only two lines of code are required.
The interface to make a kernel thread is known as process_createKernel
:
process_t *process_createKernel(char *name, unsigned int flags, unsigned int priority, kthread_t entrypoint, void *data);
kthread_t
is declared as:
typedef void (*kthread_t)(void *data);
After you create a kernel thread, insert it by doing:
scheduler_insertThread(kthread->main_thread);
Your thread is now in the scheduler and you can assume it is already running.
Current core data
To access this interface, include kernel/processor_data.h
Ethereal provides interfaces to get the data on the CPU being run on (and subsequently current process/thread) via the current_cpu
macro.
This macro is implementation specific and nothing may be assumed of it
Here is the processor_t
structure that it contains:
typedef struct _processor {
int cpu_id; // CPU ID
page_t *current_dir; // Current page directory
struct thread *current_thread; // Current thread of the process
struct process *current_process; // Current process of the CPU
struct process *idle_process; // Idle process of the CPU
#if defined(__ARCH_X86_64__) || defined(__ARCH_I386__)
#ifdef __ARCH_X86_64__
uintptr_t kstack; // (0x40) Kernel-mode stack loaded in TSS
uintptr_t ustack; // (0x48) Usermode stack, saved in SYSCALL entrypoint
#endif
int lapic_id;
/* CPU basic information */
char cpu_model[48];
const char *cpu_manufacturer;
int cpu_model_number;
int cpu_family;
#endif
scheduler_cpu_t sched; // Scheduler data
uint64_t idle_time; // Time the processor has spent idling
} processor_t;
Unless you wrap your code in #ifdef
s, anything marked as x86_64 or i386 specific is off limits.
Useful variables:
cpu_id
: Current CPU IDcurrent_dir
: Page directorycurrent_process
: Currently running processcurrent_thread
: Currently running thread
Sleep API
Sleeping and blocking threads is well supported in Ethereal.
Overview
Ethereal's sleep system operates on a basis of prepare, notify, enter.
- Prepare yourself for sleeping with a
sleep_untilXXX
or other function - Notify anyone else that you are sleeping and to wake you up (this is up to you)
- Enter sleep state.
Here's an example that sleeps for a second:
sleep_untilTime(current_cpu->current_thread, 1, 0);
int w = sleep_enter();
sleep_enter
will enter you into the sleep state.
It returns one of a few wakeup reasons:
WAKEUP_ANOTHER_THREAD
: Another thread usedsleep_wakeup
to wake you upWAKEUP_TIME
: You wokeup on time expirationWAKEUP_SIGNAL
: You were signalledWAKEUP_COND
: Condition woke you up
Waiting for another thread to wake you up
You can use the sleep_untilNever
call for this:
int sleep_untilNever(thread_t *thr);
Simply have the other thread use sleep_wakeup
to wake you up:
int sleep_wakeup(thread_t *thread);
Wakeups from this will return WAKEUP_ANOTHER_THREAD
(or another reason)
Waiting for a duration
You can use sleep_untilTime
for this:
int sleep_untilTime(struct thread *thread, unsigned long seconds, unsigned long subseconds);
Wakeups from this will return WAKEUP_TIME
if the time expires (or another reason)
Waiting in a queue
First, you'll need to create the queue using sleep_createQueue
sleep_queue_t *sleep_createQueue(char *name);
Then to sleep in it:
int sleep_inQueue(sleep_queue_t *queue);
Others can wakeup you up via:
int sleep_wakeupQueue(sleep_queue_t *queue, int amounts);
Use -1 for all threads in the queue.
Wakeups from this will return WAKEUP_ANOTHER_THREAD
(or another reason)
Waiting for a condition
This interface may not be well supported
Use sleep_untilCondition
:
int sleep_untilCondition(struct thread *thread, sleep_condition_t condition, void *context);
The condition function simply takes context as an argument and returns 1 when the condition is met.
Wakeups from this will return WAKEUP_COND
(or another reason)
Synchronization
Spinlocks and mutexes are fun!
Spinlocks
Make a spinlock via spinlock_create
or declare it as an empty structure:
spinlock_t *spinlock_create(char *name);
Interfaces (you should be well familiar):
void spinlock_acquire(spinlock_t *spinlock);
int spinlock_tryAcquire(spinlock_t *spinlock);
void spinlock_release(spinlock_t *spinlock);
void spinlock_destroy(spinlock_t *spinlock);
IRQs are disabled during acquisition and restored after release
Mutexes
Make a mutex via mutex_create
(you cannot declare it as an empty structure):
mutex_t *mutex_create(char *name);
Interfaces (you should be well familiar):
void mutex_acquire(mutex_t *mutex);
int mutex_tryAcquire(mutex_t *mutex);
void mutex_release(mutex_t *mutex);
void mutex_destroy(mutex_t *mutex);