Process

處理器沒有看到 POSIX 共享記憶體的變化?

  • July 13, 2018

**上下文:**我正在使用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 的系統呼叫(因此是一個重新規劃點),很有可能。您需要同步對共享變數的訪問,例如使用信號量。

引用自:https://unix.stackexchange.com/questions/454945