A Tour of thread.c in OS/161
Introduction
The file kern/thread/thread.c is a crucial component of OS/161, implementing the core thread-management functionality. This tour will give you an overview of its key features and functions, helping you understand how thread management works in the operating system.
Purpose and Overview
thread.c is responsible for
- Thread creation, destruction, and lifecycle management
- CPU and thread state management
- Context switching and scheduling
- Thread migration for load balancing
- Integration with synchronization primitives
Key Data Structures
Thread Structure
The struct thread is defined in kern/include/thread.h. It contains essential information about a thread, including
- Thread identification (name, ID)
- Current state (running, ready, sleeping, etc.)
- CPU assignment
- Execution context and stack
- Pointers for list management (to use
struct threadas a linked-list node) - Process association
- Interrupt and synchronization state
For a complete view of the structure, refer to the thread.h header file.
CPU Structure
The struct cpu is defined in kern/include/cpu.h. It tracks information relevant to each CPU in the system, such as
- Current running thread
- Run queue of ready threads
- List of zombie threads
- Interrupt and synchronization state
- Statistics and management information
For full details, check the cpu.h header file.
Current Thread and CPU
OS/161 uses the pointers curthread and curcpu to refer to the currently executing thread and the CPU it's running on, respectively. These are defined in kern/include/current.h and kern/arch/mips/include/current.h.
The MIPS-specific current.h file defines curthread as
register struct thread *curthread __asm("$23"); /* s7 register */
So while curthread looks like the name of a global variable, stored in memory, it's actually kept in a processor register (s7 in this case).
Is that a performance optimization?
No, it's actually critical. All the CPU cores share the same memory, but each core has its own registers. We want each core to have its own value for
curthread, so it needs to be in a register.
Needs is a little strong. Each CPU core already has its own stack, so it would be technically possible to work out which thread we are based on that, but it would be annoying. Using a processor register is easier, even though it means that the CPU has one fewer register to use for other things as
s7is reserved all the time forcurthread.
The architecture-specific code for MIPS also defines __NEED_CURCPU, which causes the generic current.h to define curcpu in terms of curthread using a macro as
#define curcpu curthread->t_cpu
That header also defines curproc as curthread->t_proc, which is the process that the current thread is part of. Currently, process functionality is not really implemented in OS/161, but we can see some intentions as to how it's expected to be implemented.
In code, we use curthread and curcpu to access the current thread and CPU as if they were magically thread-specific global variables, but there's more going on under the hood.
Thread States and Lifecycle
Threads in OS/161 can be in one of the following states (defined in kern/include/threadlist.h):
| State | Description |
|---|---|
S_RUN |
Currently running on a CPU |
S_READY |
Ready to run, waiting in the run queue |
S_SLEEP |
Blocked, waiting on a wait channel |
S_ZOMBIE |
Exited, waiting to be cleaned up |
The lifecycle of a thread involves transitions between these states, managed by functions in thread.c.
Key Functions
Thread Creation and Destruction
thread_create: Creates a new thread structurethread_fork: Creates a new thread and sets it up to run a specified functionthread_destroy: Cleans up and frees a thread structure
Thread Scheduling and Context Switching
thread_switch: Performs a context switch, moving the current thread to a new state and selecting a new thread to runthread_make_runnable: Called fromthread_switch,thread_fork, and wait channel functions to make a thread runnable by adding it to the run queuethread_yield: Voluntarily gives up the CPU, moving the current thread to the ready statethread_consider_migration: Checks if threads should be migrated between CPUs for load balancing
CPU Management
cpu_create: Initializes a new CPU structurecpu_idle: Called when a CPU has no threads to runthread_bootstrap: Sets up the initial thread and CPU structures during system boot
Synchronization and Wait Channels
thread.c uses wait channels for thread blocking and waking. For details on wait channels, see the wait channel documentation.
Key functions related to wait channels:
wchan_sleep: Puts a thread to sleep on a wait channelwchan_wakeone: Wakes up one thread from a wait channelwchan_wakeall: Wakes up all threads from a wait channel
Spinlocks
Spinlocks are used for low-level synchronization in thread.c. For more information, refer to the spinlock documentation.
Important uses of spinlocks in thread.c:
- Protecting the run queue during thread scheduling
- Synchronizing access to CPU-specific data
- Managing inter-processor interrupts (IPIs)
Thread Migration and Load Balancing
The thread_consider_migration function implements a simple load balancing strategy:
- It checks if the current CPU is busier than others
- If so, it moves threads to less busy CPUs
- This helps distribute the workload across available processors
Interaction with Process Management
Threads in OS/161 belong to processes. The thread structure includes a pointer to its parent proc structure (t_proc). The thread_fork function can create threads within the same process or as part of a new process.
Currently, process functionality (declared in kern/include/proc.h and implemented in kern/proc/proc.c) is incomplete in OS/161. Special “in-kernel” processes are sufficiently supported, but many features of user processes are not yet implemented. There is one special kernel process for each CPU.
Important Concepts to Understand
- Thread States: Understand how threads move between different states and what causes these transitions.
- Context Switching: Grasp the basic idea of how the OS switches between threads, especially in
thread_switch. - Synchronization: Notice how
thread.cuses wait channels and spinlocks to manage thread synchronization and protect shared data. - CPU Management: Understand how the OS manages multiple CPUs and distributes threads among them.
- Scalability Considerations: Think about how the thread management system might behave with many threads and CPUs.
- Current Thread and CPU: Understand the significance of
curthreadandcurcpuin the kernel code.
Conclusion
thread.c is a central part of OS/161, implementing core threading functionality. While you may not need to modify this file directly, understanding its structure and functionality is crucial for working on other parts of the OS, especially when implementing synchronization primitives or modifying the scheduler.
As you work on your projects, refer back to this file and the associated header files to understand how your code interacts with the thread management system. Remember that the abstractions provided by thread.c (like wait channels) are tools you'll use in implementing higher-level synchronization primitives.
(When logged in, completion status appears here.)