转载地址:http://blog.csdn.net/tspangle/article/details/20543329
 
      转载地址:http://blog.sina.com.cn/s/blog_62ec29160101qus8.html
 
 
 
      感谢两位作者!
 
 
 
 
 
问题定位步骤: 
 1、 用root帐户 遍历 /proc/进程ID/fd目录,如果该目录下文件数比较大(如果大于10,一般就属于socket泄漏),根据该进程ID,可以确认该进程ID所对应的名称。 
 2、 重启程序恢复服务,以便后续查找问题。 
 3、 strace 该程序并记录strace信息。strace –p 进程ID >>/tmp/stracelog.log 2>&1 
 4、 查看 /proc/进程ID/fd 下的文件数目是否有增加,如果发现有增加,记录上一个socket编号,停止strace 
 5、 确认问题代码的位置。打开/tmp/stracelog.log,从尾部向上查找close(socket编号)所在行,可以确认在该次close后再次 创建的socket没有关闭,根据socket连接的server ip可以确认问题代码的位置。 
 
 
 另一种方法:判断是否有socket泄漏:  
 lsof | grep "can't identify protocol" 
 如果存在很多,则代表socket泄漏,同时会显示哪个进程使用的sock未关闭。
 
 
 
 
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 
 
 
 
 
 
 
 问题
 
  用 lsof 会看到 "can't identify protocol" 的 socket fd,很奇怪,啥情况下会这样呢?
 
  不求甚解下,google 了一把,得到了个错误的结论:
 
 
 
 
 
 
  先翻了下 lsof 的代码,发现 "can't identify protocol" 在 lsof_4.82_src/dialects/linux/dsock.c 下,说明这串文字并不是从 linux kernel 里面出来的。
 
 
 
 
 
 
 试验过程
 
  写了个简单的小程序,用 lsof 看一下。
 
  ===================== sock1.c ====================
 
  #include 
 
 <</span>stdio.h>
 
  #include 
 
 <</span>stdlib.h>
 
  #include 
 
 <</span>errno.h>
 
  #include 
 
 <</span>sys/resource.h>
 
  #include 
 
 <</span>sys/socket.h>
 
  #include 
 
 <</span>arpa/inet.h>
 
  #include 
 
 <</span>netinet/in.h>
 
  #include 
 
 <</span>unistd.h>
 
  #include 
 
 <</span>assert.h>
 
  #include 
 
 <</span>string.h>
 
 
 
 
 
  int main()
 
  {
 
   
  
   
 int fd, r;
 
   
  
   
 struct sockaddr_in sa;
 
   
  
   
 fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 
 
  ==================================================
 
  在 openbsd 下:
 
 
 
  COMMAND  
  PID  
    
   USER  
   FD  
   TYPE  
    
   DEVICE SIZE/OFF  
  NODE NAME
 
 
 
  a.out  
   1649 kasicass  
    
  3u  
  IPv4 0xd51d9af4  
    
    
  0t0  
   TCP *:* (CLOSED)
 
 
 
  
 
 
 
  
  在 debian 下:
 
 
 
  COMMAND  
  PID  
    
   USER  
   FD  
   TYPE DEVICE  
    
  SIZE  
   NODE NAME
 
 
 
  a.out  
   2356 kasicass  
    
  3u  
  sock  
    
  0,4  
    
    
    
    
  42992 can't identify protocol
 
 
 
  
 
 
 
 
  很奇怪哦,正确创建的 socket fd 居然显示 "can't identify protocol"。
 
 
 
  
 
 
 
 
  
 
 
 
 
  然后我增加一个 connect() 看看。
 
 
 
  ===================== sock2.c ====================
 
 
  
  
   int main()
  
 
  
   {
  
 
  
    
    
     
   int fd, r;
  
 
  
    
    
     
   struct sockaddr_in sa;
  
 
  
   
 
  
 
  
    
    
     
   fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  
 
  
   
 
  
 
  
    
    
     
   memset(&sa, 0, sizeof(sa));
  
 
  
    
    
     
   sa.sin_addr.s_addr = inet_addr("192.168.0.88");
  
 
  
    
    
     
   sa.sin_family  
     
     
   = AF_INET;
  
 
  
    
    
     
   sa.sin_port  
     
     
     
   = htons(2224);
  
 
  
    
    
     
   r = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
  
 
  
    
    
     
   assert(r != -1);
  
 
  
    
    
     
   close(fd);
  
 
  
   
 
  
 
  
    
    
     
   sleep(60);
  
 
  
    
    
     
   return 0;
  
 
   
 
  ================================================
 
 
 
  在 debian 下,连接成功:
 
 
  
  
   a.out  
    2979 kasicass  
     
   3u  
   IPv4  
   44790  
     
     
   0t0  
     
   TCP 10.0.2.15:58282->192.168.0.88:2224 (ESTABLISHED)
  
 
  
   如果连接不成功:
  
 
  
   a.out  
    3001 kasicass  
     
   3u  
   IPv4  
   44885  
     
     
   0t0  
     
   TCP 10.0.2.15:58283->192.168.0.88:2224 (SYN_SENT)
  
 
  
   
 
  
 
  
   恩,再试了下 listen() 的情况:
  
 
  
   ====================== sock3.c ===================
  
 
   
   
    static int bind_and_listen(int fd, int port)
   
 
   
    {
   
 
   
     
     
      
      
      
    struct sockaddr_in in_addr;
   
 
   
     
     
      
      
      
    int reuseaddr_on = 1;
   
 
   
    
 
   
 
   
     
     
      
      
      
    // addr reuse
   
 
   
     
     
      
      
      
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuseaddr_on, sizeof(reuseaddr_on)) == -1)
   
 
   
     
     
      
      
      
      
      
      
      
    return -1;
   
 
   
    
 
   
 
   
     
     
      
      
      
    // bind & listen
   
 
   
     
     
      
      
      
    bzero(&in_addr, sizeof(in_addr));
   
 
   
     
     
      
      
      
    in_addr.sin_family  
      
      
    = AF_INET;
   
 
   
     
     
      
      
      
    in_addr.sin_addr.s_addr = htonl(INADDR_ANY);
   
 
   
     
     
      
      
      
    in_addr.sin_port  
      
      
      
    = htons(port);
   
 
   
    
 
   
 
   
     
     
      
      
      
    if ( bind(fd, (struct sockaddr *) &in_addr, sizeof(in_addr)) == -1 )
   
 
   
     
     
      
      
      
      
      
      
      
    return -1;
   
 
   
    
 
   
 
   
     
     
      
      
      
    if ( listen(fd, 64) == -1 )
   
 
   
     
     
      
      
      
      
      
      
      
    return -1;
   
 
   
    
 
   
 
   
     
     
      
      
      
    return fd;
   
 
   
    }
   
 
   
    
 
   
 
   
    int main()
   
 
   
    {
   
 
   
     
     
      
    int fd, r;
   
 
   
    
 
   
 
   
     
     
      
    fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
   
 
   
     
     
      
    r  
    = bind_and_listen(fd, 2224);
   
 
   
     
     
      
    assert(r != -1);
   
 
   
    
 
   
 
   
     
     
      
    sleep(60);
   
 
   
     
     
      
    return 0;
   
 
   
    }
   
 
    
  
   ================================================
  
 
  
   如果 bind + listen 结果是:
  
 
  
   a.out  
    3052 kasicass  
     
   3u  
   IPv4  
   45083  
     
     
   0t0  
     
   TCP *:2224 (LISTEN)
  
 
  
   如果只是 bind 则:
  
 
  
   a.out  
    3074 kasicass  
     
   3u  
   sock  
     
   0,4  
     
     
   0t0  
   45174 can't identify protocol
  
 
  
   
 
  
 
  
   结论
  
 
  
   没有再细致去看代码,不过可以推断,linux 下,lsof 对于没有 connect() or listen() 的 socket fd,都是显示 "can't identify protocol"。
  
 
  
   而bsd下则会正确显示出 socket type。
  
 
  
   
 
  
 
  
   ps. 据说shutdown后没有close会出现这个情况,待验证。:-)