linux一些機制的總結

引文出處:http://blog.csdn.net/smart_qiang/article/details/7667741

 

1. Work

 

將任務添加到系統的工作隊列中

Struct work_struct cd_wq;

INIT_WORK(&cd_wq,work_func);

Schedule_work(&cd_wq);

實際上工作隊列就是一個進程,添加到工作隊列中就是調度的時候運行

 

Struct delayed_work otg_event;

#define DELAY_TIME 100

INIT_DELAYED_WORK(&otg_event,work_delayed_func);

Schedule_delayed_work(&otg_event,DELAY_TIME);

Cancel_delay_work(&otg_event);

 

Flush_scheduled_work(void)

刷新共享隊列,因爲不知道誰可能會用到這個隊列,所以不能知道flush_schedule_work返回需要多長時間

 

創建自己的工作隊列,並且將工作添加到此工作隊列中(一個進程)

用PS能看到此工作隊列名

Struct workqueue_struct *workstruct;

Struct work_struct work;

Workqueue=create_singlethread_workqueue(dev_name(kobject));

INIT_WORK(&work,work_func);

Queue_work(workqueue,&work);

Destroy_workqueue(workqueue);

 

2. Tasklet

Struct tasklet_struct finish_tasklet;

Tasklet_init(&finish_tasklet,tasklet_func,(unsigned long)xxx);

Tasklet_func(unsigned long param)

{

Struct XXX *xxx ;

Xxx=(struct XXX *)param;

}

Tasklet_schedule(&finish_tasklet);

Tasklet_kill(&finish_tasklet);

這裏,tasklet與work類似

不過在硬件中斷服務程式中,同時調度tasklet和work,tasklet會被先調用,也就是說tasklet在那些需要快速調用的中斷下半部中可能會有較大的應用

比如

static irqreturn_t hw_irq(…)

{

Schedule_work(&work1);

Taklet_schedule(&tasklet1);

}

雖然tasklet激活得晚,但會先調度

同時在tasklet_handle中一般不做休眠的操作,如果在handle中msleep(10),編譯的會有提示

 

3. Completion

有點類似信號量,保證按照某種順序執行,實現同步

#include<linux/completion>

Struct completion done;

Init_completion(&done);

Wait_for_completion(&done); //死鎖

Wait_for_completion_timeout(&done,timeout)://不死鎖 超時則處理

Wait_for_completion_interruptible(&done);//可以被信號打斷,如果當前進程受到TIF_SIGPENDING信號,則等待該完成量的進程會被從等待隊列中刪除

Wait_for_completion_interruptible_timeout(&done,timeout);

Complete(&done);

Complete_all(&done);

完成量機制是基於等待隊列的,內核使用該機制等待某一操作的完成。其有兩個參與者:一是等待某操作完成;另一是在操作完成時發出聲明。當然可以有“任意數目”個進程等待操作完成。

完成量的數據描述如下:

 

[cpp] view plaincopyprint?

  1. struct completion {

  2. unsigned int done; /* 用於處理“在進程開始等待之前,事件或操作已經完成” */

  3. wait_queue_head_t wait; /* 地等待隊列 */

  4. };

一、完成量的初始化

 

初始化一個動態分配的completion完成量結構體,

 

[cpp] view plaincopyprint?

  1. static inline void init_completion(struct completion *x)

  2. {

  3. x->done = 0;

  4. init_waitqueue_head(&x->wait);

  5. }

初始化一個靜態completion完成量結構體。

 

 

[cpp] view plaincopyprint?

  1. #define DECLARE_COMPLETION(work) \

  2. struct completion work = COMPLETION_INITIALIZER(work)

二、添加到等待隊列

進程可以通用一下函數添加到等待隊列

 

[cpp] view plaincopyprint?

  1. void __sched wait_for_completion(struct completion *x)

  2. unsigned long __sched wait_for_completion_timeout(struct completion *x, unsigned long timeout)

  3. int __sched wait_for_completion_interruptible(struct completion *x)

  4. long __sched wait_for_completion_interruptible_timeout(struct completion *x, unsigned long timeout)

  5. int __sched wait_for_completion_killable(struct completion *x)

  6. long __sched wait_for_completion_killable_timeout(struct completion *x, unsigned long timeout)

(1) 進程在等待完成時處於不可中斷狀態,若使用wait_for_completion_interruptible表示可中斷,如果進程被中斷,則返回-ERESTARTSYS,否則返回0;

 

(2) wait_for_completion_timeout表示等待事件的發生,並且提供超時設置,如果超過了這一設置,則取消等待,可防止無限等待;如果在超時之前完成則返回剩餘時間,否則返回0。

(3) wait_for_completion_killable表示可以由kill信號中斷。

其他函數均是這三者的變種。

三、喚醒進程

進程喚醒,可以通過以下函數實現:

 

[cpp] view plaincopyprint?

  1. extern void complete(struct completion *);

  2. extern void complete_all(struct completion *);

(1) complete調用每次只能從等待隊列中移除一個進程。如果等待隊列有N個進程,則需要執行N次、
(2) complete_all喚醒所有的等待線程。

 

 

done的解釋:

每次調用complete,done計數器都會+1,僅當done=0時,wait_for系列函數纔會使得調用進程睡眠。


4. Time.c

調試時可以查看當前時間 ns單位

struct timespec tv3;

getnstimeofday(&tv3);

 

//定時啓動的操作相關

#define timer_req 20

struct timer_list timer;

setup_timer(&timer,timer_func,(u32)param);

mod_timer(&timer,jiffies+HZ/1000*timer_req);

del_timer(&timer);

 

5. semaphore

Struct semaphore mutex;

Init_MUTEX(&mutex); //這裏將信號量初始爲互斥鎖,即設置信號量的值爲1

Down(&mutex); //--

Up(&mutex); //++

//兩個信號量可以用於同步 1,0

//一個信號量可以用於互斥 1

//一個信號量初始值大於1 表明可以被多個調用

 

6. mutex

struct mutex lock;

mutex_init(&lock);

mutex_lock(&lock);

mutex_unlock(&lock);

 

7. spin_lock

由於自旋鎖使用者一般保持鎖時間非常短,所以選擇自旋鎖而調用者不會進入睡眠,所以自旋鎖的效率遠高於互斥鎖。

信號量和讀寫信號量適合於保持時間較長的情況,它們會導致調用者睡眠,因此只能在進程上下文中使用(_trylock的變種能在中斷上下文使用);而自旋鎖非常適合保持時間非常短的情況,因爲一個被爭用的自旋鎖使用請求它的線程在等待重新可用時自旋,特別浪費處理時間,這也是自旋鎖的要害之處,所以自旋鎖不應該被長時間持有。在實際應用中,自旋鎖代碼只能有幾行,而持有自旋鎖的時間也一般不會超過兩次上下文切換,因爲線程一旦進行切換,至少花費切出切入兩次,自旋鎖的時間如果遠遠長於兩次上下文切換,就會讓線程睡眠,這也失去了設計自旋鎖的意義

如果被保護的共享資源只能在進程上下文(也就是多個線程共享的資源)訪問,使用信號量保護該共享資源非常合適,如果對於共享資源的訪問時間非常短,自旋鎖也可以。但是,如果被保護的共享資源需要在中斷上下文訪問(包括底半部即中斷處理句柄和頂半部即軟中斷,就必須使用自旋鎖。

 

Spinlock_t lock;

Spin_lock_init(&lock);

Spin_lock(&lock);

Spin_unlock(&lock);

 

Spin_lock_irq(&lock);

Spin_unlock_irq(&lock);

//在某些情況下需要訪問共享資源時必須中斷失效,然後訪問後才能使能中斷,這種時候會用到spin_lock_irqspin_unlock_irq。考慮mxc_spi.c的驅動,在中斷處理程序中有對隊列是否爲空進行判斷,而隊列的操作在中斷服務程式和其他函數中均有涉及,這個時候就要保證訪問共享資源時是否使中斷失效

中斷處理程序中,使用spin_lock spin_unlock 判斷隊列是否爲空

外部函數中,使用spin_lock_irq spin_unlock_irq 判斷隊列是否爲空

 

 

Spin_lock_irqsave(&lock); //保護訪問共享資源前的中斷標誌,然後失效中斷;

Spin_lock_irqrestore(&lock);//恢復訪問共享資源前的中斷標誌,然後使能中斷

 

8. Kref

對於U盤這種支持熱插拔的設備,如果用戶程序正在訪問的時候設備突然被拔掉,驅動程序中的設備對象是否立刻釋放呢?如果立刻釋放,系統調用一定會發生內存非法訪問,如果需要等到用戶程序close之後在釋放設備對象,可以通過計數kref來實現。

Struct kref ref;

Kref_init(&ref);

Kref_get(&ref);

Kref_put(&ref,release_func);

設想,在一個字符設備的probe中,初始化計數爲1,在open函數中kref_get,計數又加一,

如果出現熱插拔事件,在device_disconnect函數,調用kref_put 減一,此時還不執行release_func,不釋放設備對象,只有在close函數調用時,再次調用kref_put,這樣計數爲0,則會調用release_func,從而正確退出,保證內存管理(USB中用到)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章