出招表七:訊號量(訊號量其實和自旋鎖是一樣的,就是有一點不同:當擷取不到訊號量時,進程不會原地打轉而是進入休眠等待狀態)
Linux系統中與訊號量相關的操作主要有一下4種:
1)定義訊號量 struct semaphore sem;
2)初始化訊號量
void sema_init (struct semphore *sem, int val); //設定sem為val
void init_MUTEX(struct semaphore *sem); //初始化一個使用者互斥的訊號量sem設定為1
void init_MUTEX_LOCKED(struct semaphore *sem); //初始化一個使用者互斥的訊號量sem設定為0
DECLARE_MUTEX(name); //該宏定義訊號量name並初始化1
DECLARE_MUTEX_LOCKED(name); //該宏定義訊號量name並初始化0
3)獲得訊號量
void down(struct semaphore *sem); //該函數用於擷取訊號量sem,會導致睡眠,不能被訊號打斷,所以不能在中斷上下文使用。
int down_interruptible(struct semaphore *sem); //因其進入睡眠狀態的進程能被訊號打斷,訊號也會導致該函數返回,這是返回非0。
int down_trylock(struct semaphore *sem);//嘗試獲得訊號量sem,如果能夠獲得,就獲得並返回0,否則返回非0,不會導致調用者睡眠,可以在中斷上下文使用
一般這樣使用
if(down_interruptible(&sem))
{
return - ERESTARTSYS;
}
4)釋放訊號量
void up(struct semaphore *sem); //釋放訊號量sem,喚醒等待者
訊號量一般這樣被使用,如下所示:
//定義訊號量
DECLARE_MUTEX(mount_sem);
down(&mount_sem);//擷取訊號量,保護臨界區
…
critical section //臨界區
…
up(&mount_sem);
好了,下邊給大家一個例子看看使用訊號量來實現裝置只能被一個進程開啟的例子:
static DECLARE_MUTEX(xxx_lock);//定義互斥鎖
static int xxx_open(struct inode *inode, struct file *filp)
{
…
if(down_trylock(&xxx_lock)) //獲得開啟鎖
return – EBUSY; //裝置忙
…
return 0;//成功
}
static int xxx_release(struct inode *inode, struct file *filp)
{
up(&xxx_lock); //釋放開啟鎖
return 0;
}
在上面介紹的有關訊號量的操作中,我們提到了一個問題就是訊號量的初始化問題,一般對訊號量的初始化是沒有限制的,但如果訊號量被初始化為0,則它可以用於同步,說到這裡,就不得不介紹新的一招了
出招表八:同步(它意味著一個執行單元的繼續執行需要等待另一個執行單元完成其事,保證了執行的先後順序)
在這個圖中,執行單元A執行代碼地區b之前,必須等待執行單元B執行完代碼單元c,而訊號量剛好可輔助完成這一同步過程.
這時你可能要問,像這樣的同步,在現實中可是常常遇到.比如,報名時必須等前一項完成了才能完成後一項..等等,是不是同步就這麼一種方式啊..
呵呵,真聰明,小菜終於長大了,成為大菜了.其實,Linux系統提供了一種更好的同步機制----完成量,好吧,看在你求學若渴的份上,就把它一起傳授給你了,不收費哦,呵呵
出招表九:完成量(completion),它用於一個執行單元等待另一個執行單元執行完某事
使用方法:1)定義完成量
struct completion my_completion;
2)初始化
init_completion(&my_completion); //要是覺得這兩步麻煩,就再給你來個宏即定義又初始化DECLARE_COMPLETION(my_completion);
3)等待完成量
void wait_for_completion(structcompletion *c); //等待一個completion被喚醒
wait_for_completion_interruptible(struct completion *c); //可中斷的wait_for_completion
unsigned long wait_for_completion_timeout(struct completion *x,unsigned long timeout); //帶逾時處理的wait_for_completion
4)喚醒完成量
void complete(struct completion *c); //只喚醒一個等待的執行單元。
void complete_all(struct completion *c); //喚醒所有等待這個完成量的執行單元
瞧我這記性,說了這麼多,怎麼就忘了給出completion結構體呢:
struct completion
{
unsigned int done;//標誌是否已經做完,未做完就是負值,代表等待個數,完成為0
wait_queue_head_t wait;
}
如果覺得不太理解,不過癮,就給你來張圖:
這下感覺怎樣,好多了吧...
說句真的,我都沒想到,這點東西要將那麼久,雖然不願意,但只能說:欲知後事如何,咱們下回接著再聊..