2013-02-27 92 views
3

我不知道linux進程是否可以調用位於另一個進程的內存中的代碼?Linux:是否可以在進程之間共享代碼?

假設我們在進程A中有一個函數f(),並且我們希望進程B調用它。我想過的是,使用帶MAP_SHARED和PROT_EXEC標誌的mmap映射包含函數代碼的內存,並將指針傳遞給B,假設f()不會從A二進制文件調用任何其他函數。它會工作嗎?如果是,那麼我如何確定內存中f()的大小?

===編輯===

我知道,共享庫將做到這些,但我不知道是否有可能在進程之間動態共享代碼。

+5

不是直接。請使用動態庫或[RPC](http://en.wikipedia.org/wiki/Remote_procedure_call)。 – 2013-02-27 14:13:07

+0

這正是庫的用途。根據你將要完成的任務,[IPC](http://www.tldp.org/LDP/lpg/node7.html)也可能會這樣做。 – KBart 2013-02-27 14:16:55

+0

您想共享的代碼是由編譯器生成的,還是由您的程序生成的? – ams 2013-02-27 14:22:16

回答

5

是的,您可以這樣做,但第一個進程必須首先通過mmap和內存映射文件或使用shm_open創建的共享區域創建共享內存。

如果您正在共享編譯代碼,那麼這就是共享庫創建。您可以通過普通的方式與他們進行鏈接,共享將自動發生,或者您可以使用dlopen(例如,用於插件)手動加載它們。


更新:

由於代碼已經由編譯器產生的,那麼你將有搬遷擔心。編譯器不會生成任何可以正常工作的代碼。它預計.data部分位於某個位置,並且.bss部分已歸零。 GOT將需要填充。任何靜態構造函數都必須被調用。

總之,你想要的可能是dlopen。這個系統允許你像打開一個文件一樣打開共享庫,然後按名稱提取函數指針。程序庫中的每個程序都將共享代碼段,從而節省內存,但每個程序都將擁有自己的數據段副本,因此它們不會相互干擾。

請注意,您需要使用-fPIC來編譯您的庫代碼,否則您將無法獲得任何代碼共享(實際上,許多架構的鏈接器和動態加載器可能不支持不是PIC的庫) 。

+0

我其實從來沒有嘗試從mmaped內存運行代碼..它真的有效嗎?如何權限? – KBart 2013-02-27 14:18:52

+0

@KBart:只要設置了PROT_EXEC權限,就應該工作。當然,並非所有的體系結構都支持該標誌,但在大多數情況下,您無法停止它的工作。 – ams 2013-02-27 14:20:42

+0

很好。也許你可以舉個小例子嗎?我相信這正是這個問題無論如何。 – KBart 2013-02-27 14:22:06

4

標準方法是將代碼f()置於共享庫libfoo.so中。然後,你既可以鏈接到該庫(例如,通過建立程序gcc -Wall a.c -lfoo -o a.bin),或動態地加載(例如,在程序)使用dlopen(3)然後檢索的f使用dlsym地址。

當你編譯一個共享庫要:

  • gcc -Wall -fPIC -c foo1.c -o foo1.pic.o每個源文件foo1.c編譯成position independent code,同樣爲foo2.cfoo2.pic.o
  • 鏈接所有的人都爲libfoo.sogcc -Wall -shared foo*.pic.o -o libfoo.so;注意到可以鏈接附加共享庫lbfoo.so(例如通過附加-lm到連接命令)

又見Program Library Howto

你可以通過mmap來玩瘋狂的技巧 - 其他一些/proc/1234/mem但這是不合理的。使用共享庫。

PS。你可以dlopen大量(數十萬)共享對象lib*.so文件;你可能想要dlclose他們(但實際上你不必)。

2

這是可能的,但這正是共享庫的用途。

另外,請注意您需要檢查兩個進程的共享內存地址是否相同,否則任何引用都是「絕對」(即指向共享代碼中的某個指針)。和共享庫一樣,代碼的位必須是相同的,並且與所有共享內存一樣,如果修改任何共享內存,則需要確保不會「混淆」其他進程記憶。

根據生成的實際代碼以及可用信息的級別,確定函數的大小範圍從「硬」到「幾乎不可能」。調試符號將具有函數的大小,但要注意,我已經看到編譯器會生成代碼,其中兩個函數共享相同的「返回」代碼片段(也就是說,編譯器會生成跳轉到具有相同代碼位的另一個函數返回結果,因爲它節省了幾個字節的代碼,並且無論如何已經有一個跳轉[例如編譯器必須跳轉的if/else])。

相關問題