久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 4457|回復: 0
打印 上一主題 下一主題
收起左側

Android 深度探索(卷1)-學習筆記2(讀寫、順序鎖、信號量)

[復制鏈接]
跳轉到指定樓層
樓主
ID:82083 發表于 2015-6-6 02:50 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
一、讀寫鎖
使用自旋鎖時,無論什么時候都只有一個可執行單元進入臨界區,無論該執行單元是讀操作還是寫操作,大多數情況下我們讀操作會多于寫操作,
多個執行單元進入臨界區進行讀操作是不會有問題的,如果仍采用自旋鎖就會有效率低下的問題,而采用讀寫鎖的話就會大大的提升效率。


讀寫鎖,從自旋鎖中衍生而出,分讀自旋鎖和寫自旋鎖。
1、多個執行單元可以同時獲取到讀鎖并訪問臨界區的資源(或代碼),但只能獲取一個寫自旋鎖。
2、如果某些執行單元已經獲取到讀鎖仍未釋放該鎖,這時B執行單元去獲取寫鎖,B執行單元就會阻塞,直到所有讀鎖被釋放。
3、如果有個A執行單元獲取到寫鎖仍未釋放該鎖,這時B執行單元去獲取寫鎖,B執行單元就會阻塞,直到A寫鎖被釋放。
4、如果有個A執行單元獲取到寫鎖仍未釋放該鎖,這時B執行單元去獲取讀鎖,B執行單元就會阻塞,直到A寫鎖釋放。


無論是讀鎖還是寫鎖,都會對臨界區加鎖:
讀鎖:讀鎖不會互斥讀鎖,但會互斥寫鎖,也就是說可以重復獲取讀鎖(讀鎖上鎖期間不應修改共享資源)
寫鎖:寫鎖會互斥寫鎖和讀鎖,也就是說在上了寫鎖期間,其他執行單元無法獲取到寫鎖和讀鎖
(讀鎖上鎖期間不允許修改共享資源,寫鎖上鎖期間只允許一個執行單元修改共享資源。)



使用示例:
// 定義并初始化讀寫自旋鎖
static DEFINE_RWLOCK(rwlock);


static ssize_t demo_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
// ......

read_lock(&rwlock);// 加了讀鎖,為了互斥寫鎖
// ..
// 讀臨界區這里可以并發進入臨界區不會阻塞,提高效率
// ..
read_unlock(&rwlock);

// ......
}


static ssize_t demo_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
// .......

write_lock(&rwlock); //加了寫鎖,為了互斥讀鎖和寫鎖
// ..
// 修改臨界區 這里只允許有一個執行單元在所有讀鎖釋放后進入
// ..
write_unlock(&rwlock);

// .......
}


優點:在沒有寫操作時可以并發讀操作,不會被阻塞。
缺點:當執行寫操作時,所有讀操作都會被阻塞。


讀寫鎖僅適合在讀多寫少并且臨界區很短的情況下。


一些接口:
DEFINE_RWLOCK(lock)
void rwlock_init(rwlock_t *lock)


int read_trylock(rwlock_t *lock)
void read_lock(rwlock_t *lock)
void read_unlock(rwlock_t *lock)
void read_lock_irq(rwlock_t *lock)
void read_unlock_irq(rwlock_t *lock)
void read_lock_bh(rwlock_t *lock)
void read_unlock_bh(rwlock_t *lock)


int write_trylock(rwlock_t *lock)
void write_lock(rwlock_t *lock)
void write_unlock(rwlock_t *lock)
void write_lock_irq(rwlock_t *lock)
void write_unlock_irq(rwlock_t *lock)
void write_lock_bh(rwlock_t *lock)
void write_unlock_bh(rwlock_t *lock)




二 順序鎖
順序鎖則是自旋鎖的另外一個升級版,和讀寫鎖有些相似。順序鎖主要是圍繞順序鎖和順序號來設計的。


typedef struct{
unsigned sequence;// 順序鎖的順序計數器-順序號
spinlock_t lock;// 自旋鎖
}seqlock_t;


順序鎖:順序鎖未被釋放時,獲取順序鎖的執行單元會阻塞(自旋)
在需要修改共享資源(臨界區)的時候獲取順序鎖,成功獲取順序號+1
當完成共享資源的操作后釋放順序鎖,順序號+1


順序號:順序鎖未被釋放時,獲取順序號的執行單元會阻塞(自旋)
在讀取共享資源時需要先取得順序號,只有在順序鎖釋放的情況下才會得到順序號,該順序號一定是偶數。
完成共享資源的讀取后再次取得順序號并對比之前獲取的順序號,若不一致則需要重新讀取共享資源。

成功獲取順序鎖時順序號一定是奇數,釋放順序鎖時一定是偶數。之所以有這種規律跟順序鎖實現有關。


// 實現源碼,獲取順序號的過程中并沒有上鎖操作
static _always_inline unsigned read_seqbegin(const seqlock_t *sl)
{
unsigned ret;
repeat:
ret = sl->sequence; // 讀取順序號
smp_rmb(); // 讀內存屏障

// 如果順序號是奇數則循環檢測(自旋)否則返回順序號
if(unlikely(ret & 1))
{
cpu_relax();
goto repeat;
}
return ret;
}



使用示例:
// 定義并初始化讀寫自旋鎖
static DEFINE_SEQLOCK(seqlock);


static ssize_t demo_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
// ......
// -------------------- 關鍵代碼 ----------------------------
unsigned seq;
do
{// 獲取順序號,如果順序鎖未被釋放 將會被阻塞(自旋)
seq = read_seqbegin(&seqlock); // 成功返回的順序號一定是偶數
// ...
// 臨界區
// ...
//如果當前順序號和seq一致,則退出循環,否則重新讀取共享資源
}while(read_seqretry(&seqlock, seq));
// -------------------- 關鍵代碼 ----------------------------
// ......
}


static ssize_t demo_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
// .......
// 獲取順序鎖  seqlock.sequence 會被+1  這時 seqlock.sequence 會是奇數
write_seqlock(&seqlock);
// ..
// 修改臨界區 這里只允許有一個執行單元在所有讀鎖釋放后進入
// ..
// 釋放順序鎖 seqlock.sequence 會被+1 這時 seqlock.sequence 會是偶數
write_sequnlock(&seqlock);

// .......
}


順序鎖的讀臨界區操作并沒有鎖定臨界區,只是簡單讀取臨界區中的共享資源,所以在讀取共享數據時
是可以修改共享數據的。


優點:讀取到的數據一定是最新的數據,在沒有寫操作時讀操作不會阻塞并且可以并發讀。
缺點:如果在讀共享數據的過程中發生了寫操作,就會使得系統不斷的地循環等待(自旋)和重新執行讀臨界區數據。


順序鎖僅適合在讀多寫少、臨界區很短并要求數據實時更新情況下。


一些接口:
DEFINE_SEQLOCK(lock)
void seqlock_init(seqlock_t *lock)
// 以下接口在成功執行后順序號會+1
int write_tryseqlock(seqlock_t *lock)
void write_seqlock(seqlock_t *lock)
void write_seqlock_irqsave(seqlock_t *lock)
void write_seqlock_irq(lock)
void write_seqlock_bh(lock)
void wrtie_sequnlock(seqlock_t *lock)
void write_sequnlock_irqrestore(lock, flags)
void write_sequnlock_irq(lock)
void write_sequnlock_bh(lock)
// 以下接口不會修改順序號
unsigned read_seqbegin(const seqlock_t *lock)
void read_seqbegin_irqsave(lock, flags)
int read_seqretry(const seqlock_t *lock, unsigned iv)
void read_seqretry_irqrestore(lock, iv, flags)


三、信號量


信號量和鎖機制最大的區別就是實現阻塞的方式不一樣。
自旋鎖、讀寫鎖、順序鎖都是通過不斷循環檢測實現阻塞,當臨界區很短時效率很高,
當臨界區很長的時候性能就會急劇下降。而信號量則是通過休眠方式實現阻塞,適合
臨界區比較長的情況,休眠不會占用CPU資源,所以不會影響系統性能更不會出現死機現象。


信號量比鎖機制要靈活很多,它可以指定可以有幾個執行單元進入臨界區。


1、信號量數據結構

struct semaphore sem;


struct semaphore {
raw_spinlock_tlock;// 鎖
// 資源數 決定可以有幾個執行單元進入臨界區
// >0,資源空閑. ==0,資源忙  
// <0 資源不可用,并至少有一個進程等待資源
unsigned intcount;
struct list_headwait_list;// 鏈表
};


2、初始化

sema_init(&sem, 1);// 初始化信號量并指定 sem.count 的初始值(即資源數)


#define __SEMAPHORE_INITIALIZER(name, n)\
{\
.lock= __RAW_SPIN_LOCK_UNLOCKED((name).lock),\
.count= n,\  // <--- 這里設置了可用資源數目
.wait_list= LIST_HEAD_INIT((name).wait_list),\
}


#define DEFINE_SEMAPHORE(name)\
struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)


static inline void sema_init(struct semaphore *sem, int val)
{
static struct lock_class_key __key;
*sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0);
}


3、獲取信號量

中斷無法喚醒:
down(&sem); // 獲取信號量,若計數器的值小于或等于0則進入休眠,若大于0則遞減。
void down(struct semaphore *sem)
{
unsigned long flags;
// 上鎖
raw_spin_lock_irqsave(&sem->lock, flags);
if (likely(sem->count > 0))
sem->count--;// 計數器大于 0 表示資源可用則將資源數自減
else
__down(sem);// 計數器小于或等于0 表示資源忙則進入休眠狀態
// 解鎖
raw_spin_unlock_irqrestore(&sem->lock, flags);
}
EXPORT_SYMBOL(down);

static noinline void __sched __down(struct semaphore *sem)
{
__down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}

中斷可喚醒:返回非0值表示是由中斷喚醒,返回0值則表示成功獲取到信號量
int down_interruptible(struct semaphore *sem)
{
unsigned long flags;
int result = 0;
// 上鎖
raw_spin_lock_irqsave(&sem->lock, flags);
if (likely(sem->count > 0))
sem->count--;
else
result = __down_interruptible(sem);// 這里不一樣
// 解鎖
raw_spin_unlock_irqrestore(&sem->lock, flags);


return result;
}
EXPORT_SYMBOL(down_interruptible);


static noinline int __sched __down_interruptible(struct semaphore *sem)
{// 只是傳遞的參數不一樣 TASK_INTERRUPTIBLE
// MAX_SCHEDULE_TIMEOUT 表示永不超時
return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}


static inline int __sched __down_common(struct semaphore *sem, long state, long timeout)
{
struct task_struct *task = current;
struct semaphore_waiter waiter;
// 將當前進程加入到等待鏈表
list_add_tail(&waiter.list, &sem->wait_list);
waiter.task = task;
waiter.up = 0;// 將當前進程標記為休眠


for (;;) {
// 這里是為 down_interruptible() 準備
if (signal_pending_state(state, task))
goto interrupted;

// 這里是為 down_timeout() 準備
// 若超時被喚醒則跳出
if (timeout <= 0)
goto timed_out;

// 設置進程狀態
__set_task_state(task, state);

// 解鎖
raw_spin_unlock_irq(&sem->lock);
// 讓出CPU,此時執行到這里停止往下執行
timeout = schedule_timeout(timeout);
// 進程被喚醒時 再上鎖
raw_spin_lock_irq(&sem->lock);
// 判斷是否由  __up() 喚醒,如果是則表示資源可用返回0。
if (waiter.up)
return 0;
}


// 超時喚醒 將該進程從等待鏈表中刪除。返回非0值
timed_out:
list_del(&waiter.list);
return -ETIME;
// 中斷喚醒 將該進程從等待鏈表中刪除。返回非0值
interrupted:
list_del(&waiter.list);
return -EINTR;
}


4、釋放信號量

up(&sem); // 釋放信號量 若有進程在等待則喚醒,直到沒有進程再等待才將計數器自增


static noinline void __sched __up(struct semaphore *sem)
{
// 獲取等待鏈表中第一個進程
struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list, struct semaphore_waiter, list);
list_del(&waiter->list);    // 從等待鏈表中移除該進程
waiter->up = 1;    // 喚醒標志
wake_up_process(waiter->task);    // 喚醒該進程
}


void up(struct semaphore *sem)
{
unsigned long flags;


raw_spin_lock_irqsave(&sem->lock, flags);
if (likely(list_empty(&sem->wait_list)))
sem->count++;        // 如果沒有等待鏈表中沒有進程在等待 才自增 表示資源空閑
else
__up(sem);// 如果有進程等待則喚醒
raw_spin_unlock_irqrestore(&sem->lock, flags);
}
EXPORT_SYMBOL(up);


使用示例:
struct semaphore sem;
static ssize_t demo_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
// ......
// 獲取信號量
down(&sem);
// ...
// 臨界區
// ...
// 釋放信號量
up(&sem);
// ......
}


static ssize_t demo_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
// ......
// 獲取信號量
down(&sem);
// ...
// 臨界區
// ...
// 釋放信號量
up(&sem);
// ......
}


static int __init demo_init(void)
{
// 將信號量值初始化為 1 表示只允許一個執行單元進入臨界區
sema_init(&sem, 1);
}


接口:略


四、讀寫信號量


讀寫信號量與信號量之間的關系和讀寫自旋鎖與自旋鎖一樣。
讀寫信號量可以有多個執行單元獲得讀信號量從而并發讀操作,但只能有一個執行單元獲取到寫信號量。


使用示例:


struct rw_semaphore rw_sem;
static ssize_t demo_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
// ......
// 獲取讀信號量 如果有寫信號量未釋放則進入休眠
down_read(&rw_sem);
// ...
// 臨界區
// ...
// 釋放讀信號量
up_read(&rw_sem);
// ......
}


static ssize_t demo_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
// ......
// 獲取寫信號量 如果有寫信號量或所有讀信號量未全部釋放則進入休眠
down_write(&rw_sem);
// ...
// 臨界區
// ...
// 釋放寫信號量
up_write(&rw_sem);
// ......
}


static int __init demo_init(void)
{
// 將信號量值初始化為 1 表示只允許一個執行單元進入臨界區
init_rwsem(&rw_sem, 1);
}

接口:略


鎖機制適合在臨界區執行時間短的情況下。
信號量適合在臨界區執行時間較長的情況下。
鎖機制是采用不斷循環檢測,所以實時性、速度快。
信號量是采用休眠喚醒的方式,休眠喚醒需要時間,實時性和速度較慢。

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: av免费网站 | 亚洲欧美精品一区二区 | 夜夜肉她怀孕h周君彦 | 亚洲欧美成人 | 国产伦精品一区二区 | 欧美成人免费 | 日韩精品视频网站 | 久久在线播放 | 国产精品羞羞答答 | 成人欧美一区二区三区黑人孕妇 | 中文字幕免费视频 | 色综合视频在线观看 | 黄色免费观看视频 | 欧美色综合天天久久综合精品 | 亚洲大片在线观看 | 欧美三级三级三级爽爽爽 | 手机看片欧美 | 日韩精品第一页 | 四虎影视最新地址 | 一二三区视频 | 久草资源网 | 欧美在线天堂 | 欧美理伦| 黄色直接看| 欧美日韩激情 | 久久久精品一区二区三区 | 欧美一级欧美三级 | 少妇网址 | 久久伊人网站 | 亚洲第一av| 长河落日电视连续剧免费观看 | 三级视频在线播放 | 亚洲一区网站 | 天天舔天天干 | 欧美黄色一级视频 | 亚洲国产免费 | 伊人久久在线 | 亚洲综合在线播放 | 日本黄色免费视频 | 久久精品国产视频 | 伊人黄色|