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

常规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来命令的,查找的大致过程如下:
  1. 通过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]
  1. 读取/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的工作将会很笨重。
  1. 首先从/proc/net/tcp(或者其他文件)下读取该Ip:port对应的socketid信息;
  2. 拿着这个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")

}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值