亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 5287 | 回復(fù): 9
打印 上一主題 下一主題

[內(nèi)核同步] 原子操作如何保證原子性 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2013-09-07 16:24 |只看該作者 |倒序瀏覽
本帖最后由 ycnian 于 2013-09-07 16:29 編輯

大家好,我正在看內(nèi)核同步部分的代碼,但是沒有弄清楚原子操作到底是怎么回事。舉個例子來說,atomic_add()可以實現(xiàn)原子加操作,代碼如下。
static inline void atomic_add(int i, atomic_t *v)
{
        asm volatile(LOCK_PREFIX "addl %1,%0"
                     : "+m" (v->counter)
                     : "ir" (i));
}

其中LOCK_PREFIX定義如下:
#ifdef CONFIG_SMP
#define LOCK_PREFIX_HERE \
                ".section .smp_locks,\"a\"\n"   \
                ".balign 4\n"                   \
                ".long 671f - .\n" /* offset */ \
                ".previous\n"                   \
                "671:"
#define LOCK_PREFIX LOCK_PREFIX_HERE "\n\tlock; "
#else /* ! CONFIG_SMP */
#define LOCK_PREFIX_HERE ""
#define LOCK_PREFIX ""
#endif

先不考慮SMP了,只看單處理器的情況,這時候可以直接忽略掉LOCK_PREFIX。這種情況下,我對atomic_add()進行了匯編,結(jié)果如下:
00000000 <atomic_add>:
(第1條指令)   0:        55                      push   %ebp
(第2條指令)   1:        89 e5                   mov    %esp,%ebp
(第3條指令)   3:        8b 45 0c                mov    0xc(%ebp),%eax   // 這是v->counter的地址.
(第4條指令)   6:        8b 55 08                mov    0x8(%ebp),%edx   // 這是變量i的值
(第5條指令)   9:        8b 4d 0c                mov    0xc(%ebp),%ecx   // 可能是編譯器的問題,這條指令可以忽略
(第6條指令)   c:        01 10                   add    %edx,(%eax)      // 求和,并把結(jié)果寫到v->counter中.
(第7條指令)   e:        5d                      pop    %ebp
(第8條指令)   f:        c3                      ret

也就是說atomic_add()匯編的結(jié)果是8條指令。假設(shè)v的初始值是3,現(xiàn)在兩個進程分別執(zhí)行原子加操作: 進程1 atomic_add(2, v)    進程2 atomic_add(5, v)。
進程1先執(zhí)行,當(dāng)執(zhí)行完第5條指令后發(fā)生了進程切換,此時v中的值還是3。進程2接著執(zhí)行,且中間沒有發(fā)生進程切換,當(dāng)進程2執(zhí)行完畢后v的值變成了8,F(xiàn)在進程1接著執(zhí)行第6條指令 “add    %edx,(%eax)”。寄存器eax中保存的是v的地址,CPU將執(zhí)行三個操作:1.將v的值讀取到寄存器中(現(xiàn)在v的值已經(jīng)是8了),2.求和(結(jié)果是10),3.將v的值寫到內(nèi)存中。這樣的話,對于進程1來說,執(zhí)行atomic_add(2, v)前v的值是3,執(zhí)行后的值是10了,這還能叫原子操作嗎?

不知道是我的理解有誤,還是怎么回事?大家怎么理解的?非常感謝。我看的內(nèi)核版本是3.6.0。

論壇徽章:
0
2 [報告]
發(fā)表于 2013-09-07 16:53 |只看該作者
可不可以這么理解:這些原子操作不是針對進程的,而是針對整個系統(tǒng)的。雖然對于進程1來說atomic_add()的結(jié)果不對,但是對于整個系統(tǒng)來說,最后的結(jié)果是10,這是正確的。內(nèi)核中的原子操作是為了避免SMP系統(tǒng)中的異常情況,因為多個處理器可以同時操作內(nèi)存單元。比如進程1在CPU1上執(zhí)行,進程2在CPU2上執(zhí)行。這種情況下如果不加lock的話,add就不能認(rèn)為是原子操作了。因為add包含了讀、求和、寫三個步驟。進程1執(zhí)行讀、求和操作(結(jié)果是5),進程2執(zhí)行讀、求和操作(結(jié)果是8),進程1執(zhí)行寫操作(結(jié)果是5),進程2執(zhí)行寫操作(結(jié)果是8)。這樣的話對于整個系統(tǒng)來說,結(jié)果就錯了。因此SMP系統(tǒng)中需要使用lock。

論壇徽章:
0
3 [報告]
發(fā)表于 2013-09-07 18:27 |只看該作者
接著說我現(xiàn)在的理解。進程1執(zhí)行atomic_add(2, v)之前v的值是2,執(zhí)行之后v的值是10。這個結(jié)果看似錯了,其實沒有錯。我們看匯編后生成的8條指令,按照前面的例子,進程1執(zhí)行完第5條指令后發(fā)生了進程切換,但是其實這時候atomic_add(2, v)還沒有讀取v的值,前幾條指令只獲取了v的地址,可以看作一些準(zhǔn)備操作。當(dāng)進程再次切換回來后進程1接著執(zhí)行"add    %edx,(%eax)"。因為這時候v的實際值是8,進程1當(dāng)然要在v當(dāng)前值的基礎(chǔ)上加2,只要保證“讀數(shù)據(jù)->求和->寫數(shù)據(jù)”這個步驟不被其他進程或者其他CPU打攪就行了,這就叫原子操作。不過程序中需要注意:

.......
v = 3;
atomic_add(2, v);
// 現(xiàn)在v的值不一定是5
.......

論壇徽章:
0
4 [報告]
發(fā)表于 2013-09-07 18:50 |只看該作者
不過我又有新問題了,看另外一個函數(shù)int atomic_dec_and_test(atomic_t *v),這個函數(shù)對v進行減1操作。如果v減到0了,函數(shù)返回true,否則返回false。當(dāng)v減到0時表示系統(tǒng)中某個資源已經(jīng)空閑不用了,可以刪除這個資源了。這個函數(shù)匯編后的代碼如下:
00000000 <atomic_dec_and_test>:
(第1條指令)   0:        55                      push   %ebp
(第2條指令)   1:        89 e5                   mov    %esp,%ebp        // 寄存器ebp中保存棧幀
(第3條指令)   3:        53                      push   %ebx
(第4條指令)   4:        83 ec 10                sub    $0x10,%esp       // 這里留出16字節(jié)保存局部變量
(第5條指令)   7:        8b 45 08                mov    0x8(%ebp),%eax   // v的地址
(第6條指令)   a:        8b 55 08                mov    0x8(%ebp),%edx   // 忽略
(第7條指令)   d:        f0 ff 08                decl   (%eax)           // v--
(第8條指令)  10:        0f 94 c3                sete   %bl              // 如果ZF的值為1就將bl設(shè)置為1 (如果v的計數(shù)減到0了就將bl設(shè)置為1).
(第9條指令)  13:        88 5d fb                mov    %bl,-0x5(%ebp)   // 將bl的值保存到堆棧中
(第10條指令)  16:       80 7d fb 00             cmpb   $0x0,-0x5(%ebp)  // 比較bl是否為0  (b的實際值是1)
(第11條指令)  1a:       0f 95 c0                setne  %al              // 如果bl!=0,就將al設(shè)置為1;否則將al設(shè)置為0. (顯然,現(xiàn)在al=1)
(第12條指令)  1d:       0f b6 c0                movzbl %al,%eax         // 將寄存器eax高24位設(shè)置為0.
(第13條指令)  20:       83 c4 10                add    $0x10,%esp
(第14條指令)  23:       5b                      pop    %ebx
(第15條指令)  24:       5d                      pop    %ebp
(第16條指令)  25:       c3                      ret

其中第7條指令執(zhí)行v--操作,并且把結(jié)果寫到內(nèi)存中了。假設(shè)執(zhí)行atomic_dec_and_test()之前v的值是1,那么現(xiàn)在v的值就是0了。同樣,進程可以在這個時候被切換(執(zhí)行了第7條指令,沒有執(zhí)行第8條指令)。假設(shè)切換到了另一個進程,并且另一個進程執(zhí)行了atomic_inc()(這個函數(shù)執(zhí)行v++),這時v==1,表示這個資源仍然在使用,然后切換回開始的進程。由于從8條指令開始,后面的指令已經(jīng)跟v沒有關(guān)系了。根據(jù)第8-16條指令的執(zhí)行過程可以看出,最后atomic_dec_and_test()的結(jié)果是true。true表示這個資源已經(jīng)不被使用了,可以釋放了(比如清除內(nèi)存等)。但是實際上v的值是1,因為有另外一個進程在使用這個資源。如果把這個資源釋放了,那么不就出錯了嗎??????

論壇徽章:
0
5 [報告]
發(fā)表于 2013-09-07 19:06 |只看該作者
本帖最后由 ycnian 于 2013-09-07 19:12 編輯

其實不用考慮4樓這么極端的情況。一般情況下,資源釋放不會是原子操作

........
if (atomic_dec_and_test()) {
    // 開始釋放資源
    // 仍然在釋放資源
    .......
    // 資源釋放完了
    return;
}
........

進程在釋放資源的過程中同樣可能被切換到另外一個進程,另外一個進程執(zhí)行atomic_inc(),開始使用這個資源。切換回原來的進程后,繼續(xù)釋放資源,就出錯了。內(nèi)核不會這么弱智的,應(yīng)該在其他層面進行了保護,只是我還沒有看到而已(估計是自旋鎖或者信號量),接著看代碼。也歡迎大家?guī)臀沂崂硎崂,謝謝。

論壇徽章:
0
6 [報告]
發(fā)表于 2013-09-08 08:13 |只看該作者
原子性都是cpu匯編指令保證的,和編程一樣檢查標(biāo)志位,然后cpu內(nèi)部多核保持一致

單核不用保證原子性

論壇徽章:
0
7 [報告]
發(fā)表于 2013-09-08 12:32 來自手機 |只看該作者
按照定義,atomic操作就應(yīng)該是不被中斷打斷也不會有上下文的切換,否則就不叫原子操作可。會不會樓主的反匯編代碼有誤。理論上講,這么多步的操作,要想達(dá)到atomic效果,至少應(yīng)該有關(guān)中斷的指令

論壇徽章:
17
水瓶座
日期:2013-08-29 12:09:27白羊座
日期:2014-08-07 12:36:42丑牛
日期:2014-07-24 12:44:41寅虎
日期:2014-04-16 16:15:33寅虎
日期:2014-03-12 09:28:43摩羯座
日期:2014-03-06 13:22:04技術(shù)圖書徽章
日期:2014-03-06 11:34:50天蝎座
日期:2014-01-09 11:31:44寅虎
日期:2013-12-27 17:01:44雙子座
日期:2013-12-27 12:32:29雙子座
日期:2013-12-25 09:03:33丑牛
日期:2013-12-24 16:18:44
8 [報告]
發(fā)表于 2013-09-08 12:49 |只看該作者
static inline int atomic_add_return(int i, atomic_t *v)
{
        unsigned long flags;
        int val;

        raw_local_irq_save(flags);
        val = v->counter;
        v->counter = val += i;
        raw_local_irq_restore(flags);

        return val;
}
#define atomic_add(i, v)        (void) atomic_add_return(i, v)


摘抄于3.10.1,arch/arm/include/asm/atomic.h,不知道LZ是哪個內(nèi)核版本的代碼?

論壇徽章:
0
9 [報告]
發(fā)表于 2013-09-08 15:42 |只看該作者
回復(fù) 8# asuka2001
我看的X86架構(gòu)的代碼,內(nèi)核版本是3.6.0,我發(fā)現(xiàn)自己以前對內(nèi)核同步的理解有誤。


   

論壇徽章:
0
10 [報告]
發(fā)表于 2013-09-10 10:02 |只看該作者
ycnian 發(fā)表于 2013-09-07 18:50
不過我又有新問題了,看另外一個函數(shù)int atomic_dec_and_test(atomic_t *v),這個函數(shù)對v進行減1操作。如果 ...


所以atomic_inc的返回值必須大于1才行,如果atomic_inc都是1,那么你引用的資源本來就是0,那個資源早就是無效的了。
常用做的get 和put 操作的指針的引用計數(shù)時,這個是可以保證的,因為get的時候肯定是從另外一個有效的指針來做atomic_inc的,
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP