The following information is provided to assist you in understanding the locking kernel services:
The following lock allocation services allocate and free internal operating system memory for simple and complex locks, or check if the caller owns a lock:
lock_alloc | Allocates system memory for a simple or complex lock. |
lock_free | Frees the system memory of a simple or complex lock. |
lock_mine | Checks whether a simple or complex lock is owned by the caller. |
Simple locks are exclusive-write, non-recursive locks that protect thread-thread or thread-interrupt critical sections. Simple locks are preemptable, meaning that a kernel thread can be preempted by another, higher priority kernel thread while it holds a simple lock. The simple lock kernel services are:
simple_lock_init | Initializes a simple lock. |
simple_lock, simple_lock_try | Locks a simple lock. |
simple_unlock | Unlocks a simple lock. |
On a multiprocessor system, simple locks that protect thread-interrupt critical sections must be used in conjunction with interrupt control in order to serialize execution both within the executing processor and between different processors. On a uniprocessor system interrupt control is sufficient; there is no need to use locks. The following kernel services provide appropriate locking calls for the system on which they are executed:
disable_lock | Raises the interrupt priority, and locks a simple lock if necessary. |
unlock_enable | Unlocks a simple lock if necessary, and restores the interrupt priority. |
Using the disable_lock and unlock_enable kernel services to protect thread-interrupt critical sections (instead of calling the underlying interrupt control and locking kernel services directly) ensures that multiprocessor-safe code does not make unnecessary locking calls on uniprocessor systems.
Simple locks are spin locks; a kernel thread that attempts to acquire a simple lock may spin (busy-wait: repeatedly execute instructions which do nothing) if the lock is not free. The table shows the behavior of kernel threads and interrupt handlers that attempt to acquire a busy simple lock.
Caller | Owner is Running | Owner is Sleeping |
---|---|---|
Thread (with interrupts enabled) | Caller spins initially; it sleeps if the maximum spin threshold is crossed. | Caller sleeps immediately. |
Interrupt handler or thread (with interrupts disabled) | Caller spins until lock is acquired. | Caller spins until lock is freed (must not happen). |
A simple lock that protects a thread-interrupt critical section must never be held across a sleep, otherwise the interrupt could spin for the duration of the sleep, as shown in the table. This means that such a routine must not call any external services that might result in a sleep. In general, using any kernel service which is callable from process level may result in a sleep, as can accessing unpinned data. These restrictions do not apply to simple locks that protect thread-thread critical sections.
The lock word of a simple lock must be located in pinned memory if simple locking services are called with interrupts disabled.
Complex locks are read-write locks that protect thread-thread critical sections. Complex locks are preemptable, meaning that a kernel thread can be preempted by another, higher priority kernel thread while it holds a complex lock. The complex lock kernel services are:
lock_init | Initializes a complex lock. |
lock_islocked | Tests whether a complex lock is locked. |
lock_done | Unlocks a complex lock. |
lock_read, lock_try_read | Locks a complex lock in shared-read mode. |
lock_read_to_write, lock_try_read_to_write | Upgrades a complex lock from shared-read mode to exclusive-write mode. |
lock_write, lock_try_write | Locks a complex lock in exclusive-write mode. |
lock_write_to_read | Downgrades a complex lock from exclusive-write mode to shared-read mode. |
lock_set_recursive | Prepares a complex lock for recursive use. |
lock_clear_recursive | Prevents a complex lock from being acquired recursively. |
By default, complex locks are not recursive (they cannot be acquired in exclusive-write mode multiple times by a single thread). A complex lock can become recursive through the lock_set_recursive kernel service. A recursive complex lock is not freed until lock_done is called once for each time that the lock was locked.
Complex locks are not spin locks; a kernel thread that attempts to acquire a complex lock may spin briefly (busy-wait: repeatedly execute instructions which do nothing) if the lock is not free. The table shows the behavior of kernel threads that attempt to acquire a busy complex lock:
Current Lock Mode | Owner is Running and no Other Thread is Asleep on This Lock | Owner is Sleeping |
---|---|---|
Exclusive-write | Caller spins initially, but sleeps if the maximum spin threshold is crossed, or if the owner later sleeps. | Caller sleeps immediately. |
Shared-read being acquired for exclusive-write | Caller sleeps immediately. | |
Shared-read being acquired for shared-read | Lock granted immediately |
Lockl locks are exclusive-access and recursive locks. The lockl lock kernel services are:
lockl | Locks a conventional lock. |
unlockl | Unlocks a conventional lock. |
A thread which tries to acquire a busy lockl lock sleeps immediately.
The lock word of a lockl lock must be located in pinned memory if the lockl service is called with interrupts disabled.
Atomic operations are sequences of instructions that guarantee atomic accesses and updates of shared single word variables. This means that atomic operations cannot protect accesses to complex data structures in the way that locks can, but they provide a very efficient way of serializing access to a single word.
The atomic operation kernel services are:
fetch_and_add | Increments a single word variable atomically. |
fetch_and_and, fetch_and_or | Manipulates bits in a single word variable atomically. |
compare_and_swap | Conditionally updates or returns a single word variable atomically. |
Single word variables accessed by atomic operations must be aligned on a full word boundary, and must be located in pinned memory if atomic operation kernel services are called with interrupts disabled.