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

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

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

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

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

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

驅(qū)動程序是為上層服務的,一個驅(qū)動程序可能會被多個應用進程同時使用。

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

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

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

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

        示例:
                /* 一般定義為全局變量 */
                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 的值 此時 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í)行不會有問題,但是64位整型原子操作在32位系統(tǒng)下執(zhí)行會造成難以預料的后果。
                為了讓自己的驅(qū)動程序通用,若非必要則盡量使用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é):
                整型原子操作和位原子操作都是圍繞一個變量的操作做為原子操作。
                可以用來限定設(shè)備能被幾個進程操作,和作為計數(shù)器使用。
               
        實例:(例如操作打印機)
                #define DevNumber        1
                atomic_t v = ATOMIC_INIT(DevNumber);        // 限定1個
                // 打開打印機設(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;
                        }
                }
                // 釋放打印機設(shè)備
                void ClosePrinterDevice(void)
                {
                        atomic_set(&v, DevNumber);
                }

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

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



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

使用道具 舉報

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

本版積分規(guī)則

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

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

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 国产精品夜夜春夜夜爽久久电影 | 亚洲精品一区二区三区蜜桃久 | 欧美精品一区三区 | 日韩欧美国产精品综合嫩v 一区中文字幕 | 伊人久久免费 | 玖操| 国产真实精品久久二三区 | 精品视频在线一区 | a级黄色网| 亚洲国产欧美一区 | av大片 | 亚洲成人av | 免费毛片网 | 91成人| 成人在线小视频 | 天天草天天射 | 91精品久久久久久久久久小网站 | 国产伦精品一区二区三区四区视频 | 国产精品亚洲成在人线 | 日韩在线日韩 | 亚洲综合日韩精品欧美综合区 | 一区二区三区中文字幕 | 中文精品久久 | 精品国产一区二区 | 国产午夜精品久久久 | 久久久久1| 99热这里有精品 | 精品亚洲一区二区三区四区五区 | 国产亚洲一区精品 | 日韩高清中文字幕 | 久久69精品久久久久久久电影好 | 亚洲成av片人久久久 | 免费成人高清在线视频 | 欧美中文在线 | 久久久免费精品 | av黄色在线观看 | 午夜在线影院 | 91.xxx.高清在线 | 欧美精品一区三区 | 国产精品日韩欧美一区二区三区 | 免费人成激情视频在线观看冫 |