C

共享記憶體中的不可重入庫?

  • March 11, 2016

我發現這個問答說共享庫可以使用共享記憶體在程序之間共享。但是,如果不對可以共享的程式碼類型進行一些非常嚴格的限制,似乎不可能在程序之間共享程式碼。我正在考慮具有不可重入 C 函式的庫,其輸出取決於其定義主體內的全域變數或靜態變數的值。像這個。

int really_really_nonreentrant(void x)
{
   static int i = 0;
   i++;
   return i;
}

具有這樣一個函式的庫將為使用它的每個程序返回單獨的遞增序列,因此看起來程式碼似乎沒有在程序之間共享。real_really_nonreentrant() 是從可重入函式中分離出來的,還是主要與其他函式一起保留,而僅將靜態 int i 分離出來?還是因為這個函式是不可重入的,所以整個庫都被排除在共享記憶體之外?

最終,需要做多少工作才能確保將自己的庫分配到共享記憶體?

我相信真正簡短的答案是 Linux 編譯器將程式碼排列成片段,其中至少有一個只是純程式碼,因此可以將記憶體映射到多個程序的地址空間。任何全域變數都會被映射,以便每個程序都有自己的副本。

我認為,您可以使用readelf, 或看到這一點objdump,但可以readelf提供更清晰的畫面。

這是來自readelf -e /usr/lib/libc.so.6. 那是 C 庫,可能映射到幾乎每個程序。輸出的相關部分readelf(儘管所有這些都很有趣)是程序標題:

Program Headers:
 Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
 PHDR           0x000034 0x00000034 0x00000034 0x00140 0x00140 R E 0x4
 INTERP         0x164668 0x00164668 0x00164668 0x00017 0x00017 R   0x4
     [Requesting program interpreter: /usr/lib/ld-linux.so.2]
 LOAD           0x000000 0x00000000 0x00000000 0x1adfc4 0x1adfc4 R E 0x1000
 LOAD           0x1ae220 0x001af220 0x001af220 0x02c94 0x057c4 RW  0x1000
 DYNAMIC        0x1afd90 0x001b0d90 0x001b0d90 0x000f8 0x000f8 RW  0x4
 NOTE           0x000174 0x00000174 0x00000174 0x00044 0x00044 R   0x4
 TLS            0x1ae220 0x001af220 0x001af220 0x00008 0x00048 R   0x4
 GNU_EH_FRAME   0x164680 0x00164680 0x00164680 0x06124 0x06124 R   0x4
 GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10
 GNU_RELRO      0x1ae220 0x001af220 0x001af220 0x01de0 0x01de0 R   0x1

兩個 LOAD 行是文件中唯一直接映射到記憶體中的部分。第一個 LOAD 標頭將一段映射/usr/lib/libc.so.6到具有 R 和 E 權限的記憶體中:讀取和執行。這就是程式碼。硬體特性阻止程序寫入那塊記憶體,因此所有程序都可以共享相同的真實物理記憶體頁面。核心可以設置硬體將相同的物理記憶體映射到所有程序。

第二個 LOAD 標頭標記為 RW - 可讀寫。這是 C 庫使用的具有全域變數的部分。每個程序在物理記憶體中都有自己的副本,映射到該程序的地址空間,並設置硬體權限以允許讀取和寫入。該部分不共享。

/proc您可以使用文件系統在正在執行的程序中查看這些記憶體映射。一個很好的命令來說明:cat /proc/self/maps. 這列出了cat程序擁有的所有記憶體映射,以及核心從哪些文件中獲取它們。

至於您需要做多少工作來確保將函式分配給映射到不同程序的記憶體,這幾乎完全取決於您提供給編譯器的標誌。用於“.so”共享庫的程式碼被編譯為“位置無關”。與位置無關的程式碼會執行諸如引用具有相對於目前指令的偏移量的變數的記憶體位置,並跳轉或分支到相對於目前指令的位置,而不是從絕對地址載入或寫入,以及跳轉到絕對地址。這意味著“RE” LOAD 塊/usr/lib/libc.so和“RW”塊只需要在每個程序中相隔相同距離的地址載入。在您的範常式式碼中,static除了引用它的程式碼之外,變數總是至少是頁面大小的倍數,並且由於給出 LOAD ELF 標頭的方式,它總是會在程序的地址空間中載入該距離。

只是關於“共享記憶體”一詞的註釋:有一個使用者級共享記憶體系統,與“System V 程序間通信系統”相關聯。這是多個程序非常明確地共享一塊記憶體的一種方式。設置和正確設置相當複雜且晦澀難懂。我們在這裡討論的共享記憶體或多或少對任何使用者程序都是完全不可見的。如果它作為多個程序之間共享的位置無關程式碼執行,或者它是唯一的副本,您的範常式式碼將不知道差異。

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