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

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

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

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

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

第三篇 Linux 驅動開發高級技術
----------------------------------------------------------------
第11章 Linux 驅動程序中的并發控制

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

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

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

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

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

        示例:
                /* 一般定義為全局變量 */
                atomic_t n = ATOMIC_INIT(3);                // 將變量 n.counter 的初始值設為 3 --> n.counter = 3
                n.counter = 10;                                                // 這種寫法沒有意義,它并不是原子操作。
                atomic_set(&n, 2);                                        // 將變量 n.counter 的初始值設為 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位整型原子操作的其他的函數(列出,方便查詢):
                        ATOMIC_INIT(int i)                                                                宏 用 i 初始化 atomic_t 類型的變量
                        int        atomic_read(atomic_t *v)                                        宏 讀 v 的值
                        void atomic_set(atomic_t *v, int i);                         宏 設 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 后的結果
                        int atomic_sub_return(int i, atomic_t *v);                 函 將 v 的值減 i,并返回 -i 后的結果
                        int atomic_inc_return(atomic_t *v);                         宏 將 v 的值加 1,并返回 +1 后的結果
                        int atomic_dec_return(atomic_t *v);                         宏 將 v 的值減 1,并返回 -1 后的結果
                        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);                         宏 設 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位下執行不會有問題,但是64位整型原子操作在32位系統下執行會造成難以預料的后果。
                為了讓自己的驅動程序通用,若非必要則盡量使用32位整型原子操作。
               
        位原子操作:
                這種操作的數據類型是 unsigned long, 32位系統下為32bit,64位系統下為64bit。
                位原子操作函數主要功能是將 unsigned long 變量中的指定位設為0或設為1。
               
        示例:
                unsigned long value = 0;
                // 設置 value 的第0位為1, value = 0000000000000000 0000000000000001
                set_bit(0, &value);       
                // 設置 value 的第2位為1, value = 0000000000000000 0000000000000101       
                set_bit(2, &value);       
                // 設置 value 的第0位為0, value = 0000000000000000 0000000000000100
                clear_bit(0, &value);
                // 將 value 的第0位取反,第0位為1則設為0,為0則設為1
                change_bit(0, &value);
               
        接口:都是宏
                void set_bit(int nr, void *addr);                        將addr的第nr位設為 1
                void clear_bit(int nr, void *addr);                 將addr的第nr位設為 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位設為 1,設置之前該位為1則返回非0值,否則返回0
                int test_and_clear_bit(int nr, void *addr); 將addr的第nr位設為 0,設置之前該位為1則返回非0值,否則返回0
                int test_and_change_bit(int nr, void *addr);將addr的第nr位設取反,設置之前該位為1則返回非0值,否則返回0
               
               
        總結:
                整型原子操作和位原子操作都是圍繞一個變量的操作做為原子操作。
                可以用來限定設備能被幾個進程操作,和作為計數器使用。
               
        實例:(例如操作打印機)
                #define DevNumber        1
                atomic_t v = ATOMIC_INIT(DevNumber);        // 限定1個
                // 打開打印機設備
                int OpenPrinterDevice(unsigned char *buf, unsigned int size)
                {
                        // 將v減1后,判斷v是否為0
                        if(atomic_dec_and_test(v))
                        {
                                // v 為 0,表示成功得到操作權限
                                return 0;
                        }
                        else
                        {
                                // 表示 設備已經被占用。
                                return -EBUSY;
                        }
                }
                // 釋放打印機設備
                void ClosePrinterDevice(void)
                {
                        atomic_set(&v, DevNumber);
                }

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

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



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

使用道具 舉報

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

本版積分規則

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

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 日韩在线免费视频 | 欧美精品一区二区三区四区 在线 | 久久久久久亚洲精品 | 欧美日本亚洲 | 精品一区精品二区 | 欧美精品一区在线 | 一级h片 | julia中文字幕久久一区二区 | 水蜜桃久久夜色精品一区 | 日韩a在线| 欧美一区二区三区久久精品 | 狠狠视频 | 久久777| 欧洲成人免费视频 | 国产免费一区二区三区 | 综合色导航 | 激情五月婷婷综合 | 久久青草av| 欧美视频中文字幕 | 在线成人免费视频 | 欧美色影院 | 在线视频成人 | 日本爱爱视频 | 亚洲国产精品一区二区三区 | 99精品电影 | 久久久成人精品 | www.se91| 在线日韩 | 在线一区二区三区 | 国产在线不卡视频 | 日本在线免费视频 | 日日夜夜免费精品视频 | 国产一区在线免费 | 午夜影院普通用户体验区 | 国产91丝袜在线18 | 成人av影院 | 精品久久久久久亚洲综合网 | 久久精品国产清自在天天线 | 久久久精品一区二区 | 久久国产一区 | 亚洲福利片 |