EmacsTalk

修复 Emacs 在 macOS 下最大打开文件数为 1024 的问题

  tags: macOS

文章目录

不知道从哪个版本开始,macOS 最大文件数(max open files)改成了 1024,这对于使用 lsp 进行开发来说,显得有些小。而且这个问题并不能简单通过调大 ulimit 解决,在这个 reddit 帖子里,rpluim 用户提到:

Emacs uses pselect, which is limited to FD_SETSIZE file descriptors, usually 1024. I suspect you've got one of the file-watching utilities enabled in emacs, which tends to use up a lot of file descriptors.

Increasing the maxfiles limit will not change the value of FD_SETSIZE compiled into emacs and the macOS libraries. Emacs would have to move to using poll or kqueue to fully solve this issue.

在 macOS 的开发者文档里也能找到印证:

The default size FD_SETSIZE (currently 1024) is some-what somewhat what smaller than the current kernel limit to the number of open files. However, in order to accommodate programs which might potentially use a larger number of open files with select, it is possible to increase this size within a program by providing a larger definition of FD_SETSIZE before the inclusion of <sys/types.h>.

但是文档里没提到怎么改,搜了下找到了一个 erlang 的类似问题,里面有提到怎么修改:

1CFLAGS="-DFD_SETSIZE=10000 -DDARWIN_UNLIMITED_SELECT"

Great!经过一番测试,成功将 emacs 的最大文件数改成 10000,这里总结下步骤:

  1. 调大系统级别 ulimit 的限制,可参考这个 gistMac OS X下的资源限制。新建文件 limit.maxfiles.plist

     1<?xml version="1.0" encoding="UTF-8"?>
     2<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
     3     "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
     4<plist version="1.0">
     5<dict>
     6 <key>Label</key>
     7 <string>limit.maxfiles</string>
     8 <key>ProgramArguments</key>
     9 <array>
    10   <string>launchctl</string>
    11   <string>limit</string>
    12   <string>maxfiles</string>
    13   <string>64000</string>
    14   <string>524288</string>
    15 </array>
    16 <key>RunAtLoad</key>
    17 <true/>
    18 <key>ServiceIPC</key>
    19 <false/>
    20</dict>
    21</plist>

    上面的 xml 含义是把最大文件打开数的 soft/hard 限制分别改成 64000 与 524288。

    1sudo chown root:wheel /Library/LaunchDaemons/limit.maxfiles.plist
    2sudo chmod 644 /Library/LaunchDaemons/limit.maxfiles.plist
    3# 加载plist文件
    4sudo launchctl load -w /Library/LaunchDaemons/limit.maxfiles.plist
    5# 确认已经生效
    6launchctl limit maxfiles
    7# 或
    8ulimit -n
  2. 从源码编译 Emacs,在 configure 时指定 CFLAGS 参数。参考命令:

     1git clone https://git.savannah.gnu.org/emacs && cd emacs
     2git checkout emacs-28
     3git clean -xf
     4
     5./autogen.sh
     6./configure "CFLAGS=-DFD_SETSIZE=10000 -DDARWIN_UNLIMITED_SELECT"
     7
     8export CPATH=`xcrun --show-sdk-path`/usr/include:`xcrun --show-sdk-path`/usr/include/libxml2
     9
    10make -j 4 && make install

    这里指定 FD_SETSIZE 为 1w,需要小于 xml 中的 hard 限制。之后打开新编译的 Emacs 进行测试:

    1(shell-command-to-string "ulimit -n")
    2;; 10000
    3
    4(dotimes (i 2000)
    5(make-process
    6:name (format "Sleep-%s" i)
    7:buffer nil
    8:command '("sleep" "60000")
    9:connection-type 'pipe))

    上面的 dotimes 代码块创建了 2000 个进程,之后用 lsof -p ${emacs_pid} | wc -l 查看打开的文件数,可以看到是 4000 多个,应该是一个 process 会打开两个文件:stdout 与 stderr。

这样,Emacs 最大文件打开数就成功修改了!

签名

如果在启动 Emacs 时,遇到下面的错误:

1Killed: 9
2Exception Type:  EXC_BAD_ACCESS (SIGKILL (Code Signature Invalid))

可以尝试重新签名

1codesign --force --deep --sign - /Applications/Emacs.app/

收听方式

反馈