您的位置:首页 > 运维架构 > Linux

根据ip:port快速获取进程pid

2017-10-21 22:36 429 查看

常规pid与ip:port信息的相互查找

对于如何由进程号获取端口号以及由端口号获取进程号,使用lsof命令都可以做到,以sshd 服务为例:


//ps -ef | grep sshd
root    1   0  0 Jul31 ?   00:00:07 /usr/sbin/sshd -D
//lsof -i:22 由端口获取进程
sshd   1 root  3u  IPv4 1649695748   0t0  TCP *:ssh (LISTEN)
// lsof -anP  -itcp -p1 由进程获取端口
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))
// Info不同位置代表不同,想从哪查就获取啥
remPort := strings.Split(Info[1], ":")
if len(remPort) == 2 {
if remPort[1] == port {
SocketId = Info[9]
return
}
}
}
return "", errors.New("get SocketId fail")

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux golang 进程 端口