Process
處理器沒有看到 POSIX 共享記憶體的變化?
**上下文:**我正在使用POSIX 共享記憶體為一組程序提供共享記憶體空間。為了共享數據,我已經使用這個方案一段時間了,它工作正常。但是,我最近在某類程序中遇到了一個奇怪的問題。
**問題:**我編寫了一個程序,其中每個程序都必須為共享記憶體空間中的共享總和貢獻一個值。當共享對象較早映射到記憶體時,總和被初始化為零。但是,當每個程序嘗試將它的一部分添加到共享總和時,它可以看到最新的值,但添加的結果總是好像它添加了它自己的值與零。見下文:
[21017] Adding 6 to 0! [21020] Adding 33 to 0! [21016] Adding 15 to 0! [21018] Adding 24 to 0! [21017] Got access! (0x7fe953fcb000 = 0) [21017] Done (0x7fe953fcb000 = 6) [21016] Got access! (0x7fe953fcb000 = 6) [21016] Done (0x7fe953fcb000 = 15) [21018] Got access! (0x7fe953fcb000 = 15) [21018] Done (0x7fe953fcb000 = 24) [21020] Got access! (0x7fe953fcb000 = 24) [21020] Done (0x7fe953fcb000 = 33) Sum = 33
每個程序“看到”最新寫入的值,但不知何故,在添加它自己的組件之後,似乎忽略了現有值。您可以看到每個訪問都是按順序排列的,因為有一個訪問控制系統管理誰可以寫入共享記憶體空間。使用的測試程序如下所示(雖然我不希望讀者執行它):
int main (void) { int local_sum = 0, gid = -1; volatile int *sum; // Fork for four processes. for (int i = 1; i < 4; i++) { if (fork() == 0) break; } // Initialize the DSM. Set GID. sum = (int *)dsm_init(&cfg); gid = dsm_get_gid(); // Compute range. for (int i = 0; i < 3; i++) { local_sum += array[(gid * 3) + i]; } // Add to local sum. printf("[%d] Adding %d to %d!\n", getpid(), local_sum, *sum); *sum = *sum + local_sum; // Barrier. dsm_barrier(); // Print sum if process zero. if (gid == 0) printf("Sum = %d\n", *sum); // Exit. dsm_exit(); }
為什麼每個程序都可以“看到”
0x7fe953fcb000
共享空間中地址處的正確值,但是在添加之後,行為就好像在添加期間該地址處的值仍然為零?以下是我對這個問題的困擾:
- 如果是記憶體問題,我怎麼能在算術運算之前列印正確的值,但仍然不正確?
- 我正在添加程序堆上的共享值。編譯器不可能假設那裡的值為零,並優化了任何東西。
是否有一些解釋為什麼這可能發生在引擎蓋下?我試圖在我的程序中使用 GDB 來查看發生了什麼。但據我所知,它只是將記憶體地址的值移動到寄存器中。我還看不到任何優化問題。
據我所見,四個程序快速連續生成,每個程序都嘗試執行 * sum += some_value; 他們完全有可能在加法之前都看到 * sum 為零。
讓我們使用抽象的彙編語法。C 語句
*sum = *sum + local_sum
被編譯成
LOAD *sum into R0 LOAD local_sum into R1 ADD R1 to R0 STORE R0 to *sum
四個程序競相執行這個序列。在他們中的任何一個有機會對 *sum 執行 STORE R0 之前,他們都將 LOAD *sum 載入到 R0 中是完全可能的;事實上,正如你所說,有一個由 STORE R0 觸發到 *sum 的系統呼叫(因此是一個重新規劃點),很有可能。您需要同步對共享變數的訪問,例如使用信號量。