Semaphore
Categories:
5 minute read
Overview
The semaphore is a relatively low-level synchronization mechanism in operating systems. It is a counter with a wait queue.
The semaphore contains an integer counter:
- When the semaphore value equals 0, threads wait in the wait queue for the value to become greater than 0;
- When the semaphore value is greater than 0, a waiting thread can be awakened. The awakened thread takes one unit of value, reducing the semaphore count by 1;
- When the semaphore value is less than 0, the semaphore is in a frozen state. This state does not exist in theoretical semaphores; it is an extension of XWOS.
Any context can increase the value of the semaphore; this operation is called post.
Semaphores are often used to wake up a thread from within an interrupt, placing time-consuming operations in the thread for execution. This reduces execution time in the interrupt context, increases interrupt throughput, and reduces interrupt latency.
The XWOS kernel has two types of semaphores:
- Pipeline Semaphore: All threads blocked in its wait queue are scheduled using a First-In-First-Out (FIFO) policy. That is, when the semaphore is available, the first thread to enter the wait queue will be the first to acquire the semaphore.
- Realtime Semaphore: All threads blocked in its wait queue are scheduled by priority. High-priority threads always acquire the semaphore first, and threads of the same priority are scheduled using a First-In-First-Out (FIFO) policy.
The OSAL CAPI only encapsulates one type of semaphore. When both pipeline semaphore and realtime semaphore are configured in the system configuration file, the realtime semaphore is used by default.
Semaphore Object and Object Descriptor
The semaphore object is a derived class of XWOS Object struct xwos_object.
Similarly, semaphore objects also use the semaphore object descriptor xwos_sem_d
to solve the problems of validity and identity legitimacy.
The semaphore object descriptor is composed of a pointer to the semaphore object and a tag:
typedef struct {
struct xwos_sem * sem; /**< Pointer to the semaphore object */
xwsq_t tik; /**< Tag */
} xwos_sem_d;
When referencing an object through the object descriptor, first check the value of obj->magic to see if it is 0x58574F53U, which confirms that the pointer obj points to a valid XWOS object.
Then compare the tags obj->tik and tik to see if they are equal, which confirms the object’s identity.
Since the object’s tik is globally unique, when the object is released, its tik will be destructed to 0 by the destructor.
When the memory address is reconstructed into a new object, its tik will definitely differ from the tik in the object descriptor.
Semaphore Initialization, Destruction and Dynamic Creation, Deletion
When creating a semaphore, two parameters must be specified: initial value and maximum value.
Static Initialization and Destruction
- Static initialization:
xwos_sem_init()- Static means the user predefines thread structure objects, which are allocated memory by the compiler at compile time.
- Destroy statically initialized semaphore:
xwos_sem_fini()
Dynamic Creation and Deletion
- Dynamic creation:
xwos_sem_create()- Dynamic means the program allocates memory through memory allocation functions at runtime and constructs the object on the allocated memory.
- Delete dynamically created semaphore:
xwos_sem_delete()
Posting a Semaphore
You can increase the semaphore value in any context via xwos_sem_post().
When the semaphore value is greater than 0, it wakes up one thread in the semaphore wait queue. The awakened thread takes one unit of value, decrementing the semaphore counter by 1.
Waiting on a Semaphore
When the semaphore value is greater than 0, one unit can be taken directly, at which point the semaphore value decreases by 1; When the semaphore value equals 0, the thread attempting to acquire the semaphore can only block and wait. XWOS provides four methods:
xwos_sem_wait(): Wait for and acquire the semaphore, can only be used in thread contextxwos_sem_wait_to(): Timed wait for and acquire the semaphore, can only be used in thread contextxwos_sem_wait_unintr(): Uninterruptible wait for and acquire the semaphore, can only be used in thread contextxwos_sem_trywait(): Try to acquire the semaphore, can be used in any context
Reading the Semaphore State
- The maximum value of the semaphore counter can be read via
xwos_sem_get_max(). This maximum value is set during semaphore initialization or creation. - The semaphore counter value can be read via
xwos_sem_get_value(). This CAPI only reads; it does not change the semaphore value or wait on the semaphore.
Freeze and Thaw
Freeze
The semaphore can be frozen using xwos_sem_freeze().
A frozen semaphore has a negative value, which does not affect wait operations on the semaphore, but post operations are not allowed.
Thaw
A frozen semaphore can be thawed via xwos_sem_thaw().
After the semaphore is thawed, its value is reset to 0, and posting the semaphore can resume.
Binding and Unbinding Signal Selector
The semaphore can be bound to a signal selector via xwos_sem_bind().
When the semaphore is posted, it sends a select signal to the signal selector. The signal selector then wakes up the waiting thread.
A bound semaphore can be unbound via xwos_sem_unbind().
Semaphore Object Lifecycle Management
The base class of the semaphore object is XWOS Object struct xwos_object.
The semaphore object also has two sets of lifecycle management CAPI:
-
Access lifecycle management CAPI using object pointer: requires ensuring that the object is definitely valid when calling the CAPI, and there is no case of released-then-reallocated as another object.
xwos_sem_grab(): Increase reference count.xwos_sem_put(): Decrease reference count. When the reference count drops to 0, the garbage collection function is called to release the object.
-
Access lifecycle management CAPI using object descriptor: used when the user cannot guarantee that the object is definitely valid or cannot guarantee that the object has not become another object.
xwos_sem_acquire(): Determine that the object is valid and legitimate through the object descriptor, then increase the reference count.xwos_sem_release(): Determine that the object is valid and legitimate through the object descriptor, then decrease the reference count. When the reference count drops to 0, the garbage collection function is called to release the object.