众所周知,在相应进程的/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

但是这只是查看限制个数。

实际在查看是否是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不够的情况。

新blog地址:

最近,线上一个应用,发现socket数缓慢增长,并且不回收,超过警告线之后,被运维监控自动重启了。

首先到zabbix上观察JVM历史记录,发现JVM-Perm space最近两周没有数据,猜测是程序从JDK7切换到JDK8了。问过开发人员之后,程序已经很久没有重启了,最近才重新发布的。而在这期间,线上的Java运行环境已经从JDK7升级到JDK8了。

因为jdk8里没有Perm space了,换成了Metaspace。

netstat

到线上服务器上,用netstat来统计进程的connection数量。

netstat -antp | grep pid | wc -l

发现比zabbix上的统计socket数量要少100多,netstat统计只有100多,而zabbix上监控数据有300多。

于是到/proc/$pid/fd下统计socket类型的fd数量:

cd /proc/$pid/fd

ls -al | grep socket | wc -l

发现数据和zabbix上的数据一致。

netstat是怎么统计的

下载netstat的源代码

apt-get source net-tools

从netstat的代码里,大概可以看到是读取/proc/net/tcp里面的数据来获取统计信息的。

java和c版的简单netstat的实现

java版的

C版的:

用starce跟踪netstat

strace netstat -antp

可以发现netstat把/proc 下的很多数据都读取出来了。于是大致可以知道netstat是把/proc/pid/fd 下面的数据和/proc/net/下面的数据汇总,对照得到统计结果的。

哪些socket会没有被netstat统计到?

又在网上找了下,发现这里有说到socket如果创建了,没有bind或者connect,就不会被netstat统计到。

实际上,也就是如果socket创建了,没有被使用,那么就只会在/proc/pid/fd下面有,而不会在/proc/net/下面有相关数据。

简单测试了下,的确是这样:

int socket = socket(PF_INET,SOCK_STREAM,0); //不使用

另外,即使socket是使用过的,如果执行shutdown后,刚开始里,用netstat可以统计到socket的状态是FIN_WAIT1。过一段时间,netstat统计不到socket的信息的,但是在/proc/pid/fd下,还是可以找到。

中间的时候,自己写了个程序,把/proc/pid/fd 下的inode和/proc/net/下面的数据比较,发现的确有些socket的inode不会出现在/proc/net/下。

用lsof查看

用lsof查看socket inode:

触发GC,回收socket

于是尝试触发GC,看下socket会不会被回收:

jmap -histo:live

结果,发现socket都被回收了。

再看下AbstractPlainSocketImpl的finalize方法:

/**

* Cleans up if the user forgets to close it.

*/

protected void finalize() throws IOException {

close();

}

可以看到socket是会在GC时,被close掉的。

写个程序来测试下:

public class TestServer {

public static void main(String[] args) throws IOException, InterruptedException {

for(int i = 0; i < 10; ++i){

ServerSocket socket = new ServerSocket(i + 10000);

System.err.println(socket);

}

System.in.read();

}

}

先执行,查看/proc/pid/fd,可以发现有相关的socket fd,再触发GC,可以发现socket被回收掉了。

其它的东东

anon_inode:[eventpoll]

ls -al /proc/pid/fd

可以看到有像这样的输出:

661 -> anon_inode:[eventpoll]

这种类型的inode,是epoll创建的。

再扯远一点,linux下java里的selector实现是epoll结合一个pipe来实现事件通知功能的。所以在NIO程序里,会有anon_inode:[eventpoll]和pipe类型的fd。

为什么tail -f /proc/$pid/fd/1 不能读取到stdout的数据

总结

原因是jdk升级之后,GC的工作方式有变化,FullGC执行的时间变长了,导致有些空闲的socket没有被回收。

本文比较乱,记录下一些工具和技巧。

java套接字创建失败_Linux的文件描述符个数限制导致创建文件(或socket)失败的问题...相关推荐

  1. C语言socket accept()函数(提取出所监听套接字的等待连接队列中第一个连接请求,创建一个新的套接字,并返回指向该套接字的文件描述符)

    文章目录 名称 使用格式 功能参数描述 参数 sockfd addr addrlen 返回值 示例 man 2 文档中的accept解释 错误处理 名称 accept() 接收一个套接字中已建立的连接 ...

  2. java套接字客户端_使用Java从客户端套接字读取数据(Read data from a client socket in Java)...

    使用Java从客户端套接字读取数据(Read data from a client socket in Java) 我编写了从客户端套接字发送/接收数据的代码. 发送数据步骤已成功完成,但是当我想从套 ...

  3. Java 套接字(Socket)

    网络应用模式主要有: 主机/终端模式:集中计算,集中管理: 客户机/服务器(Client/Server,简称C/S)模式:分布计算,分布管理: 浏览器/服务器模式:利用Internet跨平台. www ...

  4. java套接字通信_JAVA套接字实现简易的双人通信系统

    JAVA套接字实现简易的双人通信系统 JAVA套接字资料 socket是基于应用服务与TCP/IP通信之间的一个抽象,他将TCP/IP协议里面复杂的通信逻辑进行分装,对用户来说,只要通过一组简单的AP ...

  5. java套接字编程_Java套接字编程:教程

    java套接字编程 本教程是Java套接字编程的简介,从一个简单的客户机-服务器示例开始,该示例演示了Java I / O的基本功能. 将向您介绍原始的java.io软件包和NIO,即Java 1.4 ...

  6. java jai create 方法_Java-JAI创建似乎使文件描述符保持打开状态

    我有一些旧代码,直到最近仍在工作,但是现在看来已经讨厌了,因为它可以在使用OpenJDK 6而不是Java SE 6的新服务器上运行. 这个问题似乎与JAI.create有关.我有jpeg文件,可以缩 ...

  7. java最大文件描述符,java – 为什么JDK NIO使用这么多的anon_inode文件描述符?

    我正在使用Sun的JDK 1.6.0_26和NIO(使用Netty),在lsof中我看到数百个文件描述符是anon_inode: $lsof -np 11225 | fgrep -w anon_ino ...

  8. Linux下利用文件描述符恢复的成功失败实验

    数据误删除是作为初级运维人员常常遇到的"低级错误",一些有经验的老手有时也在疲劳.不冷静的情况下"马失前蹄".一旦误删除数据文件,尽快采用影响最小.最迅速的手段 ...

  9. Java 套接字Socket

    套接字 一.概述 本质上是一套基于网络传输数据的流 实际上是一套用于网络通信的API IP地址 IPv4是指由4组数组成的IP地址 , 每组数的范围在0~255 .一共有2的32次方个地址(43亿多) ...

最新文章

  1. 在组策略中通过.zap的形式发布软件!
  2. java常用类解析十:Date类和Calendar类示例
  3. orcle10忘记密码
  4. 基于HLS格式的低延时互动直播技术
  5. 大数据 -- zookeeper和kafka集群环境搭建
  6. 有奖问题征集|向大咖Scott 发问,好礼等你领!
  7. python全局变量赋值_Python全局变量和局部变量
  8. CVPR2019 oral 目标跟踪算法之SiamRPN++
  9. Nginx设置日志打印post请求参数
  10. UNIX环境高级编程之第3章:文件I/O
  11. Java中swing使用ImageIcon类添加图片
  12. 连点器---鼠标连续点击(c语言)
  13. html5小游戏塔防,HTML5塔防(一)
  14. 【洛谷P4234】最小差值生成树
  15. HDU-5755-Gambler Bo-高斯消元
  16. pythonU盘小偷并发送邮箱
  17. opencv简易数字识别
  18. 推荐几个学习linux的国外著名论坛网站
  19. 铠甲进化!套上它,毛绒玩具也会动起来
  20. 字符设备、块设备与网络设备

热门文章

  1. 用calc()绘制手机图案解锁的九宫格样式
  2. Elasticsearch+Spring Boot集成实践
  3. ssm微信小程序美容理发店预约系统app——计算机毕业设计
  4. win10开始菜单打不开_windows10系统电脑开始菜单无法打开的解决教程
  5. S32K14x CAN休眠唤醒的实现方案
  6. 什么是软件压力测试,靠谱的出压力测试报告的软件第三方测评机构推荐
  7. MySql实验嵌套查询_数据库实验:SQL嵌套查询
  8. java谷歌填表_chrome自动填表会遮挡input中背景图的问题解决方法
  9. 离线翻译android开发,有道翻译官推出Android版 可离线翻译多国语言
  10. 获取淘宝商品历史价格信息API(PHP,JAVA都可对接)