Linux系统解析域名(将主机名转换为IP地址)的过程是一个多层次的、由操作系统组件协同完成的复杂机制。其核心入口是glibc库中的getaddrinfo函数,该函数遵循/etc/nsswitch.conf文件所定义的“名称服务开关”(NSS)配置,按顺序尝试不同的数据源。典型的解析顺序首先是检查本地/etc/hosts文件,其次是DNS服务器,最后是其他如NIS、LDAP等网络服务。这一设计允许管理员灵活调整优先级,实现本地覆盖或加速特定域名解析。

当应用程序调用getaddrinfo时,glibc会加载对应的NSS模块(如files和dns)。若/etc/hosts中存在匹配的主机名,则直接返回其IP地址,整个过程不产生网络请求。如果本地文件未命中,则进入DNS解析流程。此时,解析器会读取/etc/resolv.conf配置文件,该文件定义了DNS服务器的IP地址(nameserver)、搜索域(search)、超时时间(timeout)和重试次数(attempts)。默认情况下,Linux会向第一个nameserver发送一个递归查询请求,等待响应;若超时或失败,则依次尝试下一个服务器,直到获得结果或耗尽所有服务器。
DNS查询本身遵循RFC 1035等标准。客户端构造一个DNS报文,包含目标域名的完整限定名称(FQDN)、查询类型(通常为A记录用于IPv4,AAAA记录用于IPv6)和类(IN)。该报文通过UDP协议的53端口发送给DNS服务器。如果响应报文被截断(TC标志置位),客户端会自动切换为TCP协议的53端口重新请求,以保证数据完整性。对于本地网络中常见的*.local主机名,Linux还可能使用多播DNS(mDNS)或LLMNR协议,这通常由systemd-resolved或avahi守护进程处理,它们维护独立的本地缓存并响应本地子网内的名称解析请求。
为了提升效率,Linux系统引入了多种缓存机制。传统的nscd(Name Service Cache Daemon)可以缓存主机、用户和组信息,但现代发行版更倾向于使用systemd-resolved。该系统服务不仅提供DNS缓存,还集成了DNSSEC验证、事务ID随机化等安全特性,并通过D-Bus接口与getaddrinfo交互。当glibc的dns模块指向systemd-resolved时(通过/etc/resolv.conf指向127.0.0.53),所有解析请求都会先经过该服务。此外,dnsmasq也常被用作本地DNS转发器和缓存,尤其常见于路由器或虚拟机网络环境中。这些缓存层会记录已解析的域名及其TTL(生存时间),在TTL过期前直接返回缓存结果,从而显著减少对上游DNS服务器的请求压力。
从全局视角看,Linux的DNS解析最终依赖于全球分布式的DNS层级结构。当本地DNS服务器(如运营商的递归解析器)收到客户端的递归查询时,它会依次访问根域名服务器(.)、顶级域名服务器(如.com、.org)和权威域名服务器。例如解析www.example.com时,递归服务器先向根服务器询问com的权威服务器,再向com服务器询问example.com的权威服务器,最后向example.com的权威服务器获取www的A记录。整个过程对客户端透明,客户端只发送一次递归查询,并等待最终答案。若启用了DNSSEC,解析器还会验证响应中的RRSIG记录,确保数据完整性和真实性,防止缓存投毒。
值得一提的是,Linux还支持通过/etc/nsswitch.conf中的hosts条目自定义解析顺序,例如“files dns myhostname”。其中myhostname是一个NSS模块,用于解析本机主机名(如localhost或通过/etc/hostname设置的主机名)。此外,环境变量LOCALDOMAIN和RES_OPTIONS可以覆盖/etc/resolv.conf中的部分设置。对于容器或特殊场景,Linux还可以使用ndots选项控制域名解析时是否先尝试带搜索域的绝对域名。总之,Linux域名解析过程是本地文件、系统服务、网络协议和分布式数据库的有机组合,其设计兼顾了灵活性、安全性和性能,是网络功能的基础构件。