據其man page,dlopen()
不會加載同一庫兩次:如何規避dlopen()緩存?
如果相同的共享對象被使用dlopen再次加載(),則返回相同 對象的句柄。動態鏈接器維護對象句柄的引用計數 ,因此動態加載的共享對象是 不會被釋放,直到在其上調用dlclose()的次數爲 (因爲dlopen()已成功執行)。任何初始化返回(請參閱下面的 )都只調用一次。但是,隨後的調用 的dlopen()加載與RTLD_NOW相同的共享對象可能會強制使用先前加載了RTLD_LAZY的共享對象的符號 分辨率。
(強調我的)。
但究竟是什麼決定了共享對象的身份呢?我試圖查看代碼,但並未走得太遠。它是:
- 某種形式的標準化路徑名(如真實路徑?)
- inode的?
- libray的內容?
我很確定我可以排除這最後一點,因爲實際的文件系統副本會產生兩個不同的句柄。
爲了解釋這個問題背後的動機:我正在處理一些具有靜態全局變量的代碼。我需要該代碼的多個實例以線程安全的方式運行。我目前的做法是編譯並將所述代碼鏈接到一個動態庫並加載該庫多次。有了一些鏈接魔法,它似乎創建了全局變量的多個副本,並將每個庫中的訪問解析爲它自己的副本。唯一的問題是我的原型爲n個併發使用複製生成的庫n次。這不僅有些醜陋,但我也懷疑它可能會在不同的平臺上崩潰。
那麼根據POSIX標準,dlopen()
的確切行爲是什麼?
編輯:因爲它出現在評論和答案中,所以不重構代碼絕對不是一種選擇。這將涉及數月甚至數年的工作,並可能首先犧牲使用代碼的所有好處。有存在一個正在進行的研究項目,可能以更清潔的方式解決這個問題,但它是實際的研究,可能會失敗。我現在需要一個解決方案。
編輯2:因爲人們似乎還不相信用例實際上是有效的。我正在研究一種純粹的函數式語言,它將被嵌入到一個更大的C/C++應用程序中。因爲我需要一個帶有垃圾回收器的原型,一個經過驗證的類型檢查器,並且儘快提供合理的性能,所以我使用OCaml作爲中間代碼。現在,我編譯一個源模塊放入OCaml的模塊,鏈接生成的目標代碼(包括啓動等)到共享庫與OCaml的運行時和dlopen()的該共享庫。每個.so都有它自己的運行時副本,包括幾個全局變量(例如指向年輕一代),而且應該是完全正確的。該庫提供了兩個函數:一個初始化程序和一個單獨的導出,它可以完成原始模塊的任何操作。沒有OCaml運行時的符號被導出/共享。當我加載庫時,它的內部符號按預期重新定位,我現在唯一的問題是,我實際上需要在運行時爲每個作業實例複製.so文件。
關於線程本地存儲:這實際上是一個有趣的想法,因爲對運行時的修改確實很簡單。但問題是由OCaml編譯器生成的機器碼,因爲它不能發出tls符號的加載指令(還沒有?)。
'dlopen()'不能解決你基本破壞的庫的問題......如果你可以自己編譯有問題的代碼,你應該更好地解決它是線程安全的:o –
如果庫不是線程意識到,至少可以用線程本地數據替換靜態存儲持續時間數據。這是一項完全機械化的任務,不需要研究工作或幾個月即可完成。 –
這可能是一個有價值的實驗。但是對於這個任務,我需要知道兩件事:1.如何共享庫處理線程本地存儲以及ELF對象文件如何聲明它們? – choeger