You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

设置ulimit -n为无限制仍遇C共享库"Too Many Files Open"错误

问题:拦截connect系统调用的共享库出现"Too Many Files Open"错误

我正在开发一个C程序,创建共享库拦截connect系统调用,将连接路由至代理服务器。代码可正常运行,但运行一段时间后会出现"Too Many Files Open"错误,尽管已将ulimit -n设置为无限制。

相关代码

toralize.c

#include "toralize.h"

Req *request(struct sockaddr_in *sock2)  {
  Req *req;

  req = malloc(reqsize);
  if (req == NULL) {
    perror("malloc");
    return NULL; // Handle the error appropriately in the calling function
  }
  req->vn = 4;
  req->cd = 1;
  req->dstip = sock2->sin_addr.s_addr;
  req->dstport = sock2->sin_port;
  strncpy(req->userid, USERNAME, 8);
  
  return req;
}

int connect(int s2, const struct sockaddr *sock2, socklen_t addrlen)  {

  int s;
  Req *req;
  Res *res;
  struct sockaddr_in sock;
  char buf[ressize];
  int success;
  int (*p)(int, const struct sockaddr*, socklen_t);

  p = dlsym(RTLD_NEXT, "connect");
  if (p == NULL) {
    perror("dlsym");
    return -1;
  }

  s = socket(AF_INET, SOCK_STREAM, 0);
  if (s < 0)  {
    perror("socket");
    return -1;
  }

  sock.sin_family = AF_INET;
  sock.sin_port = htons(PROXYPORT);
  sock.sin_addr.s_addr = inet_addr(PROXY);

  printf("Connecting to %s:%d\n", inet_ntoa(sock.sin_addr), ntohs(sock.sin_port));

  if (p(s, (struct sockaddr *)&sock, sizeof(sock)) < 0) {
    perror("connect");
    close(s);
    return -1;
  }

  printf("Connected to proxy.\n");

  req = request((struct sockaddr_in *)sock2);
  if (req == NULL) {
    close(s);
    return -1;
  }

  if (write(s, req, reqsize) < 0) {
    perror("write");
    free(req);
    close(s);
    return -1;
  }

  memset(buf, 0, ressize);
  if (read(s, buf, ressize) < 1)  {
    perror("read");
    free(req);
    close(s);
    return -1;
  }

  res = (Res *)buf;
  success = (res->cd == 90);

  if (!success) {
    fprintf(stderr, "Unable to traverse"
            "the proxy, error code: %d\n",
            res->cd);
    close(s);
    free(req);
    return -1;
  }

  printf("Connected through the proxy.\n");
  
  close(s2);
  if (dup2(s, s2) < 0) {
    perror("dup2");
    free(req);
    close(s);
    return -1;
  }
  
  close(s);
  free(req);
  return 0;
}

toralize.h

/* toralize.h */
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<dlfcn.h>
#define PROXY "127.0.0.1"
#define PROXYPORT 9050
#define reqsize sizeof(struct proxy_request)
#define ressize sizeof(struct proxy_response)
#define USERNAME "toraliz" 
typedef unsigned char int8;
typedef unsigned short int int16;
typedef unsigned int int32;

struct proxy_request  {
  int8 vn;
  int8 cd;
  int16 dstport;
  int32 dstip;
  unsigned char userid[8];
};

typedef struct proxy_request Req;

struct proxy_response {
  int8 vn;
  int8 cd;
  int16 _;
  int32 __;
};
typedef struct proxy_response Res;

Req *request(struct sockaddr_in*);
int connect(int, const struct sockaddr*, socklen_t);

编译命令

gcc toralize.c -o toralize.so -fPIC -shared -ldl -D_GNU_SOURCE

已尝试操作

  • 确认ulimit -n已设置为无限制;
  • 检查代码中可能未正确关闭文件描述符的位置。

疑问

  1. 此场景下导致"Too Many Files Open"错误的原因可能是什么?
  2. 如何更有效地修复或调试该问题?

问题分析与解决方案

可能的泄漏原因

从代码逻辑看,大部分错误路径都已正确关闭socket描述符s,但仍存在以下潜在问题:

  1. 上层应用未关闭原socket:dup2成功后,原s2被替换为代理连接的描述符,若上层应用长期持有该描述符不关闭,会导致累积泄漏;
  2. close调用失败未被检测:部分close操作可能因描述符无效等原因失败,但代码未检查返回值,无法发现此类异常;
  3. 线程安全隐患:多线程并发调用时,若未正确处理全局资源(如后续优化的dlsym缓存),可能导致描述符管理混乱;
  4. 冗余的close(s2):手动close(s2)后调用dup2,若s2已失效,可能触发不必要的错误,干扰正常流程。

调试与修复步骤

  1. 添加close错误检查:给所有close调用添加返回值校验,确保描述符被正确关闭:

    if (close(s) < 0) {
        perror("close proxy socket");
    }
    
  2. 添加文件描述符计数监控:用原子变量统计打开/关闭的描述符数量,实时监控泄漏情况:

    #include <stdatomic.h>
    atomic_int fd_counter = 0;
    
    // 创建socket成功后
    atomic_fetch_add(&fd_counter, 1);
    // 关闭socket前
    atomic_fetch_sub(&fd_counter, 1);
    // 在connect函数中打印计数
    printf("Current FD count: %d\n", atomic_load(&fd_counter));
    
  3. 用lsof定位泄漏:运行程序时,通过lsof -p <进程PID>实时查看打开的文件描述符,确认是否有大量未关闭的socket描述符。

  4. 优化dlsym调用:将系统connect函数指针缓存为静态变量,避免重复调用dlsym:

    static int (*p)(int, const struct sockaddr*, socklen_t) = NULL;
    if (p == NULL) {
        p = dlsym(RTLD_NEXT, "connect");
        if (p == NULL) {
            perror("dlsym");
            return -1;
        }
    }
    
  5. 替换过时的inet_addr:用inet_pton替代inet_addr,增强IP地址解析的错误处理:

    if (inet_pton(AF_INET, PROXY, &sock.sin_addr) != 1) {
        perror("inet_pton");
        close(s);
        return -1;
    }
    
  6. 移除冗余的close(s2):让dup2自动处理原描述符的关闭,避免重复操作引发的异常:

    // 移除 close(s2);
    if (dup2(s, s2) < 0) {
        perror("dup2");
        free(req);
        close(s);
        return -1;
    }
    

内容的提问来源于stack exchange,提问作者Faraz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 17:44:51