type
status
date
slug
summary
tags
category
icon
password
Question
Each thread exclusively occupies one CPU, and when the number of threads exceeds the number of CPUs, there will be situations where multiple threads share a single CPU. However, a CPU can only run one thread at a time. Therefore, Windows will perform a thread switch every 20 milliseconds. Suppose Thread A is currently executing at address
0x12345678
, with eax=1
, ebx=2
, ecx=3
, edx=4
, etc. When the execution time for Thread A is reached, Windows will hand over CPU control to Thread B. When Thread B's time is up, Windows will return CPU control to Thread A. So, how does the system know from which address to resume execution for Thread A? How does the system restore the values of the registers to their original state for Thread A? Context Switching
The operating system knows from which address to resume execution for Thread A, and how to restore the values of the registers to their original state, by using a process known as context switching.
Here's how it works:
- Save the Context: When it's time to switch from Thread A to Thread B, the operating system performs a context save. This means it saves all of the current execution context of Thread A, including the program counter (EIP, which points to the next instruction to execute), the CPU registers like
eax
,ebx
,ecx
,edx
, and any other state that's relevant to the execution of the thread. This information is saved to a data structure known as a context or process control block (PCB).
- Switch Threads: The OS then loads the context for Thread B, restoring its program counter, registers, and other execution state from where it was when Thread B last ran. Thread B now executes.
- Restore the Context: When it's time to switch back to Thread A, the OS again performs a context save, this time for Thread B, and then restores the context for Thread A from its PCB. This includes setting the program counter back to 0x12345678, and restoring the values of eax, ebx, ecx, edx, etc., to their original state.
Context Structure
Win32 uses the
__Context
structure to store the processor-specific register data that the CPU needs to temporarily save during thread switching. Win32 also provides APIs that allows us to obtain the context of a thread that has been temporarily suspended. Obtaining the context structure of a thread that is in a running state is meaningless, as the values within that structure will continuously change with the execution of the thread.
Kernel Objects
Kernel objects are data structures in kernel-mode memory that represent system resources, such as files, processes, threads, events, semaphores, and more. These objects can be indirectly accessed and manipulated by developers using windows APIs.
Each process in user mode has its own private virtual address space. This means one process cannot access the memory of another process. On the other hand, kernel objects exist in a shared space that any process can access. Therefore, kernel objects are useful for inter-process communication and system-wide synchronization.
reference counter
Kernel objects are reference-counted. This means that the system maintains a count of the number of references (or handles) to each object.
The system can safely deallocate or clean up a kernel object once its reference count drops to zero. This means no references to the object exist anymore, ensuring the object's memory can be safely reclaimed without risking dangling references. For example, see the difference between the two code examples below:
kernel object states
Kernel objects in the Windows operating system often have associated "signal states" that determine the readiness or status of the object. The signal state essentially tells you whether or not a certain condition related to the object has been met. Different types of kernel objects have different conditions that cause them to become signaled or non-signaled.
- Signaled State:
- When a kernel object is in the signaled state, it indicates that the object is available or that a particular condition has been met.
- For instance, if a mutex (a type of synchronization kernel object) is signaled, it means no other thread owns it and it can be acquired.
- Non-signaled State:
- When in the non-signaled state, the object is not available or a specific condition has not been met.
- Taking the mutex example again, if a mutex is non-signaled, it means it's currently owned by a thread, and other threads will block if they try to acquire it.
WaitForSingleObject
WaitForSingleObject
and WaitForMultipleObjects
are synchronization functions provided by the Windows API. These functions allow a thread to block its own execution until the kernel objects it waits for are in the signaled state.Thread Mutual Exclusion
Imagine a library with multiple study rooms. Each study room has a table with several chairs around it. One of the study rooms is designated as a "Single-Task Room". In this room, only one student can sit at the table and work on a project at a time, even if other chairs are vacant. If another student wants to work, they have to wait outside until the current student leaves the room.
- The "Single-Task Room" represents a critical section of code.
- Students represent threads.
- The rule that only one student can work at a time ensures mutual exclusion. No two students (or threads) can use the room (or access the shared resource) at the same time.
Mutual exclusion ensures that only one thread can execute a specific section of code at a time, typically a critical section where a shared resource is accessed or modified.
mechanisms
- Mutex: This is an kernel object that provides mutual exclusion. If a thread owns a mutex, no other thread can own it until the original thread releases it.
- Critical Section: In Windows, this is a lighter-weight form of mutual exclusion compared to a mutex and is only valid within a single process (therefore it is not a kernel object).
Thread Synchronization
Another study room, the "Group-Task Room", is designed for collaborative projects. Here, students come in teams. They can only start their group discussion when all members of their team are present in the room. If a team member is late, the others wait for them before starting.
- The "Group-Task Room" represents a section of code where multiple threads need to coordinate.
- Each student team represents a set of threads that need to synchronize.
- Waiting for all team members before starting the discussion is analogous to synchronization mechanisms, ensuring all threads reach a certain point before proceeding.
Thread synchronization ensures that multiple threads work together coherently, often by controlling the sequence in which threads execute.
mechanisms
- Events: An event can be signaled or non-signaled. Threads can wait for an event to be signaled before proceeding. Events can be used to notify threads when a particular condition has occurred.
- Semaphore: They are used to control access to a shared resource by multiple threads. A semaphore maintains a count, and threads can "acquire" and "release" the semaphore. If the count drops to zero, threads trying to acquire the semaphore will block.
- Condition Variables: Used in some environments, they allow threads to wait until a particular condition is true.
producer-consumer problem
- 作者:Zack Yang
- 链接:https://zackyang.blog/article/win32-thread-control
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章