当前位置: 首页 > Linux

socket描述符的那些事儿

时间:2023-04-06 02:06:01 Linux

前几天看到有人发的面试题,问的是MySQL连接的进程描述符。在Linux中,一切皆文件,进程描述符其实就是一个文件描述符。我们也知道Linux内核提供了一个proc文件系统,/proc文件系统是一个虚拟文件系统,通过它可以用一种新的方式来实现Linux内核空间与用户之间的通信。在/proc文件系统中,我们可以读写虚拟文件作为与内核中的实体进行通信的一种方式,但与普通文件不同的是,这些虚拟文件的内容是动态创建的。用户和应用程序可以通过proc获取系统信息,可以改变内核的一些参数。/proc目录通常对用户是只读的。如果想直接在bash下修改一个文件,权限不够。但对系统来说是可写的,可以通过编程实现增、删、改、查。如果查看套接字描述符,文件描述符必须在/proc目录中。想必这个进程打开的所有fds都存放在对应进程的/proc/$pid/fd目录下。[root@localhostfd]#pwd/proc/1723/fd[root@manager1723]#llfd|grepsocketlrwx------1rootroot64Jul713:49103->socket:[5722374]lrwx------1rootroot64Jul713:49104->socket:[5057632]lrwx------1rootroot64Jul713:49105->socket:[5722375]lrwx------1rootroot64Jul713:49106->socket:[5057636]lrwx-----1rootroot64Jul713:49107->socket:[5983188]lrwx------1rootroot64Jul713:49124->socket:[5983189]lrwx------1rootroot64Jul713:49130->socket:[27456]lrwx------1rootroot64Jul713:49131->socket:[27458]lrwx------1rootroot64Jul713:49132->socket:[27460]lrwx------1rootroot64Jul713:4951->socket:[23447]lrwx------1rootroot64Jul713:4952->socket:[23448]lrwx------1rootroot647月7日13:4978->套接字:[5057630]lrwx------1根root647月7日13:4979->套接字:[5721339]lrwx------1根root647月7日13:4980->插座:[3639663]lrwx------1rootroot64Jul713:4981->socket:[5057631]lrwx------1rootroot64Jul713:4982->套接字:[5721340]lrwx------1rootroot64Jul713:4995->socket:[5722372]这个结果和netstat看到的几乎一样[root@manager~]#netstat-antp|grep1723|wc-l16当然,这应该和使用lsof得到的结果类似。之所以说相似,说不一样,是因为netstat和lsof虽然也是读取/proc文件系统,但是它们有自己的过滤和判断条件。例如,这两个工具除了读取/proc/pid/fd目录外,还会读取/proc/net/tcp(udp)文件。因此,如果socket被创建但没有被使用,它只会存在于/proc/pid/fd下,而不会存在于/proc/net/tcp(udp)下,那么netstat将无法统计。那么这个socket:后面的一串数字是什么呢?看起来像端口号,有的明明不是,但实际上是socket的inode号。那么,知道了某个进程打开的socket的inode号后,我们可以做什么呢?这里涉及到/proc/net/tcp(udp对应/proc/net/udp)文件,里面也列出了对应socket的inode号。通过比较这个字段,我们可以得到/proc/net/tcp下这个socket的其他信息,比如对应的<本地地址:端口号,远程地址:端口号>四元组,窗口大小,状态等信息。具体字段含义参见net/ipv4/tcp_ipv4.c中的tcp4_seq_show函数。[root@managernet]#cat/proc/net/tcpsllocal_addressrem_addressrem_addresssttx_queuerx_queuetrtm-->trtm->当retersmtuidtimutinode0:0000000000:006f100001001:00000000:001600000000:00000A00000000:00000000000000:00000000000000000000000000000000000000000000000000来1ffff88042dfc8000100001003:00000000:21DE00000000:00000A00000000:0000000000:000000000000000000215131ffff88042dfc87c0100001004:55F9B40A:001659CDB40A:27790100000000:0000000002:000936FD000000000060049302ffff88042dfca6c0226110-15:55F9B40A:001659CDB40A:27330100000030:0000000001:00000018000000000059843624ffff880426f645c02543110-16:55F9B40A:001659CDB40A:27780100000000:0000000002:000936FD000000000060048662ffff88042dfcae80247110-17:55F9B40A:8F2E55F9B40A:20F90100000000:0000000000:000000000000000000220511ffff88042dfc9f002043010-18:55F9B40A:001659CDB40A:27380100000000:0000000002:000843C9000000000059839092ffff880426f664c02242176[root@managernet]#这个文件怎么解释,我们只看第一部分8:55F9B40A:00130016||||-->连接状态(套接字状态)|||||------>远程TCP端口号(远程端口,主机字节序)||||------------>远程IPv4地址(远程IP,网络字节序)|||------------------>本地TCP端口号(本地端口,主机字节顺序)||---------------------------->本地IPv4地址(本地IP,网络字节序)|------------------------------>入口数比如我们看到rem_address59CDB40A:2738,很自然,就是TCP的四元组。十六进制转二进制后为89.205.180.10:10040。注意这里的IP地址应该是10.180.205.89用lsof验证[root@managernet]#lsof-i|grep10040sshd17757root3uIPv459839090t0TCPmanager.bigdata:ssh->10.180.205.89:10040(ESTABLISHED)连接状态(socket状态),不同的值代表不同的状态,如下:TCP_ESTABLISHED:1TCP_SYN_SENT:2TCP_SYN_RECV:3TCP_FIN_WAIT1:4TCP_FIN_WAIT2:5TCP_TIME_WAIT:6TCP_CLOSE:7TCP_CLOSE_WAIT:8TCP_LAST_ACL:9TCP_LISTEN:10TCP_CLOSING:11我们看到的数据不是MySQL连接。问题来了,为什么我在MySQL中看到那么多fds,netstat也看到很多,但是/proc/net/tcp下却没有那么多socket描述符。如前所述,/proc/net/tcp(udp)可以认为是proc/pid/fd的子集,但这太糟糕了。其实原因很简单。如果在/proc/net/tcp下找不到,请尝试在/proc/net/tcp6下寻找。关闭指定的套接字连接。如果我们更进一步,我们现在可以找到每个pid下的套接字描述符。如果我要打破这个描述符,就意味着断开连接。怎么做?使用防火墙显然不是一个好主意,防火墙通常是针对某个IP和固定端口的。这时候socketfd号就派上用场了。请注意,fd和inode是两个不同的东西。查看当前fd(base)[root@manager~]#ll/proc/1723/fd|grepsocketlrwx------1rootroot64Jul713:49103->socket:[10232948]lrwx------1rootroot64Jul713:49105->socket:[9490029]lrwx-----1rootroot64Jul713:49107->socket:[10232952]lrwx------1rootroot64Jul713:49124->socket:[10232954]lrwx------1rootroot64Jul713:49130->socket:[27456]lrwx------1rootroot64Jul713:49131->socket:[27458]lrwx------1rootroot64Jul713:49132->socket:[27460]lrwx------1root根64Jul713:4951->socket:[23447]lrwx------1rootroot64Jul713:4952->socket:[23448]lrwx------1rootroot64Jul713:4979->socket:[10882498]现在在另一台服务器上,直接使用命令行mysql-h连接本地mysql服务,然后查看fd列表(base)[root@manager~]#ll/proc/1723/fd|grepsocketlrwx-----1rootroot64Jul713:49103->socket:[10232948]lrwx------1rootroot64Jul713:49105->socket:[9490029]lrwx------1rootroot64Jul713:49107->socket:[10232952]lrwx------1rootroot64Jul713:49124->socket:[10232954]lrwx------1rootroot647月7日13:49130->套接字:[27456]lrwx------1root64Jul713:49131->socket:[27458]lrwx------1rootroot64Jul713:49132->套接字:[27460]lrwx------1rootroot64Jul713:4951->socket:[23447]lrwx------1rootroot64Jul713:4952->套接字:[23448]lrwx------1rootroot64Jul713:4979->socket:[10882498]lrwx------1rootroot64Jul713:4980->socket:[11222776]相比之下,底部多出来的那一行是新添加的连接,fd=80,socketinode=11222776我们用gdb调用syscall,关闭这个fdgdb-p1723callclose(80)quit看看,远程mysql连接已经断了---公众号