是否可以根据/ proc / self / maps中的信息执行munmap?

时间:2016-04-20 01:44:17

标签: c linux

我需要能够取消映射通过我正在链接的一些库打开的文件。需要这样做的原因是这些库所做的映射包含对程序执行时可能需要重新加载的模块的引用(可能是长时间运行的执行)。问题是当我的进程持有引用时,无法卸载模块。

我编写了C代码来解析proc/self/maps中的信息,以便读取映射的地址范围并计算其长度。我通过从结束地址中减去起始地址然后将起始地址和计算出的长度作为相应参数传递给munmap来计算长度。问题是munmap因EINVAL(无效参数)而失败。

我已经检查了我的机器使用sysconf(_SC_PAGESIZE)页面的大小,它返回4096,这是我计算出的长度的值。 GNU手册说如果出现以下情况munmap可能会因EINVAL而失败:

  

给定的内存范围超出了用户mmap范围或不是页面   对齐。

我错过了什么或这根本不可能吗?我的最后一招是仔细梳理系统调用并通过mmap检查每个strace,但我希望这是最后的手段,谢谢。

2 个答案:

答案 0 :(得分:2)

您选择的方法有效,但不一定是您认为的原因。但是,有一种方法,请继续阅读...

  

我需要能够取消映射通过我链接的某些库打开的文件。

一旦您让ELF加载程序(例如ld.linux.so)为您加载库,您就失去了控制权。

你可以取消映射区域[无论方法]。加载器已经为这些库完成了重定位和符号链接。 unmap删除了该区域,但现在一切都中断了,因为链接器设置的各种指针现在指向空白区域。装载者将不知道你做了什么。

然后,您将如何重新映射新版本的库[以及内存中的位置]?即使将其重新映射到同一地址也无法保证,因为您无法调整加载器已经完成的操作。

  

需要执行此操作的原因是这些库所做的映射包含对 modules 的引用,这些引用可能需要在程序执行时重新加载(可能是长时间运行的执行)。

大多数需要更新到新版本库的程序只需重新执行。如果需要保留数据,可以设计转储/恢复机制。

但是,如果您真的想要卸载/加载较新的库版本,可以使用动态链接来执行此操作。

不要将ld链接与(例如)libA一起使用,请离开ld命令行并让程序自行加载libA

您使用dlopen/dlsym/dlclose打开/加载您控制下的库。

您必须跟踪符号表,但更改为新版本很容易。如果您需要新版本,只需执行dlclose,然后执行dlopen。您必须重做dlsym次来获取更新后的地址,但这一切都非常简单和标准

  

问题是当我的进程持有引用时,无法卸载模块。

原因是ELF装载机做到了这一点。使用dlopen et。你不会遇到同样的问题。

答案 1 :(得分:0)

是的,这是可能的。我仔细看了看我的代码;在将映射的起始地址传递给munmap时,我有一些误解。我最初将起始地址读入一个无符号长long,并且出于某种原因,我将此值转换为十六进制字符串,而不是在调用munmap时转换为void指针。实质上:

/* 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);