Jul
26
[Linux] 如何通过端口号获取监听进程PID
如果到Google去搜索,"How to find out which process is listening upon a port"这是第一篇文章。
事实上大部分文章都是告诉你,要么 lsof -i :80 要么 netstat -antulp | grep :80 就能找到httpd。
可是如果就这样的话,这篇BLOG就变成微博了。
事实上我的目的是希望通过编程找出这个PID,而不是调用某个命令。
第一个尝试是去看lsof的源码。找源码容易,apt-get source lsof 就行。但是源码跟大部分linux软件包一样,看起来相当晦涩。
第二个尝试是Google,但是能找到的都是命令版的。
第三个尝试是stackoverflow,没有直接搜到问题,于是准备自己提问,但是在“Questions that may already have your answer”里头找到了一篇“How to get the pid of a process that is listening on a certain port programmatically?” ( http://stackoverflow.com/questions/10996242 )。
Answer给出的步骤非常清晰:与netstat的实现一样,先读取 /proc/net/tcp 这个文件,第二个字段(local_address)冒号后面的是端口号(十六进制),第四个字段st为0A表示TCP_LISTEN,第10个字段是十进制的inode编号;而通过遍历 /proc/PIDs/fd 下面的链接,找到链接到形如 socket:[端口号] 的fd进行对比,就能知道哪些进程与该端口有一腿。
我在机器上监听的是8888端口,换成hex是22B8,但是在 /proc/net/tcp 中却找不到。幸而那篇文章的第二个Answer给了个提示,于是 strace /usr/sbin/lsof -i :8888 ,发现它还打开了 /proc/net/tcp6 (也就是对应IPv6的那个文件了)。过去一查,果然有,再顺着inode,对照lsof的结果一看,的确符合。
于是再去grep一把 lsof 的源码目录,发现在 00FAQ 文件中的 10.2.2 一节就说明了 lsof 的实现机制:
看来在 Linux 下的实现方式确实只有这一种。顺便提一下,Stackoverflow上面的另一篇 http://stackoverflow.com/questions/4041003/c-what-process-is-listening-on-a-certain-port-in-windows 提到了,在Windows下可以用GetExtendedTcpTable/GetExtendedUdpTable来实现。
最后附上PHP实现的源码(这个代码用C/C++写确实蛋疼)
转载请注明出自 ,如是转载文则注明原出处,谢谢:)
RSS订阅地址: https://www.felix021.com/blog/feed.php 。
事实上大部分文章都是告诉你,要么 lsof -i :80 要么 netstat -antulp | grep :80 就能找到httpd。
可是如果就这样的话,这篇BLOG就变成微博了。
事实上我的目的是希望通过编程找出这个PID,而不是调用某个命令。
第一个尝试是去看lsof的源码。找源码容易,apt-get source lsof 就行。但是源码跟大部分linux软件包一样,看起来相当晦涩。
第二个尝试是Google,但是能找到的都是命令版的。
第三个尝试是stackoverflow,没有直接搜到问题,于是准备自己提问,但是在“Questions that may already have your answer”里头找到了一篇“How to get the pid of a process that is listening on a certain port programmatically?” ( http://stackoverflow.com/questions/10996242 )。
Answer给出的步骤非常清晰:与netstat的实现一样,先读取 /proc/net/tcp 这个文件,第二个字段(local_address)冒号后面的是端口号(十六进制),第四个字段st为0A表示TCP_LISTEN,第10个字段是十进制的inode编号;而通过遍历 /proc/PIDs/fd 下面的链接,找到链接到形如 socket:[端口号] 的fd进行对比,就能知道哪些进程与该端口有一腿。
我在机器上监听的是8888端口,换成hex是22B8,但是在 /proc/net/tcp 中却找不到。幸而那篇文章的第二个Answer给了个提示,于是 strace /usr/sbin/lsof -i :8888 ,发现它还打开了 /proc/net/tcp6 (也就是对应IPv6的那个文件了)。过去一查,果然有,再顺着inode,对照lsof的结果一看,的确符合。
于是再去grep一把 lsof 的源码目录,发现在 00FAQ 文件中的 10.2.2 一节就说明了 lsof 的实现机制:
引用
Lsof identifies protocols by matching the node number associated
with the /proc//fd entry to the node numbers found in
selected files of the /proc/net sub-directory. Currently
/proc-based lsof examines these protocol files:
/proc/net/ax25 (untested)
/proc/net/ipx (needs kernel patch)
/proc/net/raw /proc/net/raw6
/proc/net/tcp /proc/net/tcp6
/proc/net/udp /proc/net/udp6
/proc/net/unix
with the /proc/
selected files of the /proc/net sub-directory. Currently
/proc-based lsof examines these protocol files:
/proc/net/ax25 (untested)
/proc/net/ipx (needs kernel patch)
/proc/net/raw /proc/net/raw6
/proc/net/tcp /proc/net/tcp6
/proc/net/udp /proc/net/udp6
/proc/net/unix
看来在 Linux 下的实现方式确实只有这一种。顺便提一下,Stackoverflow上面的另一篇 http://stackoverflow.com/questions/4041003/c-what-process-is-listening-on-a-certain-port-in-windows 提到了,在Windows下可以用GetExtendedTcpTable/GetExtendedUdpTable来实现。
最后附上PHP实现的源码(这个代码用C/C++写确实蛋疼)
<?php
$filelist = array("/proc/net/tcp6", "/proc/net/tcp"); //udp/unix的就先不管了
$port2inode = array();
foreach ($filelist as $file)
{
$lines = file($file);
array_shift($lines);
foreach ($lines as $line)
{
$values = split(" +", $line);
list($addr, $port) = explode(":", $values[2]);
$port = hexdec($port);
$port2inode[$port] = $values[10];
}
}
if ($argc < 2)
die("usage: php {$argv[0]} PORT\n");
$findport = $argv[1];
if(!isset($port2inode[$findport]))
die("$findport not listened\n");
$procdir = scandir("/proc");
natcasesort($procdir);
foreach ($procdir as $pid)
{
$path = "/proc/$pid/fd";
if (!is_readable($path) || !is_numeric($pid) || !is_dir($path)) continue;
$dir = scandir($path);
foreach ($dir as $file)
{
$link = "$path/$file";
if (!is_link($link)) continue;
$real = readlink($link);
if (substr($real, 0, 7) == "socket:")
{
$port = substr($real, 8, -1);
if ($port == $port2inode[$findport])
{
echo $pid, ": ", readlink("/proc/$pid/exe") . "\n";
continue;
}
}
}
}
?>
$filelist = array("/proc/net/tcp6", "/proc/net/tcp"); //udp/unix的就先不管了
$port2inode = array();
foreach ($filelist as $file)
{
$lines = file($file);
array_shift($lines);
foreach ($lines as $line)
{
$values = split(" +", $line);
list($addr, $port) = explode(":", $values[2]);
$port = hexdec($port);
$port2inode[$port] = $values[10];
}
}
if ($argc < 2)
die("usage: php {$argv[0]} PORT\n");
$findport = $argv[1];
if(!isset($port2inode[$findport]))
die("$findport not listened\n");
$procdir = scandir("/proc");
natcasesort($procdir);
foreach ($procdir as $pid)
{
$path = "/proc/$pid/fd";
if (!is_readable($path) || !is_numeric($pid) || !is_dir($path)) continue;
$dir = scandir($path);
foreach ($dir as $file)
{
$link = "$path/$file";
if (!is_link($link)) continue;
$real = readlink($link);
if (substr($real, 0, 7) == "socket:")
{
$port = substr($real, 8, -1);
if ($port == $port2inode[$findport])
{
echo $pid, ": ", readlink("/proc/$pid/exe") . "\n";
continue;
}
}
}
}
?>
欢迎扫码关注:
转载请注明出自 ,如是转载文则注明原出处,谢谢:)
RSS订阅地址: https://www.felix021.com/blog/feed.php 。