乐趣区

关于linux:socket描述符的那些事

前几天看到有人发的一个面试题,问的是 MySQL 连贯的过程描述符的问题。

在 Linux 里,所有皆文件,那过程描述符,理论就是文件描述符了。

咱们还晓得 Linux 内核提供了一种通过 proc 文件系统,/proc 文件系统是一个虚构文件系统,通过它能够应用一种新的办法在 Linux 内核空间和用户间之间进行通信。在 /proc 文件系统中,咱们能够将对虚构文件的读写作为与内核中实体进行通信的一种伎俩,然而与一般文件不同的是,这些虚构文件的内容都是动态创建的。用户和应用程序能够通过 proc 失去零碎的信息,并能够扭转内核的某些参数。

/proc 目录通常对用户来说是只读的,如果你间接在 bash 下想要批改一个文件是权限有余的。然而对系统来说是可写的,因而也就能够通过编程来实现增删改查。

查看 socket 描述符

那么,这个文件描述符就肯定是在 /proc 目录了。想必那就是在相应过程的 /proc/$pid/fd 目录下寄存了此过程所有关上的 fd。

[root@localhost fd]# pwd
/proc/1723/fd
[root@manager 1723]# ll fd|grep socket
lrwx------ 1 root root 64 Jul  7 13:49 103 -> socket:[5722374]
lrwx------ 1 root root 64 Jul  7 13:49 104 -> socket:[5057632]
lrwx------ 1 root root 64 Jul  7 13:49 105 -> socket:[5722375]
lrwx------ 1 root root 64 Jul  7 13:49 106 -> socket:[5057636]
lrwx------ 1 root root 64 Jul  7 13:49 107 -> socket:[5983188]
lrwx------ 1 root root 64 Jul  7 13:49 124 -> socket:[5983189]
lrwx------ 1 root root 64 Jul  7 13:49 130 -> socket:[27456]
lrwx------ 1 root root 64 Jul  7 13:49 131 -> socket:[27458]
lrwx------ 1 root root 64 Jul  7 13:49 132 -> socket:[27460]
lrwx------ 1 root root 64 Jul  7 13:49 51 -> socket:[23447]
lrwx------ 1 root root 64 Jul  7 13:49 52 -> socket:[23448]
lrwx------ 1 root root 64 Jul  7 13:49 78 -> socket:[5057630]
lrwx------ 1 root root 64 Jul  7 13:49 79 -> socket:[5721339]
lrwx------ 1 root root 64 Jul  7 13:49 80 -> socket:[3639663]
lrwx------ 1 root root 64 Jul  7 13:49 81 -> socket:[5057631]
lrwx------ 1 root root 64 Jul  7 13:49 82 -> socket:[5721340]
lrwx------ 1 root root 64 Jul  7 13:49 95 -> socket:[5722372]

这个后果,和 netant 看到的相差无几

[root@manager ~]# netstat -antp | grep 1723 | wc -l
16

当然,这和用 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 下取得此套接口的其余信息,如对应的 < 本地地址:端口号,远端地址:端口号 > 四元组,窗口大小,状态等信息。具体字段含意详见 net/ipv4/tcp_ipv4.c 中的 tcp4_seq_show 函数。

 [root@manager net]# cat /proc/net/tcp
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode
   0: 00000000:006F 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 15528 1 ffff880426f60000 100 0 0 10 0
   1: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 19300 1 ffff880426f607c0 100 0 0 10 0
   2: 0100007F:0019 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 20170 1 ffff88042dfc8000 100 0 0 10 0
   3: 00000000:21DE 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 21513 1 ffff88042dfc87c0 100 0 0 10 0
   4: 55F9B40A:0016 59CDB40A:2779 01 00000000:00000000 02:000936FD 00000000     0        0 6004930 2 ffff88042dfca6c0 22 6 1 10 -1
   5: 55F9B40A:0016 59CDB40A:2733 01 00000030:00000000 01:00000018 00000000     0        0 5984362 4 ffff880426f645c0 25 4 31 10 -1
   6: 55F9B40A:0016 59CDB40A:2778 01 00000000:00000000 02:000936FD 00000000     0        0 6004866 2 ffff88042dfcae80 24 7 1 10 -1
   7: 55F9B40A:8F2E 55F9B40A:20F9 01 00000000:00000000 00:00000000 00000000     0        0 22051 1 ffff88042dfc9f00 20 4 30 10 -1
   8: 55F9B40A:0016 59CDB40A:2738 01 00000000:00000000 02:000843C9 00000000     0        0 5983909 2 ffff880426f664c0 22 4 21 7 6
[root@manager net]#

这个文件怎么解读呢,咱们临时只看第一局部

   8: 55F9B40A:0016 59CDB40A:2738 01 
   |      |      |      |      |   |--> connection state(套接字状态)|      |      |      |      |------> remote TCP port number(远端端口,主机字节序)|      |      |      |-------------> remote IPv4 address(远端 IP,网络字节序)|      |      |--------------------> local TCP port number(本地端口,主机字节序)|      |---------------------------> local IPv4 address(本地 IP,网络字节序)|----------------------------------> number of entry

比方咱们看到 59CDB40A:2738 这个 rem_address,很天然它就是 TCP 的四元组,十六进制转为二进制后就是
89.205.180.10:10040,留神此处 IP 地址应该是 10.180.205.89。用 lsof 验证下

[root@manager net]# lsof -i|grep 10040
sshd    17757   root    3u  IPv4 5983909      0t0  TCP manager.bigdata:ssh->10.180.205.89:10040 (ESTABLISHED)

connection state(套接字状态),不同的数值代表不同的状态,参照如下:

TCP_ESTABLISHED:1   TCP_SYN_SENT:2
TCP_SYN_RECV:3      TCP_FIN_WAIT1:4
TCP_FIN_WAIT2:5     TCP_TIME_WAIT:6
TCP_CLOSE:7         TCP_CLOSE_WAIT:8
TCP_LAST_ACL:9      TCP_LISTEN:10
TCP_CLOSING:11

咱们看的这条数据并不是 MySQL 的连贯。问题来了,为什么 MySQL 里看到那么多 fd,netstat 也看到了很多,然而 /proc/net/tcp 下并没有那么多 socket 描述符呢。后面说过了,/proc/net/tcp(udp) 能够认为是 proc/pid/fd 的子集,然而这也差的太离谱了。 其实起因很简略,如果你在 /proc/net/tcp 下找不到,试试去 /proc/net/tcp6 下找找呢

敞开指定 socket 连贯

如果咱们再进一步,咱们当初能够找到每个 pid 下的 socket 描述符,如果我想断掉这个描述符也就是断开这个连贯,怎么做呢?用防火墙显然不是好主见,防火墙通常是针对某个 IP 和固定端口的。这个时候,socket fd 号就派上用场了。留神,fd 和 inode 是两码事。

查看以后 fd

(base) [root@manager ~]# ll /proc/1723/fd|grep socket
lrwx------ 1 root root 64 Jul  7 13:49 103 -> socket:[10232948]
lrwx------ 1 root root 64 Jul  7 13:49 105 -> socket:[9490029]
lrwx------ 1 root root 64 Jul  7 13:49 107 -> socket:[10232952]
lrwx------ 1 root root 64 Jul  7 13:49 124 -> socket:[10232954]
lrwx------ 1 root root 64 Jul  7 13:49 130 -> socket:[27456]
lrwx------ 1 root root 64 Jul  7 13:49 131 -> socket:[27458]
lrwx------ 1 root root 64 Jul  7 13:49 132 -> socket:[27460]
lrwx------ 1 root root 64 Jul  7 13:49 51 -> socket:[23447]
lrwx------ 1 root root 64 Jul  7 13:49 52 -> socket:[23448]
lrwx------ 1 root root 64 Jul  7 13:49 79 -> socket:[10882498]

当初在另外一台服务器,间接用命令行 mysql - h 连贯本机的 mysql 服务,而后再查看下 fd 列表

(base) [root@manager ~]# ll /proc/1723/fd|grep socket
lrwx------ 1 root root 64 Jul  7 13:49 103 -> socket:[10232948]
lrwx------ 1 root root 64 Jul  7 13:49 105 -> socket:[9490029]
lrwx------ 1 root root 64 Jul  7 13:49 107 -> socket:[10232952]
lrwx------ 1 root root 64 Jul  7 13:49 124 -> socket:[10232954]
lrwx------ 1 root root 64 Jul  7 13:49 130 -> socket:[27456]
lrwx------ 1 root root 64 Jul  7 13:49 131 -> socket:[27458]
lrwx------ 1 root root 64 Jul  7 13:49 132 -> socket:[27460]
lrwx------ 1 root root 64 Jul  7 13:49 51 -> socket:[23447]
lrwx------ 1 root root 64 Jul  7 13:49 52 -> socket:[23448]
lrwx------ 1 root root 64 Jul  7 13:49 79 -> socket:[10882498]
lrwx------ 1 root root 64 Jul  7 13:49 80 -> socket:[11222776]

比照下,最上面多进去的一行就是新增的那个连贯,fd=80,socket inode=11222776。

咱们应用 gdb 调用 syscall,敞开这个 fd

gdb -p 1723
call close(80)
quit

而后看一下,近程 mysql 连贯曾经断了

— 公众号

退出移动版