好久沒寫博文了,最近開始努力的寫吧,有總結纔有提高。
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);
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_irq,spin_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中用到)