An important aspect of programming in an environment with threads and processes is to prevent the different processes to interfer with the functionalities of other processes at the wrong time.
In linux, a number of methods are used to ensure that the data or code section of processes is not disturbed by others. These methods are:
- atomic operations
- spinlocks
- semaphore
- reader writer locks
These locks and mechanisms are in the kernel space. Other methods or locking mechanisms are used in the user space.
atomic operations
The idea behind atomic operations is to perform very basic changes on variable but which cannot be interfered by other processes, because they are so small. For this, special data type is used called: atomic_t.
On this data type, a number of atomic operations can be performed:
function | description |
---|---|
atomic_read(atomic_t *v) | read the variable |
atomic_set(atomic_t *v, int i) | set the variable to i |
atomic_add(int i, atomic_t *v) | add i to the variable |
atomic_sub(int i, atomic_t *v) | substract i to the variable |
atomic_sub_and_test(int i, atomic_t *v) | substract i to the variable, return true value if 0 else return false |
atomic_inc(atomic_t *v) | increment the variable |
atomic_inc_and_test(atomic_t *v) | increment the variable, return true value if 0 else return false |
atomic_dec(atomic_t *v) | decrement the variable |
atomic_dec_and_test(atomic_t *v) | decrement the variable, return true value if 0 else return false |
atomic_add_negative(int i, atomic_t *v) | add i to the variable, and return true if its value is negative else false |
Note that I discussed in another post the local variables for CPUs.
spinlocks
This kind of locking is used the most, above all to protect sections for short periods from access of other processes.
The kernel checks continuously whether a lock can be taken on the data. This is an example of busy waiting.
spinlocks are used in the following way:
spinlock_t lock = SPIN_LOCK_UNLOCKED;
...
spin_lock(&lock);
/** critical operations */
spin_unlock(&lock);
Due to the busy waiting, if the lock is not released... the computer may freeze, therefore spinlocks should not be used for long times.
semaphores
Unlike linux spinlocks, the kernel sleeps while waiting for the release of the semaphore. Contrary to spinlocks, this kind of structure should only be used for locks which have a certain length, while for short locks using linux spinlocks is recommended.DECLARE_MUTEX(mutex);
....
down(&mutex);
/** critical section*/
up(&mutex);
The waiting processes then sleep in an uninterruptable state to wait for the release of the lock. The process cannot be woken up using signals during his sleep.
There are other alternatives to the down(&mutex) operation:- down_interruptible(&mutex) : the process can be woken up using signals
- down_trylock(&mutex): if the lock was successful then the process goes on and does not sleep
For the user space, there are also futexes.... But this is another story.
reader writer locks
Using this kind of locks, processors can read the locked data structure but when the structure is to be written the structure can only be manipulated by one processor at a time.