众所周知,在相应进程的/proc/$pid/fd 目录下存放了此进程所有打开的fd。当然有些可能不是本进程自己打开的,如通过fork()从父进程继承而来的。本文着着重讲述socket有关的内容。当我们在fd目录下使用 ls -l 命令查看时,会看到诸如下面的内容:
lrwx------ 1 root root 64 Nov 21 09:44 133 -> /dev/sda1
lrwx------ 1 root root 64 Nov 21 09:44 134 -> /dev/sdb1
lrwx------ 1 root root 64 Nov 21 09:44 136 -> /dev/sdb1
lrwx------ 1 root root 64 Nov 21 09:44 137 -> socket:[22460]
lrwx------ 1 root root 64 Nov 21 09:44 138 -> socket:[7326842]
lrwx------ 1 root root 64 Nov 21 09:44 139 -> socket:[7341066]
那么这个socket:后面的一串数字是什么呢?其实是该socket的inode号。从linux内核代码net/socket.c 中可以看出,如下
/*
* sockfs_dname() is called from d_path().
*/
static char *sockfs_dname(struct dentry *dentry, char *buffer, int buflen)
{
return dynamic_dname(dentry, buffer, buflen, "socket:[%lu]",
dentry->d_inode->i_ino);
}
那么,知道了某个进程打开的socket的inode号后,我们可以做什么呢?这就涉及到/proc/net/tcp(udp对应/proc/net/udp)文件了,其中也列出了相应socket的inode号通过比对此字段,我们能在/proc/net/tcp下获得此套接口的其他信息,如对应的<本地地址:端口号,远端地址:端口号>对,窗口大小,状态等信息。具体字段含义详见net/ipv4/tcp_ipv4.c 中的 tcp4_seq_show 函数。cat /proc/net/tcp 如下:
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
19: 0100007F:83B8 0100007F:A57D 01 00000000:00000000 00:00000000 00000000 0 0 10879 1 f622edc0 20 4 31 3 -1
20: 0100007F:0FA0 0100007F:AA06 01 00000000:00000000 00:00000000 00000000 0 0 7326842 1 f5504dc0 20 4 11 5 -1
碰到一个文件fd数量不足,导致socket创建失败的问题。
而文件描述符即fd个数其实分为两种,一种是系统本身的总的限制个数,另一种是进程能够打开的具体的限制的个数。
系统最大打开文件描述符数:/proc/sys/fs/file-max
a. 查看
$ cat /proc/sys/fs/file-max
2. 设置
a. 临时性
# echo 1000000 > /proc/sys/fs/file-max
2. 永久性:在/etc/sysctl.conf中设置
fs.file-max = 1000000
2. 进程最大打开文件描述符数:user limit中nofile的soft limit
a. 查看
$ ulimit -n
1800000
2. 设置
a. 临时性:通过ulimit -Sn设置最大打开文件描述符数的soft limit,注意soft limit不能大于hard limit(ulimit -Hn可查看hard limit),另外ulimit -n默认查看的是soft limit,但是ulimit -n 1800000则是同时设置soft limit和hard limit。对于非root用户只能设置比原来小的hard limit。
查看hard limit:
$ ulimit -Hn
1700000
设置soft limit,必须小于hard limit:
$ ulimit -Sn 1600000
2. 永久性:上面的方法只是临时性的,注销重新登录就失效了,而且不能增大hard limit,只能在hard limit范围内修改soft limit。若要使修改永久有效,则需要在/etc/security/limits.conf中进行设置(需要root权限),可添加如下两行,表示用户chanon最大打开文件描述符数的soft limit为1800000,hard limit为2000000。以下设置需要注销之后重新登录才能生效:
chanon soft nofile 1800000
chanon hard nofile 2000000
设置nofile的hard limit还有一点要注意的就是hard limit不能大于/proc/sys/fs/nr_open,假如hard limit大于nr_open,注销后无法正常登录。可以修改nr_open的值:
# echo 2000000 > /proc/sys/fs/nr_open
3. 查看当前系统使用的打开文件描述符数
[root@localhost bin]# cat /proc/sys/fs/file-nr
5664 0 186405
其中第一个数表示当前系统已分配使用的打开文件描述符数,第二个数为分配后已释放的(目前已不再使用),第三个数等于file-max。
4. 总结:
a. 所有进程打开的文件描述符数不能超过/proc/sys/fs/file-max
b. 单个进程打开的文件描述符数不能超过user limit中nofile的soft limit
c. nofile的soft limit不能超过其hard limit
d. nofile的hard limit不能超过/proc/sys/fs/nr_open
上述的信息来自于Linux最大打开文件描述符数_系统的最大打开文件数_SuperChanon的博客-CSDN博客
但是这只是查看限制个数。
实际在查看是否是fd打开数量太多导致失败,还需要从进程中去查看,其方法:
1.当前进程分配过的文件描述符的近似的最高值
cat /proc/进程号/status
在这个文件中会有一个FDSize字段,该字段是表示当前进程分配过的文件描述符的近似的最高值。为什么是近似,因为这里FDSize本身是不会减少的,如果刚开始打开了18个文件,则这里的FDSize等于32,若大于32,则以32为单位递增,例如33则是64;这个32的单位是依赖于系统位数的,如果是32位则是32,若是64位系统则是以64倍增。
2.实际的已经打开的fd
ls -l /proc/进程号/fd | wc
可以统计实际打开的fd个数
在碰到的这个实际问题中,相关进程的FDSize一起来就已经到了1024,而用ulimit -n查看进程打开的fd限制刚好是1024,再用/proc/进程号/fd中去查看个数是五百多个,这时还是未真正出错的时候。但是相对来讲未起业务就已经开了很多fd了,正常运行业务需要大量socket进行传输,自然就出现了fd不够的情况。
文章讲述了Linux系统中,进程通过/proc/$pid/fd目录下的inode号来追踪打开的socket,并可以通过/proc/net/tcp获取更多信息。讨论了系统和进程的最大文件描述符限制,包括如何查看和调整这些限制,以及如何检查进程的FDSize和实际打开的fd数量。当进程fd数量接近限制时,可能会导致socket创建失败。
1525

被折叠的 条评论
为什么被折叠?



