转: 原文:https://blog.csdn.net/mumumuwudi/article/details/48722063
strace 命令不错,非常有用!!
使用strace命令分析一下, 系统调用过程:
________________________________________________________________
关于go dns解析的一些说明参照另一篇文章http://blog.csdn.net/mumumuwudi/article/details/48200505
go dns 解析 源码在go/src/net/dnsclient_unix.go, lookupHost()通过向本地dns server发送请求,获得IP和
域名的对应关系然后返回,函数调用关系如下:
-
// ---------------------------------------------------
-
// lookupHost()
-
//
-
// ->goLookupHostOrder()
-
//
-
// ---->goLookupIPOrder()
-
//
-
// -------->tryOneName()
-
//
-
// ----------->exchange()
其中的timeout 是 dns 超时时间 是在dnsconfig_unix.go 文件中读取 /etc/reslove.conf 的配置决定的.
net.go中的DialTimeout函数也会走到DNS解析流程中,该函数最终会调用到 lookupIPDeadline 启用
一个新的协程去解析DNS, 具体调用栈如下:
-
// ----------------------------------------------------------------
-
// DialTimeout()
-
//
-
// ->resolveAddrList()
-
//
-
// --->internetAddrList()
-
//
-
// ------>lookupIPDeadline()
-
//
-
// --------->lookupGroup.DoChan() 在新的协程中去做 dns解析
-
//
-
// ------------->lookupIP()
-
//
-
// ------------------>goLookupIPOrder()
-
// ------------------------------------------------------------------
总之,纯go语言的 DNS解析流程还是比较完善的~~
——————————————————————————————
之前使用过GO语言写了一个实时图片下载程序,主要考虑到GO语言的DNS解析对协程支持友好, 即
DNS解析时不会阻塞执行线程,只会阻塞当前协程,顺便研究了一下GO的net.LookupHost/ResolveIPAddr
实现方式。下面一段描述翻译自go语言的官方文档 https://golang.org/pkg/net/域名解析:
域名解析函数,Dial函数会间接调用到,而LokupHost和LookupAddr则会直接调用域名解析函数,不同
的操作系统实现不同, 在Unix系统中有两种方法进行域名解析:
1) 纯GO语言实现的域名解析,从/etc/resolv.conf中取出本地dns server地址列表, 发送DNS请求(UDP
报文)并获得结果
2) 使用cgo方式, 最终会调用到c标准库的getaddrinfo或getnameinfo函数(不建议使用对GO协程不
友好)
关于 cgo dns 解析的坑 参照以下链接:
https://jira.mongodb.org/browse/MGO-41
https://github.com/golang/go/issues/8602#issuecomment-66098142
GO语言默认使用纯GO的域名解析,因为这样一个阻塞的DNS请求只会消耗一个协程, 使 用cgo的
方式则会阻塞一个系统线程, 只有某些特定条件下才会使用系统提供的cgo方式, 例如: 1) 在OS X系统中不
允许程序直接发送DNS请求; 2) LOCALDOMAINH环境变量存在,即使为空; 3) ES_OPTIONS或
HOSTALIASES或ASR_CONFIG环境变量非空; 4)/etc/resolv.conf或/etc/nsswitch.conf指定的使用
方式GO解析器没有实现;5) 当要解析的域名以.local结束, 或者是一个mDNS域名
可以通过GODEBUG环境变量来设置go语言的默认DNS解析方式 纯go或cgo,
-----------------------------------------------------------------------
export GODEBUG=netdns=go # force pure Go resolver 纯go 方式
export GODEBUG=netdns=cgo # force cgo resolver cgo 方式
-----------------------------------------------------------------------
也可以在编译时指定netgo或netcgo的编译tag来设置
在plan 9中 域名解析只能通过 /net/cs和 /net/dns
在windows中 域名解析只能通过windows提供的C标准库函数GetAddrInfo或DnsQuery
OK 官方说明看完了, 我们写一个例子试一下:
package main import ( "net" "fmt" "os" ) func main() { ns, err := net.LookupHost("www.baidu.com") if err != nil { fmt.Fprintf(os.Stderr, "Err: %s", err.Error()) return } for _, n := range ns { fmt.Fprintf(os.Stdout, "--%s ", n) } }
1. 编译上述程序:go build -o ss source-dns.go
2. 执行上述程序,得到进程号: 15097
root@fly:/home/fly/mm# ./ss &
[7] 15097
3. 使用strace命令分析一下, 系统调用过程: strace -p 15097 -o aaa
使用strace命令分析一下, 系统调用过程:
上面得到的aaa 文件内如如下:
restart_syscall(<... resuming interrupted futex ...>) = -1 ETIMEDOUT (Connection timed out) futex(0x60ac70, FUTEX_WAKE_PRIVATE, 1) = 1 openat(AT_FDCWD, "/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3 epoll_create1(EPOLL_CLOEXEC) = 4 epoll_ctl(4, EPOLL_CTL_ADD, 3, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=716087048, u64=139737477062408}}) = -1 EPERM (Operation not permitted) epoll_ctl(4, EPOLL_CTL_DEL, 3, 0xc000045abc) = -1 EPERM (Operation not permitted) read(3, "# /etc/nsswitch.conf # # Example"..., 1024) = 513 read(3, "", 1024) = 0 close(3) = 0 openat(AT_FDCWD, "/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 3 epoll_ctl(4, EPOLL_CTL_ADD, 3, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=716087048, u64=139737477062408}}) = -1 EPERM (Operation not permitted) epoll_ctl(4, EPOLL_CTL_DEL, 3, 0xc00004599c) = -1 EPERM (Operation not permitted) fstat(3, {st_mode=S_IFREG|0644, st_size=726, ...}) = 0 read(3, "# This file is managed by man:sy"..., 65536) = 726 read(3, "", 64810) = 0 read(3, "", 65536) = 0 close(3) = 0 newfstatat(AT_FDCWD, "/etc/mdns.allow", 0xc000073078, 0) = -1 ENOENT (No such file or directory) getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=1024*1024}) = 0 socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3 connect(3, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory) close(3) = 0 socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3 connect(3, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory) close(3) = 0 openat(AT_FDCWD, "/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=513, ...}) = 0 read(3, "# /etc/nsswitch.conf # # Example"..., 4096) = 513 read(3, "", 4096) = 0 close(3) = 0 stat("/etc/resolv.conf", {st_mode=S_IFREG|0644, st_size=726, ...}) = 0 openat(AT_FDCWD, "/etc/host.conf", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=92, ...}) = 0 read(3, "# The "order" line is only used "..., 4096) = 92 read(3, "", 4096) = 0 close(3) = 0 futex(0x7f172a74aba4, FUTEX_WAKE_PRIVATE, 2147483647) = 0 openat(AT_FDCWD, "/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=726, ...}) = 0 read(3, "# This file is managed by man:sy"..., 4096) = 726 read(3, "", 4096) = 0 close(3) = 0 openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=30918, ...}) = 0 mmap(NULL, 30918, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f172ab8a000 close(3) = 0 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libnss_files.so.2", O_RDONLY|O_CLOEXEC) = 3 read(3, "177ELF211 3 >