我需要能够取消映射通过我链接的一些库打开的文件。需要这样做的原因是,这些库所做的映射包含对模块的引用,这些模块可能需要在程序执行时重新加载(可能是长时间执行)。问题是,当我的进程持有引用时,无法卸载模块。
我已经编写了C代码来解析proc/self/maps
中的信息,以便读取映射的地址范围并计算其长度。我通过从结束地址减去开始地址来计算长度,然后将开始地址和计算的长度作为相应的参数传递给munmap
。问题是munmap
因EINVAL(无效参数)而失败。
我已经用sysconf(_SC_PAGESIZE)
检查了我的机器使用的页面的大小,它返回4096,这是我计算的长度值。GNU手册说munmap
可能会与EINVAL一起失败,如果:
给定的内存范围超出用户mmap范围或不是页面对齐。
我是错过了什么,还是根本不可能?我的最后手段是仔细梳理系统调用,并通过strace
检查每个mmap
,但我希望这是最后手段,谢谢。
您选择的方法不起作用,但不一定是因为您认为的原因。然而,有一种方法,所以请继续阅读。。。
我需要能够取消映射通过我链接的一些库打开的文件。
一旦让ELF加载程序(例如ld.linux.so
)为您加载库,您就失去了控制。
您可以而不是只是取消映射区域[,而不考虑方法的]。加载器已经为这些库进行了重新定位和符号链接。取消映射会删除该区域,但现在所有内容都会中断,因为链接器设置的各种指针现在指向空白。装载机将不知道你做了什么。
那么,你将如何重新映射库的新版本[以及在内存中的位置]?即使将其重新映射到同一地址也不能保证,因为您无法调整加载程序已经完成的操作。
需要这样做的原因是,这些库所做的映射包含对模块的引用,这些模块可能需要在程序执行时重新加载(可能是长时间执行)。
大多数需要更新到新版本库的程序只需重新执行它们自己。如果需要保留数据,可以制定一个转储/恢复机制。
但是,如果您确实想卸载/加载较新的库版本,可以使用动态链接来完成此操作。
不要使用ld
来链接与(例如)libA
,请离开ld
命令行,让程序自己加载libA
。
您可以使用dlopen/dlsym/dlclose
打开/加载您控制下的库。
您必须跟踪符号表,但更改为新版本很容易。当您想要新版本时,只需执行dlclose
,然后执行dlopen
。您必须重新进行dlsym
调用才能获得更新的地址,但所有这些都相当简单且标准的
问题是,当我的进程持有引用时,无法卸载模块。
原因是ELF加载程序做到了这一点。使用dlopen
等,您不会遇到同样的问题。
是的,这是可能的。我仔细查看了我的代码;在传递映射到CCD_ 18的起始地址时,我有一些误解。最初,我将起始地址读取为一个无符号的long-long,出于某种原因,在调用munmap
时,我将该值转换为十六进制字符串,而不是仅转换为空指针。本质上:
/* Values assigned here are really read from /proc/self/maps */
unsigned long long vm_start = 140013986873344;
unsigned long long vm_end = 140013986877440;
unsigned long long length = vm_end - vm_start;
munmap((void *)vm_start, length);