Thông báo lỗi
Bạn đang thực hiện một tiến trình build nặng hoặc khởi động cơ sở dữ liệu cục bộ thì bỗng nhiên mọi thứ dừng lại. Terminal của bạn hiển thị một loạt văn bản, thường kết thúc bằng dòng này:
Error: EMFILE: too many open files, open '/path/to/your/project/file.js'
Đôi khi, lỗi này còn khó hiểu hơn khi nó từ chối cho phép bạn thay đổi cài đặt theo cách thủ công:
ulimit: open files: cannot modify limit: Invalid argument
Nguyên nhân gốc rễ: Cơ chế bảo vệ của macOS
Mặc định, macOS khá "keo kiệt" với các file descriptor. Nó thường giới hạn một tiến trình duy nhất chỉ ở mức 256 hoặc 1.024 tệp. Mặc dù con số đó là đủ vào một thập kỷ trước, nhưng việc phát triển phần mềm hiện đại tiêu tốn nhiều tài nguyên hơn thế.
Hãy xem xét một dự án Next.js hoặc Vite tiêu chuẩn. Một lệnh npm install duy nhất có thể tải về hơn 30.000 tệp trong thư mục node_modules. Khi trình biên dịch của bạn cố gắng lập chỉ mục (index) các tệp này cùng một lúc, nó sẽ chạm tới giới hạn 1.024 đó gần như ngay lập tức. Hệ điều hành sẽ can thiệp, hủy yêu cầu và kích hoạt lỗi EMFILE để bảo vệ tài nguyên hệ thống.
Giải pháp 1: Khắc phục nhanh (Chỉ áp dụng cho Terminal hiện tại)
Nếu bạn chỉ cần hoàn tất một bản build ngay lúc này, bạn có thể tăng giới hạn cho tab terminal đang hoạt động. Thay đổi này chỉ mang tính tạm thời và sẽ biến mất ngay khi bạn đóng cửa sổ.
Trước tiên, hãy kiểm tra hạn chế hiện tại của bạn:
ulimit -n
Nếu kết quả trả về là 256 hoặc 1024, mức này quá thấp. Hãy chạy lệnh này để nâng giới hạn lên 65.536 cho phiên làm việc này:
ulimit -n 65536
Thử chạy lại bản build của bạn. Bây giờ nó sẽ có đủ không gian để hoàn tất mà không bị treo.
Giải pháp 2: Thiết lập vĩnh viễn cho Shell
Hầu hết người dùng macOS sử dụng Zsh. Để tránh phải chạy lệnh ulimit mỗi khi bắt đầu làm việc, hãy thêm nó vào tệp cấu hình shell của bạn.
- Mở tệp cấu hình của bạn:
nano ~/.zshrc
- Dán dòng này vào dưới cùng:
# Tăng giới hạn tệp mở tối đa cho phát triển
ulimit -n 65536
- Lưu và thoát bằng cách nhấn
Ctrl + O,Enter, sau đó làCtrl + X. - Buộc các thay đổi có hiệu lực:
source ~/.zshrc
Giải pháp 3: Khắc phục trên toàn hệ thống (Khuyên dùng)
Các tinh chỉnh shell sẽ không có tác dụng nếu IDE của bạn (như VS Code) hoặc một dịch vụ chạy ngầm (như MongoDB) là nguyên nhân gây lỗi. Những ứng dụng này không phải lúc nào cũng kế thừa cài đặt từ .zshrc. Để khắc phục vấn đề này ở cấp độ kernel, bạn cần sử dụng launchctl.
Làm theo các bước sau để tạo cấu hình hệ thống vĩnh viễn:
- Tạo một tệp cấu hình cấp hệ thống mới:
sudo nano /Library/LaunchDaemons/limit.maxfiles.plist
- Chèn đoạn mã XML sau. Đoạn mã này thiết lập giới hạn "mềm" (soft limit) là 64k và giới hạn hệ thống "cứng" (hard limit) là 512k:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>limit.maxfiles</string>
<key>ProgramArguments</key>
<array>
<string>launchctl</string>
<string>limit</string>
<string>maxfiles</string>
<string>65536</string>
<string>524288</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>ServiceIPC</key>
<false/>
</dict>
</plist>
- Bảo mật quyền hạn tệp để hệ thống chấp nhận:
sudo chown root:wheel /Library/LaunchDaemons/limit.maxfiles.plist
sudo chmod 644 /Library/LaunchDaemons/limit.maxfiles.plist
- Tải các cài đặt mới:
sudo launchctl load -w /Library/LaunchDaemons/limit.maxfiles.plist
- Khởi động lại máy Mac của bạn. Việc khởi động lại hoàn toàn đảm bảo các thay đổi cấp kernel này được áp dụng cho mọi ứng dụng bạn mở.
Xác minh kết quả
Sau khi khởi động lại, hãy xác minh rằng các giới hạn mới đã hoạt động. Mở một terminal mới và chạy:
launchctl limit maxfiles
Bạn sẽ thấy 65536 và 524288 trong kết quả trả về. Nếu các con số vẫn thấp, hãy kiểm tra lại cú pháp XML trong tệp .plist của bạn.
Phòng ngừa và các phương pháp hay nhất
- Quản lý Watchers: Các công cụ như
nodemonhoặcchokidarsử dụng trình theo dõi tệp (file watchers) để phát hiện thay đổi. Nếu bạn để năm dự án khác nhau cùng chạy, chúng sẽ nhanh chóng làm cạn kiệt hạn ngạch của bạn. Hãy đóng những gì bạn không sử dụng. - Kiểm tra node_modules: Đảm bảo IDE của bạn không cố gắng lập chỉ mục toàn bộ thư mục
node_modulesđể tìm kiếm kết quả. Hầu hết các trình soạn thảo hiện đại đều loại trừ thư mục này theo mặc định, nhưng bạn vẫn nên kiểm tra lại cài đặt của mình. - Dọn dẹp các kết nối: Nếu bạn đang viết các tập lệnh tùy chỉnh, hãy luôn đóng các luồng dữ liệu tệp (file streams) và kết nối cơ sở dữ liệu. Ngay cả giới hạn 65k cũng không thể cứu bạn khỏi tình trạng rò rỉ bộ nhớ (memory leak) khiến các descriptor luôn ở trạng thái mở vô thời hạn.

