用 LD_PRELOAD 写魔法程序
内容提要
作者利用 MTR 工具排查网络问题,开发了一个平台查询 IP 对应设备。通过 LD_PRELOAD 技术自定义 getnameinfo 函数,使 MTR 直接显示设备名称。尽管遇到日志打印和 Golang 程序死锁等问题,最终成功实现功能并发布。
关键要点
-
作者使用 MTR 工具排查网络问题,开发了一个平台查询 IP 对应设备。
-
通过 LD_PRELOAD 技术自定义 getnameinfo 函数,使 MTR 直接显示设备名称。
-
MTR 支持 DNS PTR 反查,但内网 DNS 没有支持 PTR 记录。
-
使用 LD_PRELOAD 技术可以自定义 getnameinfo 函数的行为。
-
编写了简单的 C 语言程序验证 LD_PRELOAD 的可行性。
-
使用 Go 语言实现逻辑,但遇到日志打印和程序死锁等问题。
-
解决日志打印问题的方法是将日志输出到 stderr。
-
Golang 程序在 fork 后出现死锁问题,最终用 C 语言实现逻辑解决了问题。
-
将程序打包成 deb 包发布到内网中,推荐使用 nfpm 工具。
延伸解读
LD_PRELOAD 技术的应用
LD_PRELOAD 技术允许开发者在运行时替换动态链接库中的函数实现。通过这种方式,作者成功自定义了 getnameinfo 函数,使 MTR 能够直接显示设备名称。这种技术在调试和功能扩展中非常有用,但也需要注意可能引发的兼容性问题。
Golang 的限制与解决方案
在使用 Golang 实现逻辑时,作者遇到了程序死锁的问题。这是由于 Golang 的运行时在 fork 后无法正确管理 goroutine 状态。最终,作者选择用 C 语言重写逻辑,避免了这一问题。这提醒开发者在选择编程语言时需考虑其特性与限制,尤其是在涉及多线程和进程管理时。
日志打印的挑战
在 MTR 中,日志打印的方式与其他程序不同,导致作者在调试时遇到困难。通过将日志输出到 stderr,作者解决了这一问题。这表明在开发过程中,了解所用工具和库的特性是至关重要的,尤其是在处理异步操作和进程间通信时。
延伸问答
如何使用 LD_PRELOAD 技术自定义 getnameinfo 函数?
可以通过编写一个共享库,重写 getnameinfo 函数,并在运行时使用 LD_PRELOAD 环境变量加载该库,从而实现自定义功能。
MTR 工具的主要功能是什么?
MTR 工具结合了 traceroute 和 ping,可以快速显示网络包的路径及其丢包和延迟情况。
在使用 Golang 实现逻辑时遇到了哪些问题?
遇到了日志打印问题和程序死锁问题,特别是在 fork 后导致的 Goroutines 死锁。
如何解决 MTR 中的日志打印问题?
将日志输出到 stderr,而不是文件,以避免在 fork 后关闭文件描述符导致日志丢失。
为什么内网 DNS 不支持 PTR 记录会影响 MTR 的功能?
因为 MTR 依赖 DNS PTR 记录来显示设备名称,内网 DNS 不支持此记录会导致无法获取设备名称。
如何将程序打包成 deb 包?
可以使用 nfpm 工具来打包程序为 deb 包,nfpm 使用起来相对简单。