LDD-Cocurrency

Spinlock

Spinlock usually used in code that cannot sleep, thus has higher performance than semaphores.
Spinlock is implemented as a bit in an integer value.
Before using, a spinlock must be initialized as follows:
spinlock_t my_lock = SPIN_LOCK_UNLOCKED;
void spin_lock_init(spinlock_t *lock);
Beforing entering the critical section, code must obtain the requisite lock with:
void spin_lock(spinlock_t *lock);

Note that all spinlock are uninterruptible, whick means once you call spin_lock, you will spin until the lock become available.

To release a lock that you have obtained, pass it to:
void spin_unlock(spinlock_t *lock);
To avoid deadlock, any code must be atomic while holding a spinlock. The kernel must disable the preemption on the relevant processor, keeping the current processor from scheduling.
 
Completion

In OS class, process synchronization is implemented with p-v operations, as semaphore does, which is inefficient.
The core part of process synchronization is informing other processes when one process is completed----completion mechanism is adopted since 2.4.7 kernel to do this.
The following methods are declared in <linux/completion.h>:
DECLARE_COMPLETION(my_completion);
struct completion my_completion;
init_completion(&my_completion);
void wait_for_completion(struct completion *c);
void complete(struct completion *c);
void complete_all(struct completion *c);/*more than one waiting processes*/
Usually, completion structure can be reused without any problems. But after calling complete_all, initialization must be done before reusing it:
INIT_COMPLETION(struct completion c);

Semaphore


Nowadays, concurrency is the base of high performance computer system, but cause  problems as well. Resources needed by different processes simultaneously must be protected to avoid inconsistency.
Luckily, the kernel provided us useful tools to manage shared resources and prevent mixups. Semaphore is one of them.
To use semaphore, include <asm/semaphore.h>.
Semaphore can be created and initialized in several ways, for example, create a semaphore, then set it up with
void sema_init(struct semaphore *sem, int val);

where val is the initial value to assign to a semaphore.

Usually, semaphores are used in a mutex mode, that means val is set to 1. More simple way is provided
DECLARE_MUTEX(name);
DECLARE_MUTEX_LOCKED(name);

Here, the first semaphore is set to 1, while the second semaphore is set to 0. 

Since we have our semaphores, we can increase and decrease its value.

void down(struct semaphore *sem);
int down_interruptible(struct semaphore *sem);
int down_trylock(struct semaphore *sem);

As the name implies, the first one decrements the value and waits as long as need be. The second one does the same, but interruptible, and is almost always the one we want. The last one never sleeps; if the semaphore is not available at the time of call, it returns immediately with a nonzero return value.

On the contrary, up increases the value:
void up(struct semaphore *sem);

With the tools above, we can manage the critical section access. However, sometimes we don't need to constrain program so strictly. Many processes may just want to read the critical section, without making any changes. In this case, rwsem works well.

Include <linux/rwsem.h> to use rwsems.
void init_rwsem(struct rw_semaphore *sem);
void down_read(struct rw_semaphore *sem);
int down_read_trylock(struct rw_semaphore *sem);
void up_read(struct rw_semaphore *sem);
void down_write(struct rw_semaphore *sem);
int down_write_trylock(struct rw_semaphore *sem);
void up_write(struct rw_semaphore *sem);
void downgrade_write(struct rw_semaphore *sem);

If you have a situation where a writer lock is needed for a quick change, followed by a longer period of readonly access, you can use downgrade_write to allow other readers in once you have finished making changes.

posted @ 2018-11-07 10:18  glob  阅读(191)  评论(0编辑  收藏  举报