常规pid与ip:port信息的相互查找
对于如何由进程号获取端口号以及由端口号获取进程号,使用lsof命令都可以做到,以sshd 服务为例:
root 1 0 0 Jul31 ? 00:00:07 /usr/sbin/sshd -D
sshd 1 root 3u IPv4 1649695748 0t0 TCP *:ssh (LISTEN)
sshd 1 root 3u IPv4 1649695748 0t0 TCP *:22 (LISTEN)
由pid查找ip:port信息
对于lsof而言,使用的办法就是查/proc下的文件,使用pid查端口无疑会很快捷,因为/proc下的文件就是以pid来命令的,查找的大致过程如下:
- 通过pid进入/proc/pid/fd目录,找到进程打开的所有文件描述,其中正在打开的链接内容为socket:[id],可使用readlink读取,拿到Socket的id。
lr-x------ 1 root root 64 7月 31 18:14 0 -> pipe:[1649692705]
l-wx------ 1 root root 64 7月 31 18:14 1 -> pipe:[1649692706]
l-wx------ 1 root root 64 7月 31 18:14 2 -> pipe:[1649692707]
lrwx------ 1 root root 64 7月 31 18:14 3 -> socket:[1649695748]
[root@99b54f61ad38 fd]# readlink 3
socket:[1649695748]
- 读取/proc/net/tcp ,就可以看到关于这个id的链接信息。
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
4: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 1649695748 1 ffff881274789e00 100 0 0 10 0
由ip:port 获取pid
由pid获取ip:port信息,lsof只需到对应的proc目录下读取相应的socketid,然后查找/proc/net下的文件即可,但是反过来,就没那么容易了。系统并不没有使用IP:Port存储对应的进程信息,换句话说,想通过端口号查找pid,lsof的工作将会很笨重。
- 首先从/proc/net/tcp(或者其他文件)下读取该Ip:port对应的socketid信息;
- 拿着这个socket id信息遍历/proc下所有pid文件的fd目录,匹配到socketid则记录下该pid,返回。
实现通过端口查pid
在有时候使用lsof命令是很危险的,比如机器复杂过高的情况下,这个命令可能成为压倒他的最后一根稻草。并且,lsof功能过于强大,他的遍历附带东西太多,所以很耗费时间。这里实现了一种轻便的方式快速实现了由端口获取pid。
本质上下面的程序并没有改变遍历的方式,只不过没有像lsof那样附加太多东西,只是单存的由端口查找pid。
func GetPid(addr int64) (pid string, err error) {
port := fmt.Sprintf("%04X", addr)
SocketId, err := GetSocketId(port)
if err != nil {
return
}
SocketInfo := fmt.Sprintf("socket:[%s]", SocketId)
procDirList, err := ioutil.ReadDir("/proc")
if err != nil {
return
}
for _, procDir := range procDirList {
_, err := strconv.Atoi(procDir.Name())
if err != nil {
continue
}
fdDir := fmt.Sprintf("/proc/%s/fd", procDir.Name())
fdSonDirList, err := ioutil.ReadDir(fdDir)
for _, socketFile := range fdSonDirList {
socket := fmt.Sprintf("/proc/%s/fd/%s", procDir.Name(), socketFile.Name())
data, err := os.Readlink(socket)
if err != nil {
continue
}
if SocketInfo == data {
return procDir.Name(), nil
}
}
}
return "", errors.New("get pid fail")
}
func GetSocketId(port string) (SocketId string, err error) {
fi, err := os.Open("/proc/net/tcp")
if err != nil {
return
}
defer fi.Close()
br := bufio.NewReader(fi)
for {
a, _, c := br.ReadLine()
if c == io.EOF {
break
}
Info := strings.Fields(string(a))
remPort := strings.Split(Info[1], ":")
if len(remPort) == 2 {
if remPort[1] == port {
SocketId = Info[9]
return
}
}
}
return "", errors.New("get SocketId fail")
}