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

 找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開始

搜索
查看: 6871|回復(fù): 0
打印 上一主題 下一主題
收起左側(cè)

Android 深度探索(卷1)-學(xué)習(xí)筆記(原子操作、自旋鎖)

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:82083 發(fā)表于 2015-6-6 02:49 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
-----------------------------------------------------------------
工作之后,比較少時(shí)間看書學(xué)習(xí),也比較少時(shí)間做筆記。
買下這本書,雖然和自己預(yù)想的有些出入,但仍對(duì)自己有蠻大的幫助。
自己買了很多的書都只是看了一點(diǎn)就丟在一旁,浪費(fèi)啊。。。這次可不能浪費(fèi)了。。。
回首2014,發(fā)現(xiàn)自己獲得的很少,原因在于自己不懂充分利用好時(shí)間,所以今年需要充分利用好時(shí)間,更加的努力,吼吼吼,聚集正能量。。。
-----------------------------------------------------------------
   花了一個(gè)星期上下班的時(shí)間,看到了RCU機(jī)制,原以為今天下午能寫完 原子操作、自旋鎖、讀寫鎖、順序鎖、RCU的筆記,沒想到當(dāng)真正實(shí)際上機(jī)操作時(shí),發(fā)現(xiàn)那些自認(rèn)為已經(jīng)理解的知識(shí)點(diǎn)在機(jī)器上得不到證實(shí)。看來(lái)不能光看書,還得要多多實(shí)踐實(shí)踐。
-----------------------------------------------------------------

第三篇 Linux 驅(qū)動(dòng)開發(fā)高級(jí)技術(shù)
----------------------------------------------------------------
第11章 Linux 驅(qū)動(dòng)程序中的并發(fā)控制

驅(qū)動(dòng)程序是為上層服務(wù)的,一個(gè)驅(qū)動(dòng)程序可能會(huì)被多個(gè)應(yīng)用進(jìn)程同時(shí)使用。

一、原子操作:即不可再細(xì)分的操作,最小的執(zhí)行單位,在操作完之前都不會(huì)被任何事件中斷。

        整型原子操作:對(duì)int類型的操作變成原子操作。
                int i = 0;
                i = i + 2;        <--- 轉(zhuǎn)換為匯編時(shí),不止一條語(yǔ)句,所以可能會(huì)被中斷。

        數(shù)據(jù)類型:atomic_t 在 linux/types.h 中定義。
                typedef struct
                {
                        int counter;
                }atomic_t;

        atomic_t.counter的變量值變化就是原子的,當(dāng)然我們不能直接去讀寫這個(gè)變量值,要使用一些函數(shù)才能對(duì)它進(jìn)行操作,
        這些函數(shù)都是圍繞著 atomic_t.counter 變量的修改、獲取而設(shè)計(jì)的。

        示例:
                /* 一般定義為全局變量 */
                atomic_t n = ATOMIC_INIT(3);                // 將變量 n.counter 的初始值設(shè)為 3 --> n.counter = 3
                n.counter = 10;                                                // 這種寫法沒有意義,它并不是原子操作。
                atomic_set(&n, 2);                                        // 將變量 n.counter 的初始值設(shè)為 2 --> n.counter = 2
                atomic_add(5, &n);                                        // 將變量 n.counter 的值加上 5 --> n.counter += 5
                atomic_dec(&n);                                                // 將變量 n.counter 的值減 1 --> n.counter -= 1
                printk("n = %d", atomic_read(&n));        // 讀取變量 n.counter 的值 此時(shí) n.counter == 6

        接口:
                32位整型原子操作的其他的函數(shù)(列出,方便查詢):
                        ATOMIC_INIT(int i)                                                                宏 用 i 初始化 atomic_t 類型的變量
                        int        atomic_read(atomic_t *v)                                        宏 讀 v 的值
                        void atomic_set(atomic_t *v, int i);                         宏 設(shè) v 的值為 i
                        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 的值加 1
                        void atomic_dec(atomic_t *v);                                         宏 將 v 的值減 1

                        int atomic_sub_and_test(int i, atomic_t *v);        宏 將 v 的值減 i,(0==v) ? 非0值 : 0;
                        int atomic_inc_and_test(atomic_t *v);                        宏 將 v 的值加 1,(0==v) ? 非0值 : 0;
                        int atomic_dec_and_test(atomic_t *v);                        宏 將 v 的值減 1,(0==v) ? 非0值 : 0;
                        int atomic_add_negative(int i, atomic_t *v);        宏 將 v 的值加 1,(v<0) ? 非0值 : 0;
                        int atomic_add_return(int i, atomic_t *v);                函 將 v 的值加 i,并返回 +i 后的結(jié)果
                        int atomic_sub_return(int i, atomic_t *v);                 函 將 v 的值減 i,并返回 -i 后的結(jié)果
                        int atomic_inc_return(atomic_t *v);                         宏 將 v 的值加 1,并返回 +1 后的結(jié)果
                        int atomic_dec_return(atomic_t *v);                         宏 將 v 的值減 1,并返回 -1 后的結(jié)果
                        int atomic_add_unless(atomic_t *v, int a, int u);        涵  ( v!=u ) ? v+a,返回非0值 : 0;
                        int atomic_inc_not_zero(atomic_t *v);                        宏 ( v!=0 ) ? v+1,返回非0值 : 0;

                64位整型原子操作:和32位整型原子操作一致,所操作的接口只是名稱不同,功能一致。
                        ATOMIC64_INIT(int i)                                                        宏 用 i 初始化 atomic_t 類型的變量
                        int        atomic64_read(atomic_t *v)                                        宏 讀 v 的值
                        void atomic64_set(atomic_t *v, int i);                         宏 設(shè) v 的值為 i
                        void atomic64_add(int i, atomic_t *v);                         宏 將 v 的值加 i
                        void atomic64_sub(int i, atomic_t *v);                        宏 將 v 的值減 i
                        void atomic64_inc(atomic_t *v);                                        宏 將 v 的值加 1
                        void atomic64_dec(atomic_t *v);                                 宏 將 v 的值減 1
                        ...
                        ...

        注意:
                32位整型原子操作在64位下執(zhí)行不會(huì)有問題,但是64位整型原子操作在32位系統(tǒng)下執(zhí)行會(huì)造成難以預(yù)料的后果。
                為了讓自己的驅(qū)動(dòng)程序通用,若非必要?jiǎng)t盡量使用32位整型原子操作。
               
        位原子操作:
                這種操作的數(shù)據(jù)類型是 unsigned long, 32位系統(tǒng)下為32bit,64位系統(tǒng)下為64bit。
                位原子操作函數(shù)主要功能是將 unsigned long 變量中的指定位設(shè)為0或設(shè)為1。
               
        示例:
                unsigned long value = 0;
                // 設(shè)置 value 的第0位為1, value = 0000000000000000 0000000000000001
                set_bit(0, &value);       
                // 設(shè)置 value 的第2位為1, value = 0000000000000000 0000000000000101       
                set_bit(2, &value);       
                // 設(shè)置 value 的第0位為0, value = 0000000000000000 0000000000000100
                clear_bit(0, &value);
                // 將 value 的第0位取反,第0位為1則設(shè)為0,為0則設(shè)為1
                change_bit(0, &value);
               
        接口:都是宏
                void set_bit(int nr, void *addr);                        將addr的第nr位設(shè)為 1
                void clear_bit(int nr, void *addr);                 將addr的第nr位設(shè)為 0
                void change_bit(int nr, void *addr);                將addr的第nr位取反
                int test_bit(int nr, void *addr);                        如果addr的第nr位為1則返回非0值,否則返回0
                int test_and_set_bit(int nr, void *addr);        將addr的第nr位設(shè)為 1,設(shè)置之前該位為1則返回非0值,否則返回0
                int test_and_clear_bit(int nr, void *addr); 將addr的第nr位設(shè)為 0,設(shè)置之前該位為1則返回非0值,否則返回0
                int test_and_change_bit(int nr, void *addr);將addr的第nr位設(shè)取反,設(shè)置之前該位為1則返回非0值,否則返回0
               
               
        總結(jié):
                整型原子操作和位原子操作都是圍繞一個(gè)變量的操作做為原子操作。
                可以用來(lái)限定設(shè)備能被幾個(gè)進(jìn)程操作,和作為計(jì)數(shù)器使用。
               
        實(shí)例:(例如操作打印機(jī))
                #define DevNumber        1
                atomic_t v = ATOMIC_INIT(DevNumber);        // 限定1個(gè)
                // 打開打印機(jī)設(shè)備
                int OpenPrinterDevice(unsigned char *buf, unsigned int size)
                {
                        // 將v減1后,判斷v是否為0
                        if(atomic_dec_and_test(v))
                        {
                                // v 為 0,表示成功得到操作權(quán)限
                                return 0;
                        }
                        else
                        {
                                // 表示 設(shè)備已經(jīng)被占用。
                                return -EBUSY;
                        }
                }
                // 釋放打印機(jī)設(shè)備
                void ClosePrinterDevice(void)
                {
                        atomic_set(&v, DevNumber);
                }

二、自旋鎖
        原子操作可以讓指定變量的操作是原子的。很多時(shí)候我們?cè)谔幚硪恍⿺?shù)據(jù)執(zhí)行某些動(dòng)作的時(shí)候要保證執(zhí)行過程中
        不能被中斷,要求是原子的,而整型、位原子操作要實(shí)現(xiàn)這種需求就會(huì)比較復(fù)雜一些。而使用自旋鎖則簡(jiǎn)單很多。
       
        示例:
                /* 一般定義為全局變量 */
                spinlock_t lock;                // 定義一把自旋鎖
                spin_lock_init(&lock);        // 初始化這把自旋鎖
                或者使用宏來(lái)定義并初始化 DEFINE_SPINLOCK(lock)
                void MyLock()
                {               
                        /* 使用場(chǎng)合:中斷下半部與中斷服務(wù)程序不會(huì)進(jìn)入臨界區(qū) */
                        spin_lock(&lock);                // 獲取并上鎖   
                        // ...                                        <--- 關(guān)閉了內(nèi)核的搶占,但仍受硬中斷和中斷下半部的影響
                        // 臨界區(qū)代碼                
                        // ...
                        spin_unlock(&lock);                // 釋放解鎖,恢復(fù)內(nèi)核的搶占       
                }
               
               
                若中斷處理函數(shù)中需要訪問上面的臨界區(qū),當(dāng)lock鎖未被釋放,同時(shí)中斷產(chǎn)生:
                void MyIRQ(void)                        // 產(chǎn)生中斷
                {
                        spin_lock(&lock);                // 由于該鎖未被釋放,所以中斷服務(wù)參數(shù)就會(huì)一直自旋(雙重請(qǐng)求)
                        // ...                                    // 而中斷服務(wù)未退出 就無(wú)法退回MyLock(),就無(wú)法釋放鎖造成死鎖                                
                        // 臨界區(qū)代碼                
                        // ...
                        spin_unlock(&lock);                // 釋放解鎖,恢復(fù)內(nèi)核的搶占
                }
               
                這種情況下需要采用以下的方式上鎖:
                void MyLockIrq()
                {
                        /*使用場(chǎng)合:
                                1、中斷服務(wù)函數(shù)與中斷下半部都需要進(jìn)入該臨界區(qū)
                                2、中斷服務(wù)函數(shù)需要進(jìn)入該臨界區(qū)
                        */
                        spin_lock_irq(&lock);        // 獲取并上鎖 臨界區(qū)的內(nèi)容可能會(huì)被
                        // ...                                        <--- 關(guān)閉了內(nèi)核的搶占以及硬件中斷響應(yīng),軟中斷依賴硬件中斷,自然也不生效。
                        // 臨界區(qū)代碼                        
                        // ...
                        spin_unloc_irq(&lock);        // 釋放解鎖,恢復(fù)內(nèi)核的搶占以及硬件中斷,中斷下半部也有效
                }
               
                如果訪問臨界區(qū)的資源的代碼不是放在中斷服務(wù)函數(shù)中,而是放在中斷下半部也會(huì)出現(xiàn)相似的情況,
                即在MyLock()上鎖之后,產(chǎn)生一個(gè)硬件中斷,當(dāng)執(zhí)行完中斷服務(wù)函數(shù)之后就可能會(huì)繼續(xù)執(zhí)行中斷下
                半部的代碼,因?yàn)樗梢該屨歼M(jìn)程上下文,而低半部要獲取的鎖已經(jīng)被MyLock()上鎖,形成死鎖。
               
                這種情況也可以用void MyLockIrq()這種方式,但是最好用一下方式,更快:
                void MyLockBh()
                {
                        /* 使用場(chǎng)合:中斷下半部與進(jìn)程上下文都需要進(jìn)入該臨界代碼 */
                        spin_lock_bh(&lock);        // 獲取并上鎖
                        // ...                                        <--- 關(guān)閉了內(nèi)核的搶占以及中斷下半部,但受硬件中斷影響
                        // 臨界區(qū)代碼
                        // ...
                        spin_unloc_bh(&lock);        // 釋放解鎖,恢復(fù)內(nèi)核的搶占以及中斷下半部
                }
               
                對(duì)于一個(gè)CPU的機(jī)器來(lái)說:當(dāng)有A、B進(jìn)程都要執(zhí)行臨界區(qū)的代碼時(shí),假設(shè)A先獲得鎖之后,B進(jìn)程不會(huì)被調(diào)度,
                系統(tǒng)呈現(xiàn)假死狀態(tài),        只有當(dāng)A釋放鎖之后,B進(jìn)程才會(huì)被調(diào)度再去獲取鎖,此時(shí)A已經(jīng)釋放鎖,所以B也就順利得到鎖。
                對(duì)于兩個(gè)CPU的機(jī)器來(lái)說:當(dāng)有A、B進(jìn)程都要執(zhí)行臨界區(qū)的代碼時(shí),假設(shè)A先獲得鎖之后,B進(jìn)程也會(huì)去獲取鎖,
                但是鎖已經(jīng)被A得到,那么B進(jìn)程則會(huì)一直不停的循環(huán)檢測(cè)鎖是否被釋放,此時(shí)系統(tǒng)會(huì)呈現(xiàn)假死狀態(tài)。
                (這些現(xiàn)象可以在VM虛擬機(jī)上驗(yàn)證,VM虛擬機(jī)可以調(diào)整CPU個(gè)數(shù))
               
                A進(jìn)程:
                        spin_lock(&lock);                // 獲取并上鎖  
                        // ...
                        // 臨界區(qū)代碼                         <--- A在執(zhí)行臨界區(qū)代碼 ---cpu0
                        // ...
                        spin_unlock(&lock);                // 釋放解鎖
                       
                B進(jìn)程:
                        spin_lock(&lock);                // <--- 阻塞這里,一直在spin_lock內(nèi)部不停的循環(huán)等待 ---cpu1
                        // ...
                        // 臨界區(qū)代碼
                        // ...
                        spin_unlock(&lock);                // 釋放解鎖
               
                也就說,只有一個(gè)進(jìn)程能進(jìn)入臨界區(qū),其他進(jìn)程要想進(jìn)入臨界區(qū)只能自己在原地循環(huán)旋轉(zhuǎn)等待。
               

        使用注意事項(xiàng):
                1、自旋鎖實(shí)際上是忙等待,因?yàn)樵诘却i的時(shí)候是在不停的循環(huán)等待,長(zhǎng)時(shí)間占用鎖會(huì)極大降低系統(tǒng)性能。
                2、要避免在臨界區(qū)中調(diào)用可能會(huì)產(chǎn)生睡眠的函數(shù),因?yàn)榇藭r(shí)搶占、中斷已經(jīng)關(guān)閉,無(wú)法被喚醒導(dǎo)致無(wú)法解鎖。
                3、若數(shù)據(jù)被軟中斷共享,也需要加鎖,因?yàn)樵诓煌幚砥魃洗嬖谲浿袛嗤瑫r(shí)執(zhí)行問題。
                4、注意避免死鎖,例如上述例子,A進(jìn)程獲得了鎖之后,又繼續(xù)獲取該鎖,因?yàn)樵撴i已經(jīng)被A獲取,
                        所以該鎖無(wú)法再次被A獲取,A就會(huì)一直循環(huán)打轉(zhuǎn)等待,A沒有機(jī)會(huì)釋放該鎖,該CPU被鎖死,
                        對(duì)于多顆CPU來(lái)說,其他進(jìn)程又無(wú)法釋放該鎖,形成死循環(huán),導(dǎo)致死機(jī)。
               
                        A進(jìn)程:
                        spin_lock(&lock);                // 獲取并上鎖  關(guān)閉了內(nèi)核的搶占
                        spin_lock(&lock);                // <--- 阻塞這里,一直在spin_lock內(nèi)部不停的循環(huán) cpu被鎖死                
                        // ...
                        // 臨界區(qū)代碼                         <--- 無(wú)法得到執(zhí)行
                        // ...
                        spin_unlock(&lock);                // 沒有機(jī)會(huì)釋放
               
               



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

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表
主站蜘蛛池模板: 日韩a在线| 国产精品久久久久久久久久妞妞 | 欧美日韩在线免费 | 亚洲欧美综合 | 午夜手机在线 | 欧美一级免费 | 久久99深爱久久99精品 | 91 久久 | 性一爱一乱一交一视频 | 欧美老少妇一级特黄一片 | 免费久久99精品国产婷婷六月 | 秋霞电影院午夜伦 | 白浆在线 | 欧美二区三区 | 欧美激情精品久久久久久免费 | 久久精品中文字幕 | 免费a v网站 | 日韩免费一区 | 日韩精品区| 亚洲成人福利在线观看 | www.97国产 | 国产精品99久久久久久宅男 | av日韩一区 | 欧美区在线观看 | 亚洲欧美综合精品另类天天更新 | 日韩成人精品一区 | www.激情.com | 在线一区二区国产 | 精品一区二区在线观看 | 亚洲精品精品 | 亚洲综合99| 午夜影院在线观看 | 美女久久久久久久久 | 欧美成视频 | 国产精品黄视频 | 日本特黄a级高清免费大片 国产精品久久性 | 欧美日韩综合 | 亚洲综合在线播放 | 国产日韩一区二区三区 | 97视频免费 | 99热精品6 |