如何在Mac OS X上增加C语言中“最大打开文件数”的限制

6

在Mac OS X上,最大打开文件数的默认限制是256(ulimit -n),而我的应用程序需要大约400个文件句柄。

我尝试使用setrlimit()更改限制,但即使函数执行正确,我仍然受到256的限制。

这是我使用的测试程序:

#include <stdio.h>
#include <sys/resource.h>

main()
{
  struct rlimit rlp;

  FILE *fp[10000];
  int i;

  getrlimit(RLIMIT_NOFILE, &rlp);
  printf("before %d %d\n", rlp.rlim_cur, rlp.rlim_max);

  rlp.rlim_cur = 10000;
  setrlimit(RLIMIT_NOFILE, &rlp);

  getrlimit(RLIMIT_NOFILE, &rlp);
  printf("after %d %d\n", rlp.rlim_cur, rlp.rlim_max);

  for(i=0;i<10000;i++) {
    fp[i] = fopen("a.out", "r");
    if(fp[i]==0) { printf("failed after %d\n", i); break; }
  }

}

输出为:

before 256 -1
after 10000 -1
failed after 253

我不能要求使用我的应用程序的人去查看/etc文件或其他文件。我需要应用程序自行完成这项任务。

为什么你需要同时打开这么多文件呢? - sbooth
不是很重要,但你是在服务器版还是桌面版的OSX上测试这个?我可以想象苹果公司决定限制桌面应用程序打开文件的数量,因为打开许多文件通常是面向服务器的任务... - Evan Teran
6个回答

5

rlp.rlim_cur = 10000;

有两个问题。

第一个,LOL。你似乎在 Mac OS X 的 stdio 中发现了一个 bug。如果我修复你的程序并添加错误处理等,并将 fopen() 替换为 open() 系统调用,则可以轻松达到 10000 的限制(这比我的 10.6.3 的 OPEN_MAX 限制 10240 低 240 个文件描述符)。

其次,请看手册:man setrlimit。关于 OPEN_MAX,最大打开文件数的情况必须进行特殊处理。


1
谢谢你的回答。当你说这可能是Mac OS X上stdio的一个bug时,你是认真的还是开玩笑?所以唯一的解决方案是使用syscall而不是标准C函数? - acemtp
@acemtp:“限制”可能是一个更好的词。标准只要求libc保证您可以同时打开8个文件(包括stdin/stdout/stderr!)。这可能是一个不寻常的限制,但并非闻所未闻。 - Evan Teran
1
@acetemp,@evan:在Linux上,stdio没有问题应对我所遇到的任何情况。我个人认为这是一个错误。一次同时处理8个文件?stdio、stdin、stderr - 已经有3个在忙了。应用程序日志文件+跟踪文件 - 只剩下3个空闲...如果你问我,这很愚蠢也是一个错误。 - Dummy00001

5

etresoft在苹果讨论区找到了答案:

问题出在你的printf()函数上。当你调用printf()时,你正在将内部数据结构初始化为特定大小。然后,你调用setrlimit()来尝试调整这些大小。那个函数失败了,因为你已经在使用那些带有printf()的内部结构。如果你使用两个rlimit结构(一个用于之前,一个用于之后),并且在调用setrlimit之后再打印它们,你会发现即使在命令行程序中也可以更改当前进程的限制。最大值为10240。


3

由于某种原因(可能是二进制兼容性),您需要在包含<stdio.h>之前定义_DARWIN_UNLIMITED_STREAMS

#define _DARWIN_UNLIMITED_STREAMS

#include <stdio.h>
#include <sys/resource.h>

main()
{
  struct rlimit rlp;

  FILE *fp[10000];
  int i;

  getrlimit(RLIMIT_NOFILE, &rlp);
  printf("before %d %d\n", rlp.rlim_cur, rlp.rlim_max);

  rlp.rlim_cur = 10000;
  setrlimit(RLIMIT_NOFILE, &rlp);

  getrlimit(RLIMIT_NOFILE, &rlp);
  printf("after %d %d\n", rlp.rlim_cur, rlp.rlim_max);

  for(i=0;i<10000;i++) {
    fp[i] = fopen("a.out", "r");
    if(fp[i]==0) { printf("failed after %d\n", i); break; }
  }

}

打印
before 256 -1
after 10000 -1
failed after 9997

这个功能似乎是在Mac OS X 10.6中引入的。

2
这可能是您的libc的一个严格限制。一些solaris版本也有类似的限制,因为它们将fd存储为FILE结构中的unsigned char。如果您的libc也是这种情况,您可能无法做您想要做的事情。
据我所知,像setrlimit之类的东西只影响您可以使用open打开多少个文件(fopen几乎肯定是以open为基础实现的)。因此,如果这种限制在libc级别上,您需要另一种解决方案。
当然,您可以不使用fopen,而是使用几乎每个unix变体都可用的open系统调用。
缺点是您必须使用writeread,而不是fwritefread,后者不会执行缓冲等操作(所有这些操作都由您的libc而不是操作系统本身完成)。因此,这可能成为性能瓶颈。
您能描述一下需要同时打开400个文件的场景吗?我并不是说没有需要这样做的情况。但是,如果您更清楚地描述您的用例,那么也许我们可以推荐更好的解决方案。

libc限制:是的。请看我的评论。将程序更改为使用open()而不是fopen()可以解决问题。在Linux上,这很好用 - 在进行明显的修复(用rlp.rlim_max替换10000),但在Mac OS X上,即使检查OPEN_MAX的容量也是不同的。需要400个文件描述符的情况...我维护专业网络服务器,还将数据备份到磁盘。看到2K个套接字和打开的文件并不罕见。 - Dummy00001
@Dummy00001:好的,那肯定是一个场景,但让acemtp描述他想要做什么仍然有助于解决问题 :-P。但看起来我们已经找到了问题的本质。 - Evan Teran

0

我知道这听起来是个愚蠢的问题,但你真的需要同时打开400个文件吗? 顺便问一下,你是以root身份运行这段代码吗?


是的,我需要同时打开400个文件,但我不是以root身份执行。正如man所说,由于我不改变最大限制,而只是当前限制,所以我不需要root权限。 - acemtp
3
但是最大限制不会限制当前限制吗? - nategoose

-1

Mac OS不像许多基于Unix的操作系统那样容易更改限制。我们需要创建两个文件:

/Library/LaunchDaemons/limit.maxfiles.plist /Library/LaunchDaemons/limit.maxproc.plist 描述最大进程和最大文件限制。文件的所有权需要更改为“root:wheel”。

仅凭这些还无法解决问题,因为默认情况下最新版本的Mac OSX使用“csrutil”,我们需要禁用它。要禁用它,我们需要在恢复模式下重新启动Mac,并从那里使用终端禁用csrutil。

现在,我们可以轻松地从终端本身(即使在正常引导模式下)轻松更改最大打开文件句柄限制。

此方法在以下链接中详细说明。 http://blog.dekstroza.io/ulimit-shenanigans-on-osx-el-capitan/

适用于OSX El Capitan和OSX Seirra。


OP 询问如何在 C 语言中以编程方式实现此操作,而不是在用户级别上。 - Paul R

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接