產生競爭的原因:
對資源的共用訪問,包括硬體資源(裝置)和軟體資源(記憶體空間)
原則:
盡量減少共用資源的使用,如全域變數等
解決的方法:
通過建立critical section是操作成為原子操作,使同一時間內,只有一個線程操作該段代碼。
kernel對不同的情況提供了不同的方法,注意的是,有的方法可以 Go to sleep
的,有的不行!
有的方法會導致執行的線程go to sleep,而有的方法不會。
有的代碼是允許sleep的,那就可以使用go to sleep的方法。
但有的代碼是不允許sleep的,那就絕對不能go to sleep的方法。
spinlock就是可以用在不允許sleep的代碼中的。
Semaphore
標頭檔<asm/semaphore.h>.
struct semaphore;
初始化 訊號量
void sema_init(struct semaphore *sem, int val);
where val is the initial value to assign to a semaphore.
直接聲明 互斥量
Thus, a mutex can be declared and initialized with one of the following:
DECLARE_MUTEX(name);
DECLARE_MUTEX_LOCKED(name);
運行時 初始化
void init_MUTEX(struct semaphore *sem);
void init_MUTEX_LOCKED(struct semaphore *sem);
擷取訊號量
void down(struct semaphore *sem);
int down_interruptible(struct semaphore *sem);
int down_trylock(struct semaphore *sem);
用down_interruptible比較多,這樣使用者可以中斷這個函數的執行。
所以一定要對這個函數做傳回值的判斷,如果返回非零,則表示並沒有得到訊號量。
返還訊號量
void up(struct semaphore *sem);
例子:
struct scull_dev {
struct scull_qset *data; /* Pointer to first quantum set */
int quantum; /* the current quantum size */
int qset; /* the current array size */
unsigned long size; /* amount of data stored here */
unsigned int access_key; /* used by sculluid and scullpriv */
struct semaphore sem; /* mutual exclusion semaphore */
struct cdev cdev; /* Char device structure */
};
for (i = 0; i < scull_nr_devs; i++) {
scull_devices[i].quantum = scull_quantum;
scull_devices[i].qset = scull_qset;
init_MUTEX(&scull_devices[i].sem);
scull_setup_cdev(&scull_devices[i], i);
}
一定要在scull_setpu_cdev之前初始化訊號量,否則會出現錯誤。
讀寫訊號量
這種訊號量使用於 有多個讀進程,少量寫進程。 可以同時有多個讀進程一起運行。
標頭檔 <linux/rwsem.h>
struct rw_semaphore
讀操作的訊號量擷取和釋放
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);
Completions
這種結構專門用於等待其他任務的完成的。
雖然用訊號量也可以達到這個作用,但訊號量的效率要低。
struct semaphore sem;
init_MUTEX_LOCKED(&sem);
start_external_task(&sem);
down(&sem);
標頭檔 <linux/completion.h>
struct completion;
靜態聲明:
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);
Spinlock
spinlock是比較特殊的一種鎖,它是可以運行在不能sleep的地方的,比如 ISR中。
進入spinlock後,有幾點要注意
1. 不能被搶佔,這點是自動的,得到spinlock後會自動disable當前處理器的搶佔。
2. 不能sleep, 所以需要非常注意得到spinlock後調用的函數。
3. disable 中斷。