为何需两次调用RTLD_DI_SERINFOSIZE?背后考量是什么?
关于
dlinfo中RTLD_DI_SERINFO两步调用必要性的疑问与改进思路 根据我对dlinfo中RTLD_DI_SERINFO用法的理解(参考dlinfo手册),第一次调用会返回dls_size值以确保结构体足够大,但第二次调用似乎只是重复第一次的计算,仅填充dls_size和dls_cnt两个字段。另外,从dlopen接口的实现代码(对应dlinfo的实现)中,我没找到区分两次调用的逻辑,因此产生疑问:这种两步调用序列真的有必要吗?能不能通过新增RTLD_DI_SERINFOSIZE_COPY请求项来复制统计信息,从库作者的角度保证向前兼容性?
下面是测试代码,在我的机器上输出完全相同的两行:
Dl_serinfo dl_search_size; if (dlinfo(dl_handle, RTLD_DI_SERINFOSIZE, &dl_search_size)) { printf("dlinfo: %s while populating search info size", dlerror()); return; } printf("%d %ld\n", dl_search_size.dls_cnt, dl_search_size.dls_size); Dl_serinfo *dl_search_info = (Dl_serinfo *)malloc(dl_search_size.dls_size); if (!dl_search_info) { perror("malloc"); return; } if (dlinfo(dl_handle, RTLD_DI_SERINFOSIZE, dl_search_info)) { printf("dlinfo: %s while ", dlerror()); return; } printf("%d %ld\n", dl_search_info->dls_cnt, dl_search_info->dls_size);
两步调用的必要性分析
这种两步调用是类Unix系统获取变长结构体数据的经典设计:
- 第一次调用的核心作用是探测内存需求:
Dl_serinfo包含变长数组字段(dls_serpath由多个Dl_serpath元素组成),不同运行场景下所需的内存空间可能不同,第一次调用让库计算出完整结构体的大小,确保用户能分配足够的内存。 - 你看到两次输出一致,是因为两次调用期间程序的动态链接状态没有变化(比如未加载新共享库)。如果在两次调用之间执行了
dlopen加载新库,第二次返回的dls_cnt和dls_size很可能会改变。
不过从glibc的实现来看,当前RTLD_DI_SERINFOSIZE确实没有区分“仅获取大小”和“填充完整数据”的逻辑——每次调用都会重新计算大小和计数,存在冗余。但这种设计是为了兼容历史实现,早期接口普遍采用这种“先探大小再取数据”的模式,保证结构体扩展时旧程序仍能正常工作。
新增RTLD_DI_SERINFOSIZE_COPY的可行性
从库维护者的角度,新增这类请求项是可行的,但需要权衡以下几点:
- 向前兼容性:新增请求项不会破坏现有代码,旧程序依然可以沿用原有的两步调用模式。
- 实现复杂度:需要在
dlinfo中新增分支逻辑,处理新请求类型,直接返回已计算的统计信息,避免重复计算。 - 需求必要性:如果多数场景下两次调用结果不会变化,新增该请求项能提升性能,但需要评估社区是否有足够的需求驱动变更。
实际上,glibc的接口设计偏保守,除非有明确的性能问题或社区诉求,否则不会轻易新增接口。如果你的场景中确实存在重复计算的性能开销,可以考虑在应用层缓存第一次调用的结果,避免重复调用dlinfo。
内容的提问来源于Stack Exchange,提问作者ksyx
相关产品推荐
相关产品推荐

