新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > arm驅動linux并發與競態并發控制

        arm驅動linux并發與競態并發控制

        作者: 時間:2016-11-19 來源:網絡 收藏
        《[arm驅動]linux并發與競態---并發控制》涉及內核驅動函數五個,內核結構體一個,分析了內核驅動函數六個;可參考的相關應用程序模板或內核驅動模板五個,可參考的相關應用程序模板或內核驅動零個

        一、并發與競態

        本文引用地址:http://www.104case.com/article/201611/318220.htm

        1、并發:多個執行單元同時被執行。例如:同一個test.out可執行程序被n次同時運行
        2、競態:并發的執行單元對共享資源(硬件資源和軟件上的全局變量,靜態變量等)的訪問導致的競爭。
        a)靜態的列子:

        char *p;//全局變量
        // 讀取函數
        module_drv_read(struct file *file, constchar __user *buf, size_t count, loff_t * ppos)
        {copy_to_user(buf, p, countt);}
        //寫入函數
        module_drv_write(struct file *file, constchar __user *buf, size_t count, loff_t * ppos)
        {copy_from_user(p, buf, countt)}

        如果有兩個進程同時在運行,一個執行read,一個執行write,那么執行read的可能會讀取到第二個程序的寫入值或只讀取到第二個程序部分寫入值。
        二、如何避免競態:(有共享就有可能發生競爭)
        方法:處理競態的常用技術是加鎖或者互斥,對應semaphore(旗標)機制、spin_lock機制
        三、信號量(semaphore)機制

        1、旗標(信號量)

        旗標(信號量):是一個整型值,結合一對函數void down(struct semaphore * sem)、void up(struct semaphore *sem),如果旗標的值大于0,這個值將減一并且繼續進程。相反,如果旗標的值是0(或者更小),此進程必須等待別的進程釋放旗標,解鎖旗標通過調用up完成,up函數會增加旗標的值。
        Tip:Linux內核的信號量在概念和原理上與用戶態的信號量是一樣的,但它不能在內核之外使用

        2、信號量機制編程

        a)信號的聲明初始化有兩種方式:方式1宏定義并初始化;方式2動態初始化

        信號的聲明初始化方式1)宏定義并初始化信號量的快捷方式

        DECLARE_MUTEX(name)
        DECLARE_MUTEX_LOCKED(name)

        內核代碼1)DECLARE_MUTEX和DECLARE_MUTEX_LOCKED他們的內核代碼為

        #define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name, 1)
        #define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name, 0)
        //__DECLARE_SEMAPHORE_GENERIC的內核代碼
        #define __DECLARE_SEMAPHORE_GENERIC(name,count)
        structsemaphore name = __SEMAPHORE_INITIALIZER (name,count)

        模板一)宏定義初始化方式信號量模板

        DECLARE_MUTEX(name);//定義信號量
        down(&name);//加鎖,保護臨界區在釋放鎖前,將訪問本臨界區的進程加入等待隊列中
        //....要修改全局變量在這修改.....
        up(&name)//釋放鎖

        信號的聲明初始化方式2)動態初始化方式

        1、定義信號量結構體 struct semaphore sem;

        structsemaphore sem;

        結構體一)struct semaphore內核源碼

        struct semaphore {
        atomic_t count;//旗號數值
        intsleepers;
        wait_queue_head_t wait;//等待隊列
        };

        2、初始化結構體struct semaphore sem有兩種方式,宏定義初始化與函數初始化

        初始化結構體sem方式a)用函數sema_init (struct semaphore *sem, int val)初始化結構體struct semaphore sem。

        內核源碼二)sema_init內核原型代碼

        static inline void sema_init (structsemaphore *sem, intval)
        {
        atomic_set(&sem->count, val);
        init_waitqueue_head(&sem->wait);
        //如果進程調用down時,發現信號量為0時就將進程加入到sem->wait等待隊列中
        }

        模板二)sema_init初始化模板

        struct semaphore sem;
        sema_init (&sem, 1);一開始就將旗標置1
        down(&sem);
        up(&sem);

        初始化結構體sem方式b)宏初始化,分為將sem的旗標直接初始化為1和0的兩種宏初始化方法

        1)init_MUTEX (struct semaphore *sem)將semaphore的旗標初始化為1

        內核源碼三)init_MUTEX內核原型代碼

        staticinline voidinit_MUTEX (struct semaphore *sem)
        {
        sema_init(sem, 1);//可以看出調用了上面的sema_init函數
        // 該函數用于初始化一個互斥鎖,即它把信號量sem的值設置為1
        }

        2)init_MUTEX_LOCKED (struct semaphore *sem)將semaphore的旗標初始化為0

        內核源碼四)init_MUTEX_LOCKED內核原型代碼

        staticinlinevoidinit_MUTEX_LOCKED (structsemaphore *sem)
        {
        sema_init(sem, 0);
        //該函數也用于初始化一個互斥鎖,但它把信號量sem的值設置為0,即一開始就處在已鎖狀態。
        }

        模板三)宏初始化模板變量sem

        struct semaphore sem;
        init_MUTEX(&sem);一開始就將旗標置1
        down(&sem);
        up(&sem);

        四、自旋鎖
        1、 概念:自旋鎖最多只能被一個可執行單元持有。自旋鎖不會引起調用者睡眠,如果一個執行線程試圖獲得一個已經被持有的自旋鎖,那么這個線程就會一直進行忙循環,一直等待下去,在那里看是否該自旋鎖的保持者已經釋放了鎖,“自旋”就是這個意思
        2、自旋鎖的編程步驟a,b,c,d

        a)聲明一個spinlock_t 變量lock

        spinlock_t lock;

        b)初始化spinlock_t 變量lock

        有兩種初始化方式:宏初始化與函數初始化

        1)宏初始化方法SPIN_LOCK_UNLOCKED

        spinlock_t lock = SPIN_LOCK_UNLOCKED;

        2)函數初始化方法spin_lock_init(lock)

        spin_lock_init(lock)

        內核源碼五)spin_lock_init的內核源碼

        //內核源碼spin_lock_init發現使用SPIN_LOCK_UNLOCKED
        #define spin_lock_init(lock) do{ *(lock) = SPIN_LOCK_UNLOCKED; } while(0)

        c)獲得自旋鎖

        1、獲得自旋鎖,spin_lock(lock) ;如果成功,立即獲得鎖,并馬上返回,否則它將一直自旋在那里,直到該自旋鎖的保持者釋放。

        spin_lock(lock)
        //獲取自旋鎖lock,如果成功,立即獲得鎖,并馬上返回,否則它將一直自旋在那里,直到該自旋鎖的保持者釋放。

        內核源碼六)spin_lock(lock)內核源碼分析

        //源碼內核(可以看出來spin_lock沒有獲取鎖,它將一直做while循環)
        spin_lock(lock) _spin_lock(lock)
        #define _spin_lock(lock) __LOCK(lock)
        #define __LOCK(lock) do{ preempt_disable(); __acquire(lock); (void)(lock); } while(0)
        #define preempt_disable() do{ } while(0)
        //preempt_disable做空循環

        2、試圖獲取自旋鎖函數的spin_trylock(lock),如果能立即獲得鎖,并返回真,否則立即返回假。它不會一直等待被釋放。(spin_trylock(lock)不做忙等待)
        d)釋放自旋鎖lock,函數spin_unlock(lock) 它與spin_trylock或spin_lock配對使用。

        3、自旋鎖spinlock使用模板

        模板四)自玄鎖模板

        spinlock_t lock;
        spin_lock_init(&lock);
        spin_lock(&lock);
        spin_unlock(&lock);

        Tip:將要保護的值(全局變量,靜態變量,硬件資源)放在spin_lock(&lock)與spin_unlock(&lock)之間操作

        spin_lock(&lock);
        //改變要保護的全局變量或硬件資源或靜態變量(共享變量)
        spin_unlock(&lock);

        4、特殊使用場景:有些設備只允許被打開一次,那么就需要一個自旋鎖保護表示設備的打開和關閉狀態的變量count。此處count屬于臨界資源,如果不對count進行保護,當設備打開頻繁時,可能出現錯誤的count計數。

        模板五)只允許被打開一次的設備驅動模板

        intcount = 0;
        spinlock_t lock;
        intxxx_init(void){
        //...其他代碼..
        spin_lock_init(&lock);
        //...其他代碼..
        }
        intxxx_open(structinode *inode, structfile *file){
        spin_lock(&lock);//如果相同程序的進程要用open,那么其他進程就進入忙等到while(1)
        if(count){
        spin_unlock(&lock);
        return-EBUSY;
        }
        count++;
        spin_unlock(&lock);
        //.....
        }
        intxxx_release(structinode *inode, structfile *file){
        //....
        spin_lock(&lock);//關閉時也要進行spin_lock保護,因為count是全局變量,要改變count就要加自旋鎖
        count--;
        spin_unlock(&lock);
        //.....
        }

        Tip:linux自旋鎖和信號量所采用的"加鎖(down)---訪問臨界區(critical section)---釋放鎖"的方式,被稱為"互斥三部曲"。

        五、總結:編寫對共享資源(硬件資源和軟件上的全局變量,靜態變量等)的訪問的驅動程序場景,就要考慮避免競態的發生;避免方法:信號量(或說旗標,semaphore)機制與spin_lock機制(自旋鎖)。

        六、補充:信號量與自旋鎖對比
        信號量可能允許有多個持有者,而自旋鎖在任何時候只能允許一個持有者。當然也有信號量叫互斥信號量(只能一個持有者),允許有多個持有者的信號量叫計數信號量。
        信號量適合于保持時間較長的情況;而自旋鎖適合于保持時間非常短的情況,在實際應用中自旋鎖控制的代碼只有幾行,而持有自旋鎖的時間也一般不會超過兩次上下文切換的時間,因為線程一旦要進行切換,就至少花費切出切入兩次,自旋鎖的占用時間如果遠遠長于兩次上下文切換,我們就應該選擇信號量。



        評論


        技術專區

        關閉
        主站蜘蛛池模板: 绥滨县| 麻城市| 汝阳县| 太保市| 子洲县| 大英县| 嘉义市| 和龙市| 崇礼县| 永定县| 陆河县| 都匀市| 色达县| 凯里市| 潼南县| 新郑市| 建瓯市| 常德市| 闽清县| 阿鲁科尔沁旗| 延津县| 巴林左旗| 台东市| 宁乡县| 凌源市| 大足县| 临城县| 金阳县| 句容市| 宣威市| 呼玛县| 巴东县| 扎鲁特旗| 仁化县| 富源县| 八宿县| 凉山| 宁武县| 搜索| 马公市| 门源|