亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 1161 | 回復(fù): 0
打印 上一主題 下一主題

(轉(zhuǎn))linux的同步與互斥 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2009-12-28 00:17 |只看該作者 |倒序?yàn)g覽

臨界區(qū):(critical region)
所謂臨界區(qū)就是訪問和操作共享數(shù)據(jù)的代碼段。
并發(fā)有偽并發(fā)(單處理器)和真并發(fā)(多處理器)之分,但是都會(huì)造成競爭條件。
同步:(synchronization)
避免并發(fā)(多個(gè)執(zhí)行線程并發(fā)訪問同一個(gè)資源)和防止競爭條件(兩個(gè)執(zhí)行線程處于同一臨界區(qū))被稱為同步。
用戶空間之所以需要同步,是因?yàn)橛脩舫绦驎?huì)被調(diào)度程序搶占和重新調(diào)度。
造成并發(fā)執(zhí)行的原因:
1 中斷
2 軟中斷和tasklet
3 內(nèi)核搶占——任務(wù)的優(yōu)先級(jí)
4 睡眠及用戶空間的同步
5 SMP
在編寫代碼的開始階段就應(yīng)該設(shè)計(jì)恰當(dāng)?shù)逆i
我們要給數(shù)據(jù)而不是代碼加鎖。大多數(shù)內(nèi)核數(shù)據(jù)結(jié)構(gòu)都需要加鎖!而局部自動(dòng)變量,動(dòng)態(tài)分配的數(shù)據(jù)結(jié)構(gòu)不需要加鎖。
Linux信號(hào)量的實(shí)現(xiàn)
0.概念:
信號(hào)量: 一個(gè)信號(hào)量本質(zhì)上是一個(gè)整數(shù)值, 它和一對函數(shù)聯(lián)合使用, 這一對函數(shù)通常成為P和V, 也就是我們所說的P/V操作.
· 當(dāng)一個(gè)進(jìn)程希望進(jìn)入臨界區(qū)時(shí), 對臨界區(qū)的信號(hào)量執(zhí)行P操作:
· 如果信號(hào)量的值大于0, 則該值會(huì)減1, 而進(jìn)程可以繼續(xù);
· 如果信號(hào)量的值等于0(或更小), 進(jìn)程必須等待直到其他人釋放該信號(hào)量.
· 當(dāng)一個(gè)進(jìn)程完成對臨界區(qū)的操作時(shí), 對臨界區(qū)信號(hào)量執(zhí)行V操作:
將信號(hào)量的值加1, 并在必要時(shí)喚醒等待的進(jìn)程.
· 在這種模式下, 一個(gè)信號(hào)量有時(shí)也稱為一個(gè)"互斥體"(mutex), 它是互斥的簡稱.
規(guī)則:
· 當(dāng)信號(hào)量用于互斥時(shí)(即避免多個(gè)進(jìn)程同時(shí)操作一個(gè)臨界區(qū)), 信號(hào)量的值應(yīng)該初始化為1.
· 信號(hào)量在任何時(shí)刻只能由單個(gè)進(jìn)程或線程擁有.
1. 定義:
頭文件:
數(shù)據(jù)類型: struct semaphore
直接創(chuàng)建:
void sema_init(struct semaphore *sem, int val); /* 其中val是信號(hào)量的初始值 */
輔助宏:
DECLARE_MUTEX(name);                 /* 把一個(gè)稱為name的信號(hào)量變量初始化為1 */
DECLARE_MUTEX_LOCKED(name); /* 把一個(gè)稱為name的信號(hào)量變量初始化為0 */
動(dòng)態(tài)分配:
/* 用于運(yùn)行時(shí)的初始化 */
void init_MUTEX(struct semaphore *sem);
void init_MUTEX_LOCKED(struct semaphore *sem);
在Linux世界中, P函數(shù)被稱為down, 指的是該函數(shù)減小了信號(hào)量的值, 它也許會(huì)將調(diào)用者置于休眠狀態(tài), 然后等待信號(hào)量變得可用, 之后再授予調(diào)用者對被保護(hù)資源的訪問權(quán)限. down函數(shù)有三個(gè)版本:
void down(struct semaphore *sem);     /* 減小信號(hào)量的值, 并在必要時(shí)一直等待。無法獲得信號(hào)量時(shí),會(huì)導(dǎo)致調(diào)用進(jìn)程休眠*/
作為通常規(guī)則, 我們不應(yīng)該使用非中斷版本. 非中斷操作是建立不可殺進(jìn)程的好方法(ps輸出中的"D state")
void down_interruptible(struct semephore *sem);  /* 可中斷版本, 常用。 無法獲得信號(hào)量時(shí),會(huì)導(dǎo)致調(diào)用進(jìn)程休眠*/
可中斷版本幾乎是我們始終要使用的版本。
使用該函數(shù), 如果得到,返回0;如果操作被中斷, 該函數(shù)會(huì)返回非0值(-EINTR), 而調(diào)用者不會(huì)擁有該信號(hào)量.
因此對該函數(shù)的正確使用需要始終檢查返回值, 并做出相應(yīng)的響應(yīng).
void down_trylock(struct semaphore *sem);
/* 永遠(yuǎn)不會(huì)休眠, 如信號(hào)量在調(diào)用時(shí)不可獲得, 立即返回非0值 */
后來的內(nèi)核又增加了:
int down_killable(struct semaphore *sem);
/*無法獲得信號(hào)量時(shí),會(huì)導(dǎo)致調(diào)用進(jìn)程休眠*,操作可被fatal信號(hào)中斷, 函數(shù)會(huì)返回非0值(-EINTR), 而調(diào)用者不會(huì)擁有該信號(hào)量. */
int down_timeout(struct semaphore *sem, long jiffies);
/*在指定時(shí)間內(nèi)獲取該信號(hào)量,會(huì)導(dǎo)致調(diào)用進(jìn)程休眠,無法獲取時(shí)返回-ETIME*/
當(dāng)一個(gè)線程成功調(diào)用down函數(shù)的某個(gè)版本之后, 就稱為該線程擁有了該信號(hào)量, 可以訪問被該信號(hào)量保護(hù)的臨界區(qū). 當(dāng)互斥操作完成后, 必須釋放該信號(hào)量.
Linux的V函數(shù)是up:
/* 調(diào)用up之后, 調(diào)用者不再擁有該信號(hào)量 */
void up(struct semaphore *sem);
2. 舉例:
我們擁有一個(gè)共享數(shù)據(jù)結(jié)構(gòu):
struct st_data
{
    char name[32];
    char data[128];
    int data_len;
};
這個(gè)數(shù)據(jù)結(jié)構(gòu)被多個(gè)進(jìn)程同時(shí)訪問.
為了避免這些進(jìn)程在訪問該結(jié)構(gòu)時(shí)產(chǎn)生競態(tài), 我們在該結(jié)構(gòu)的底部為其加上信號(hào)量:
struct st_data
{
    char name[32];              /* name */
    char data[128];              /* data */
    int data_len;                  /* data length */
    struct semaphore sem; /* semaphore */
};
信號(hào)量在使用前必須進(jìn)行初始化, 而且是在共享數(shù)據(jù)其他部分可用前初始化. 因此, 我們在其他數(shù)據(jù)賦值之前調(diào)用init_MUTEX, 否則會(huì)建立一個(gè)競態(tài), 即在信號(hào)量準(zhǔn)備好之前, 有代碼可能會(huì)訪問它們.
st_data data;
init_MUTEX(&data->sem);
setup_data(&data); /* 初始化數(shù)據(jù) */
...
接下來, 我們必須仔細(xì)檢查代碼, 確保在不擁有該信號(hào)量的時(shí)候不會(huì)訪問data數(shù)據(jù)結(jié)構(gòu). 例如, 在data_write的開始處加入:
if (down_interruptible(&data->sem))   
     return -ERESTARTSYS;
這是檢查down_interruptible的返回值, 如果返回非0值, 說明操作被中斷. 這種情況下, 通常要做的工作是返回-ERESTARTSYS. 在得到這個(gè)返回值后, 內(nèi)核會(huì)從頭重新啟動(dòng)該調(diào)用, 或者將該錯(cuò)誤返回給用戶.
如果我們返回-ERESTARTSYS, 則必須首先撤銷已經(jīng)做出的修改, 這樣, 系統(tǒng)調(diào)用才可正確重試. 如果無法撤銷這些操作, 則應(yīng)該返回-EINTR, 表明中斷.
不管data_write能否完成其他工作, 它都必須釋放信號(hào)量:
out:
    up(&data->sem);
    return retval;
在data_write中有幾個(gè)地方可能會(huì)產(chǎn)生錯(cuò)誤, 包括內(nèi)存分配失敗等. 在這些情況下, 代碼會(huì)執(zhí)行g(shù)oto out, 確保正確的完成信號(hào)量的釋放工作.
讀取/寫入信號(hào)量
信號(hào)量對所有調(diào)用者執(zhí)行互斥操作, 而不管線程想做什么(讀/寫). 正如這樣, 我們可以把任務(wù)劃分為兩種類型: 讀取和寫入. 多個(gè)并發(fā)的讀取應(yīng)該是被允許的, 因?yàn)橹蛔x任務(wù)可并行完成他們的工作, 這樣做可以大大提高性能.
如此, 便有了Linux內(nèi)核提供的一種特殊信號(hào)量類型, 稱為"rwsem"(reader/writer semaphore). 雖然在驅(qū)動(dòng)程序中使用rwsem的機(jī)會(huì)相對比較少, 但偶爾也比較有用.
頭文件:
數(shù)據(jù)類型: struct rw_semaphore
一個(gè)rwsem對象必須用一下函數(shù)顯式地初始化:
void init_rwsem(struct rw_semaphore *sem);
對受保護(hù)資源的只讀訪問, 可和其他讀取者并發(fā)地訪問. 可用的接口有:
void down_read(struct rw_semaphore *sem);/* 可能會(huì)將調(diào)用進(jìn)程置于不可中斷的休眠 */
int down_read_trylock(struct rw_semaphore *sem);/* 不會(huì)阻塞等待,授予訪問時(shí)返回非0, 其他情況返回0  注意他的返回值用法和其它函數(shù)的不同*/
void up_read(struct rw_semaphore *sem); /* 釋放rw_sem對象 */
針對寫入者的接口類似于讀取者:
void down_write(struct rw_semaphore *sem);
int down_write_trylock(struct rw_semaphore *sem);
void up_write(struct rw_semaphore *sem);
這3個(gè)函數(shù)與讀取者的對應(yīng)函數(shù)行為相同, 他們提供的是寫入訪問.
/* 當(dāng)某個(gè)快速改變獲得寫入者鎖,
  *其后有更長時(shí)間的只讀訪問的時(shí)候,
  *我們可以調(diào)用該函數(shù)來允許其他讀取者訪問 */
void downgrade_write(struct rw_semaphore *sem);
讀取/寫入信號(hào)量特點(diǎn):
· 一個(gè)rwsem可以允許一個(gè)寫入者或無限多個(gè)讀取者擁有該信號(hào)量.
· 寫入者具有更高的優(yōu)先級(jí), 某個(gè)寫入者試圖進(jìn)入臨界區(qū), 在其完成工作之前, 不會(huì)允許讀取者獲得訪問.
· 如果大量寫入者競爭該信號(hào)量, 會(huì)導(dǎo)致讀取者"餓死".
· 最好在很少需要寫訪問且寫入者只會(huì)短期擁有信號(hào)量什使用rwsem.
Completion(完成量)
有時(shí), 由于信號(hào)量輕松up操作之后, 會(huì)產(chǎn)生過長時(shí)間的操作, 為了避免信號(hào)量執(zhí)行down操作導(dǎo)致的長時(shí)間阻塞, 內(nèi)核提供了一組completion(完成)接口解決這種問題.
Completion是一種輕量級(jí)的機(jī)制, 它允許一個(gè)線程告訴另一個(gè)線程某個(gè)工作已經(jīng)完成.
頭文件:
數(shù)據(jù)類型: struct completion
一個(gè)completion對象可以通過兩種方法創(chuàng)建和初始化:
DECLARE_COMPLETION(my_completion);
或者, 如果必須動(dòng)態(tài), 則使用:
struct completion my_completion;
/* ... */
init_completion(&my_completion);
對completion操作的接口有:
等待completion的操作可以用:
/* 執(zhí)行一個(gè)非中斷等待 */
void wait_for_completion(struct completion *c);
實(shí)際的completion事件調(diào)用可以用:
/* 只會(huì)喚醒一個(gè)等待線程 */
void complete(struct completion *c);
/* 喚醒所有等待線程 */
void complete_all(struct completion *c);
但在大多數(shù)情況下, 只會(huì)有一個(gè)等待者, 此時(shí)兩個(gè)函數(shù)效果相同.
一個(gè)completion通常是一個(gè)單次(one-shot)設(shè)備, 它只會(huì)被使用一次然后被丟棄.觸發(fā)事件明確時(shí), 如果沒有使用complete_all, 可以重復(fù)使用一個(gè)completion結(jié)構(gòu).如果使用了complete_all, 則必須在重復(fù)使用該結(jié)構(gòu)之前重新初始化它.
可以用下面的宏進(jìn)行重新初始化Completion
INIT_COMPLETION(struct completion *c) ;
Completion機(jī)制的典型是模塊退出時(shí)的內(nèi)核線程終止. 這時(shí), 驅(qū)動(dòng)程序內(nèi)部由一個(gè)while(1)循環(huán)完成, 當(dāng)內(nèi)核準(zhǔn)備清楚該模塊時(shí), exit函數(shù)會(huì)令線程退出并等待completion. 此時(shí), 內(nèi)核可調(diào)用一個(gè)特殊函數(shù):
void complete_and_exit(struct completion *c, long retval);
/* 其中retval是exit的返回值, 可用于錯(cuò)誤代碼檢查 */
linux中自旋鎖(spinlock)的實(shí)現(xiàn)
信號(hào)量對互斥來講是非常有用的, 但它并不是內(nèi)核提供的唯一工具.
大多數(shù)鎖定通過稱為"自旋鎖"的機(jī)制實(shí)現(xiàn). 和信號(hào)量不同, 自旋鎖可以在不能休眠的代碼中使用, 比如: 中斷處理例程. 在正確使用的情況下, 自旋鎖通?梢蕴峁┍刃盘(hào)量更高的性能, 但它也有一組不同的使用限制.
1. 概念:
自旋鎖在概念上非常簡單, 一個(gè)自旋鎖就是一個(gè)互斥設(shè)備, 它只有兩個(gè)值: "鎖定"和"解鎖". 它通常實(shí)現(xiàn)為某個(gè)整數(shù)值中的單個(gè)位.
· 希望獲得某特定鎖的代碼, 測試相關(guān)的位. (原子操作)
· 如果鎖可用, 則"鎖定"位被設(shè)置, 而代碼繼續(xù)進(jìn)入臨界區(qū).
· 相反, 如果鎖被其他人獲得, 則代碼進(jìn)入忙循環(huán)并重復(fù)檢查這個(gè)鎖, 直到該鎖可用為止.
這個(gè)循環(huán)就是自旋鎖的"自旋"部分. 當(dāng)存在自旋鎖時(shí), 等待執(zhí)行忙循環(huán)的處理器做不了任何有用的工作.
核心原則:任何擁有自旋鎖的代碼都必須是原子的。它不能休眠。
另外一個(gè)重要原則:自旋鎖必須在可能的最短時(shí)間內(nèi)擁有。
在擁有鎖時(shí),要注意調(diào)用的每一個(gè)函數(shù)是否會(huì)引起睡眠。是否會(huì)有中斷服務(wù)程序獲得相同的鎖(需禁止本地CPU中斷)
2. 自旋鎖基本API:
頭文件:
數(shù)據(jù)類型: spinlock_t
初始化, 以下兩種方法:
      靜態(tài)(編譯時(shí)): spinlock_t my_lock = SPIN_LOCK_UNLOCKED;
      動(dòng)態(tài)(運(yùn)行時(shí)): void spin_lock_init(spinlock_t *lock);
獲得鎖: void spin_lock(spinlock_t *lock);
釋放鎖: void spin_unlock(spinlock_t *lock);
3. 自旋鎖其他API:
鎖定一個(gè)自旋鎖的函數(shù)實(shí)際有4個(gè):
void spin_lock(spinlock_t *lock);     /* 基本函數(shù) */
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);/* 會(huì)在獲得自旋鎖之前禁止本地中斷, 而先前的中斷狀態(tài)保存在flags中 */
void spin_lock_irq(spinlock_t *lock);/* 同上, 但無跟蹤標(biāo)志 */
void spin_lock_bh(spinlock_t *lock);/* 在獲得鎖之前禁止軟件中斷 */
對應(yīng)的, 也有4個(gè)unlock函數(shù):
void spin_unlock(spinlock_t *lock);
/* 這里面的flags必須和傳遞給spinlock_lock_irqsave的是同一個(gè)變量,
* 還必須在同一函數(shù)中調(diào)用, 否則代碼可能在某些架構(gòu)上出現(xiàn)問題 */
void spin_unlock_irqstore(spinlock_t *lock, unsigned long flags);
void spin_unlock_irq(spinlock_t *lock);
void spin_unlock_bh(spinlock_t *lock);
同時(shí), 還有2個(gè)非阻塞的自旋鎖操作:
int spin_trylock(spinlock_t *lock);      /*成功獲得返回非0,否則返回0*/
int spin_trylock_bh(spinlock_t *lock); /*成功獲得返回非0,否則返回0*/
對禁止中斷的情況, 沒有對應(yīng)的"try"版本.
注意:《linux內(nèi)核設(shè)計(jì)與實(shí)現(xiàn)》9.2節(jié) 自旋鎖 中論述:
自旋鎖更多的是為多處理器提供防止并發(fā)訪問所需要的保護(hù)機(jī)制。
在單處理器機(jī)器上,編譯的時(shí)候并不會(huì)加入自旋鎖。它僅僅被當(dāng)作一個(gè)設(shè)置內(nèi)核搶占機(jī)制是否被啟用的開關(guān)。如果禁止內(nèi)核搶占,那么在編譯時(shí)自旋鎖會(huì)被完全剔除出內(nèi)核。
觀察內(nèi)核代碼發(fā)現(xiàn):在自旋鎖與CONFIG_SMP和CONFIG_PREEMPT兩個(gè)配置選項(xiàng)有很大的關(guān)聯(lián)。
而在自旋鎖的各個(gè)操作函數(shù)中都會(huì)調(diào)用preempt_disable()來禁止搶占(若CONFIG_PREEMPT選項(xiàng)關(guān)閉,則preempt_disable為空操作)
內(nèi)核搶占代碼使用自旋鎖作為非搶占區(qū)域的標(biāo)記。
Linux 2.4.x及以前的版本都是非搶占式內(nèi)核方式,如果編譯成單處理器系統(tǒng),在同一時(shí)間只有一個(gè)進(jìn)程在執(zhí)行,除非它自己放棄,不然只有通過"中斷"才能中斷其執(zhí)行。因此,在單處理器非搶占式內(nèi)核中,如果需要修改某個(gè)重要的數(shù)據(jù)結(jié)構(gòu),或者執(zhí)行某些關(guān)鍵代碼,只需要禁止中斷。
但是在對稱多處理器,僅僅禁止某個(gè)CPU的中斷是不夠的,當(dāng)然我們也可以將所有CPU的中斷都禁止,但這樣做開銷很大,整個(gè)系統(tǒng)的性能會(huì)明顯下降。
此外,即使在單處理器上,如果內(nèi)核是搶占式的,也可能出現(xiàn)不同進(jìn)程上下文同時(shí)進(jìn)入臨界區(qū)的情況。為此,Linux內(nèi)核中提供了"自旋鎖(spinlock)"的同步機(jī)制。
自旋鎖和信號(hào)量的比較
需求
建議的加鎖方法
低開銷加鎖
優(yōu)先使用自旋鎖
短期加鎖
優(yōu)先使用自旋鎖
長期加鎖
優(yōu)先使用信號(hào)量
中斷上下文加鎖
使用自旋鎖
持有鎖需要睡眠
使用信號(hào)量
讀取/寫入自旋鎖
頭文件:
數(shù)據(jù)類型: rwlock_t
初始化, 以下兩種方法:
rwlock_t my_lock = RW_LOCK_UNLOCKED;    /*Static way*/
rwlock_t my_lock;
rw_lock_init(&my_lock);  /*Dynamic way*/
讀取相關(guān)的自旋鎖的函數(shù):
void read_lock(rwlock_t *lock);
void read_lock_irqsave(rwlock_t *lock, unsigned long flags);
void read_lock_irq(rwlock_t *lock);/* 同上, 但無跟蹤標(biāo)志 */
void read_lock_bh(rwlock_t *lock);/* 在獲得鎖之前禁止軟件中斷 */
void read_unlock(rwlock_t *lock);
voidread_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
voidread_unlock_irq(rwlock_t *lock);
voidread_unlock_bh(rwlock_t *lock);
沒有read_trylock函數(shù)可用。
寫入相關(guān)的自旋鎖的函數(shù):
void write_lock(rwlock_t *lock);
voidwrite_lock_irqsave(rwlock_t *lock, unsigned long flags);
voidwrite_lock_irq(rwlock_t *lock);/* 同上, 但無跟蹤標(biāo)志 */
voidwrite_lock_bh(rwlock_t *lock);/* 在獲得鎖之前禁止軟件中斷 */
voidwrite_unlock(rwlock_t *lock);
voidwrite_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
voidwrite_unlock_irq(rwlock_t *lock);
void write_unlock_bh(rwlock_t *lock);
排隊(duì)自旋鎖(FIFO Ticket Spinlock)
排隊(duì)自旋鎖(FIFO Ticket Spinlock)是 Linux 內(nèi)核 2.6.25 版本中引入的一種新型自旋鎖,它解決了傳統(tǒng)自旋鎖由于無序競爭導(dǎo)致的“公平性”問題。
參考:
Linux 內(nèi)核的排隊(duì)自旋鎖(FIFO Ticket Spinlock)
內(nèi)核處理方式我的理解如下:
將自旋鎖的計(jì)數(shù)整數(shù)分成兩個(gè)域:Next域和Owner域,兩個(gè)域的位數(shù)相同。分別保存未來鎖申請者和當(dāng)前鎖持有者的票據(jù)序號(hào)(Ticket Number),初始時(shí)Next = Owner = 0。
當(dāng)申請者申請排隊(duì)自旋鎖時(shí),它會(huì)獲得一個(gè)票據(jù)號(hào),是當(dāng)前Next值,然后Next增1;然后比較申請者的票據(jù)號(hào)和Owner值,若相等,則申請者可以獲得該鎖,否則忙等待;
當(dāng)申請者處理完后釋放排隊(duì)自旋鎖時(shí),會(huì)使該鎖的Owner值增1。等待的下一個(gè)申請者會(huì)發(fā)現(xiàn)這一變化,從忙等待狀態(tài)中退出,并獲得該鎖。
線程將嚴(yán)格地按照申請順序拿到Next票據(jù)號(hào),然后隨著Owner的增加,票據(jù)號(hào) = Owner的申請者依次獲取排隊(duì)自旋鎖,從而完全解決了“不公平”問題。
排序自旋鎖和銀行的拿號(hào)排隊(duì)的原理是相同的。
死鎖
產(chǎn)生死鎖的原因:資源競爭以及進(jìn)程推進(jìn)順序非法
產(chǎn)生死鎖的4個(gè)必要條件:
· 互斥條件
· 請求保持條件
· 不可剝奪條件
· 環(huán)路條件
死鎖的預(yù)防:預(yù)先靜態(tài)分配法 和 資源有序分配法
免鎖方法
1、循環(huán)緩沖區(qū)——免鎖算法
在設(shè)備驅(qū)動(dòng)程序中使用相當(dāng)普遍,特別是網(wǎng)絡(luò)適配器(處理交換數(shù)據(jù))
一個(gè)生產(chǎn)者將數(shù)據(jù)放入數(shù)組的結(jié)尾,消費(fèi)者從數(shù)組的另一端移走數(shù)據(jù),當(dāng)?shù)竭_(dá)數(shù)組尾部時(shí),生產(chǎn)者繞回到數(shù)組頭部。沒有多個(gè)生產(chǎn)者或消費(fèi)者的情況下,循環(huán)緩沖區(qū)不需要加鎖。
內(nèi)核中有一個(gè)通用的循環(huán)緩沖區(qū)實(shí)現(xiàn),參閱
參考:
Linux設(shè)備驅(qū)動(dòng)程序?qū)W習(xí)(3-補(bǔ))-Linux中的循環(huán)緩沖區(qū)
2、原子變量
有時(shí),共享的資源可能恰好是個(gè)簡單的整數(shù)值。
內(nèi)核提供了一種原子的整數(shù)類型,稱為:atomic_t
定義在:
這是一個(gè)與CPU架構(gòu)有關(guān)的變量。
atomic_t數(shù)據(jù)項(xiàng)必須只能通過下面的函數(shù)來訪問
初始化:
void atomic_set(atomic_t *v, int i);
atomic_t v = ATOMIC_INIT(0);
讀取變量值:
int atomic_read(atomic_t *v);
增減操作:(注意不返回操作后的結(jié)果)
void atomic_add(int i, atomic_t *v);  /* v=+i */
void atomic_sub(int i, atomic_t *v); /* v=-i */
void atomic_inc(atomic_t *v);   /* v++ */
void atomic_dec(atomic_t *v);  /* v-- */
附加操作:
操作后并測試原子值,若為0,則返回true,否則返回false
int atomic_inc_and_test(atomic_t *v);  
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i, atomic_t *v);
操作后并測試原子值,若為負(fù),則返回true,否則返回false
int atomic_add_negative(int i, atomic_t *v);
操作后并返回原子值給調(diào)用者
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);
3、位操作
內(nèi)核提供了一組可原子地修改和測試單個(gè)位的函數(shù)。
原子位操作只要硬件允許,就可以使用單個(gè)機(jī)器指令來執(zhí)行,并且不需要禁止中斷。所以位操作也是依賴于具體的架構(gòu),在中聲明
nr通常定義為int型或unsigned long;addr通常是unsigned long* 或 void*
void set_bit(nr, void *addr);   /*設(shè)置addr指向的數(shù)據(jù)項(xiàng)的第nr位*/
void clear_bit(nr, void *addr); /*清除addr指向的數(shù)據(jù)項(xiàng)的第nr位*/
void change_bit(nr, void *addr);/*切換addr指向的數(shù)據(jù)項(xiàng)的第nr位*/
test_bit(nr, void *addr);/*返回指定位的當(dāng)前值,唯一不必原子方式實(shí)現(xiàn)的位操作函數(shù)*/
int test_and_set_bit(nr, void *addr);  /*設(shè)定指定位,并返回指定位的先前值*/
int test_and_clear_bit(nr, void *addr);/*清除指定位,并返回指定位的先前值*/
int test_and_change_bit(nr, void *addr);/*切換指定位,并返回指定位的先前值*/
在新代碼中應(yīng)該優(yōu)先使用自旋鎖,因?yàn)樽孕i已經(jīng)很好的調(diào)試過,并且易讀性比位操作更好。
4、順序鎖(seqlock)
當(dāng)要保護(hù)的資源很小、很簡單、會(huì)頻繁被訪問而且寫入訪問很少發(fā)生且必須快速時(shí),就可以使用seqlock。
seqlock通常不能用于保護(hù)包含有指針的數(shù)據(jù)結(jié)構(gòu)。
seqlock在中定義。
seqlock_t lock1 = SEQLOCK_UNLOCKED;
seqlock_t lock2;
seqlock_init(&lock2);
seqlock允許讀取者對資源的自由訪問,但需要讀取者檢查是否和寫入著發(fā)生沖突,沖突發(fā)生時(shí),需要重試訪問資源。讀取者的代碼如下:
unsigned int seq;
do {
       seq = read_seqbegin(&the_lock);    /* 獲得一個(gè)(無符號(hào)的)整數(shù)順序值seq*/
      /* Do what you need to do */
} while read_seqretry(&the_lock, seq);  /*退出時(shí),seq和當(dāng)前值比較,若不等則重試*/
在中斷處理例程中,使用IRQ安全版本:
unsigned int read_seqbegin_irqsave(seqlock_t *lock, unsigned long flags);
int read_seqretry_irqrestore(seqlock_t *lock, unsigned int seq, unsigned long flags);
寫入者在進(jìn)入由seqlock保護(hù)的臨界區(qū)時(shí)獲得一個(gè)互斥鎖。(寫入鎖使用自旋鎖實(shí)現(xiàn))
void write_seqlock(seqlock_t *lock);
釋放鎖:
void write_sequnlock(seqlock_t *lock);
常用變種:
void write_seqlock_irqsave(seqlock_t *lock, unsigned long flags);
void write_seqlock_irq(seqlock_t *lock);
void write_seqlock_bh(seqlock_t *lock);
void write_sequnlock_irqrestore(seqlock_t *lock, unsigned long flags);
void write_sequnlock_irq(seqlock_t *lock);
void write_sequnlock_bh(seqlock_t *lock);
5、讀取-復(fù)制-更新(read-copy-update,RCU)
一種高級(jí)的互斥機(jī)制,它針對經(jīng)常發(fā)生讀取而很少寫入的情形的優(yōu)化。被保護(hù)的資源應(yīng)該通過指針訪問,而對這些資源的訪問應(yīng)該通過指針進(jìn)行,而對這些資源的引用必須僅有原子代碼擁有。在需要修改該數(shù)據(jù)結(jié)構(gòu)時(shí),寫入線程首先復(fù)制,然后修改副本,子后用新的版本替代相關(guān)指針。當(dāng)內(nèi)核確信老的版本上沒有其他引用時(shí),可以釋放老的版本。
參見RCU的白皮書
(
http://www.rdrop.com/users/paulmck/rclock/intro/rclock_intro.html
).
以及內(nèi)核頭文件
應(yīng)用實(shí)例參考網(wǎng)絡(luò)路由表
參考文獻(xiàn):
1.Linux Device Driver 3rd
2.Linux Kernel Develoment 2nd
3.
Linux內(nèi)核的同步機(jī)制


本文來自ChinaUnix博客,如果查看原文請點(diǎn):http://blog.chinaunix.net/u3/105293/showart_2132341.html
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號(hào)-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號(hào):11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP