os 如何确定一个 dll 是否已加载到内存中,或者 os 如何确定两个 dll 相同?
How does os figure if a dll is already loaded in memory or how does os figure two dll are the same?
在我的理解中,“dll/so”可以在程序(进程)之间共享。例如,当“libprint.so”第一次加载到“main”(称为main1)时,“libprint.so”从磁盘加载到内存中,如果我们启动另一个“main”(称为main2), “libprint.so”将不会从磁盘加载,而是从内存映射,因为“libprint.so”已经在内存中加载过一次。
所以我设计了一个实验:
main.cc --> 主要
#include <iostream>
#include <chrono>
#include <thread>
void printinfo();
int main() {
printinfo();
while(true) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
return 0;
}
print1.cc --> libprint1.so
#include <iostream>
void printinfo() {
std::cout << "Print One" << std::endl;
}
print2.cc --> libprint2.so
#include <iostream>
void printinfo() {
std::cout << "Print Two" << std::endl;
}
mv libprint1.so libprint.so
./main
// output is: Print One
保留 main.exe 运行,并将 libprint.dll 替换为 libprint2.dll,如
mv libprint2.so libprint.so
./main
// output is: Print Two
为什么输出是“Print Two”?我希望它是“Print One” 内存中已经加载了“libprint.so”,虽然我更改了“libprint.so”的内容,但 so 的绝对路径是相同的和以前一样,操作系统如何知道“新libprint.so”与以前不同?
感谢 @Michael Chourdakis,在 windows 环境中,当 main.exe 为 [=68= 时,libprint.dll 无法 被替换].
但是问题依旧在linux(libprint.so 可以换成
linux), @user253751 说肯定有一些技巧 linux 数字不同 "所以", 我想知道到底是什么技巧, 我是否必须阅读 linux os 源代码 ?
i want to know exactly what the tricks are, do i have to read the linux os source code ?
当你 运行 main
时 实际上 会发生什么(这大概是 link 反对 libprint.so
)(或在最少“explainlikeimfive”版本):
- 在执行静态 link 时(
g++ main.cpp ./libprint.so
或类似),静态 linker 记录 (a) 您的程序是动态 linked (b) 它需要 ./libprint.so
到 运行。您可以通过查看 readelf -d main
的输出来了解这一点。记录的另一件事是动态加载器(aka ELF 解释器),您可以看到 readelf -l main
.
- 当Linux内核启动新进程到运行
main
中时,它发现(通过读取程序头)要使用哪个ELF解释器,mmap
将该解释器存入内存,并将控制权转移给 it。解释器的工作是 mmap
其他库(包括 libprint.so
),重新定位它们,安排适当的符号解析等
- ELF 解释器使用
open
和 mmap
系统调用实际将 libprint.so
带入进程(这发生在 main()
被调用之前很久。
- 当任何进程调用
open("/some/path", ...)
时,OS(Linux 内核)执行目录查找以将给定路径映射到特定安装的文件系统和该文件系统上的唯一索引节点文件系统。最后,它(OS)检查该 inode 是否已经已经 被任何其他当前 运行ning 进程打开。如果文件 是 打开的,那么内核可以 重用 它已经为该文件分配的一些 in-kernel-memory 结构 <-- 这个是内核发现它可能不必为 libprint.so
分配新内存页面但可以 re-use 它已经拥有的内存页面的地方。
当你mv libprint2.so libprint.so
时,你改变了libprint.so
的索引节点号,所以内核知道这是一个完全不同的文件。您可以通过在 mv
.
之前和之后使用 ls -li libprint.so
来观察这一点
在我的理解中,“dll/so”可以在程序(进程)之间共享。例如,当“libprint.so”第一次加载到“main”(称为main1)时,“libprint.so”从磁盘加载到内存中,如果我们启动另一个“main”(称为main2), “libprint.so”将不会从磁盘加载,而是从内存映射,因为“libprint.so”已经在内存中加载过一次。
所以我设计了一个实验:
main.cc --> 主要
#include <iostream>
#include <chrono>
#include <thread>
void printinfo();
int main() {
printinfo();
while(true) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
return 0;
}
print1.cc --> libprint1.so
#include <iostream>
void printinfo() {
std::cout << "Print One" << std::endl;
}
print2.cc --> libprint2.so
#include <iostream>
void printinfo() {
std::cout << "Print Two" << std::endl;
}
mv libprint1.so libprint.so
./main
// output is: Print One
保留 main.exe 运行,并将 libprint.dll 替换为 libprint2.dll,如
mv libprint2.so libprint.so
./main
// output is: Print Two
为什么输出是“Print Two”?我希望它是“Print One” 内存中已经加载了“libprint.so”,虽然我更改了“libprint.so”的内容,但 so 的绝对路径是相同的和以前一样,操作系统如何知道“新libprint.so”与以前不同?
感谢 @Michael Chourdakis,在 windows 环境中,当 main.exe 为 [=68= 时,libprint.dll 无法 被替换].
但是问题依旧在linux(libprint.so 可以换成 linux), @user253751 说肯定有一些技巧 linux 数字不同 "所以", 我想知道到底是什么技巧, 我是否必须阅读 linux os 源代码 ?
i want to know exactly what the tricks are, do i have to read the linux os source code ?
当你 运行 main
时 实际上 会发生什么(这大概是 link 反对 libprint.so
)(或在最少“explainlikeimfive”版本):
- 在执行静态 link 时(
g++ main.cpp ./libprint.so
或类似),静态 linker 记录 (a) 您的程序是动态 linked (b) 它需要./libprint.so
到 运行。您可以通过查看readelf -d main
的输出来了解这一点。记录的另一件事是动态加载器(aka ELF 解释器),您可以看到readelf -l main
. - 当Linux内核启动新进程到运行
main
中时,它发现(通过读取程序头)要使用哪个ELF解释器,mmap
将该解释器存入内存,并将控制权转移给 it。解释器的工作是mmap
其他库(包括libprint.so
),重新定位它们,安排适当的符号解析等 - ELF 解释器使用
open
和mmap
系统调用实际将libprint.so
带入进程(这发生在main()
被调用之前很久。 - 当任何进程调用
open("/some/path", ...)
时,OS(Linux 内核)执行目录查找以将给定路径映射到特定安装的文件系统和该文件系统上的唯一索引节点文件系统。最后,它(OS)检查该 inode 是否已经已经 被任何其他当前 运行ning 进程打开。如果文件 是 打开的,那么内核可以 重用 它已经为该文件分配的一些 in-kernel-memory 结构 <-- 这个是内核发现它可能不必为libprint.so
分配新内存页面但可以 re-use 它已经拥有的内存页面的地方。
当你mv libprint2.so libprint.so
时,你改变了libprint.so
的索引节点号,所以内核知道这是一个完全不同的文件。您可以通过在 mv
.
ls -li libprint.so
来观察这一点