Hệ điều hành cho lập trình viên

Sê-ri 45 bài đo thật hệ điều hành trong container: syscall, tiến trình, bộ nhớ, lịch CPU, I/O, cgroup — mỗi bài một phép đo, một sơ đồ, một lần tự sửa mình.

45/45 phần đã đăng Hệ điều hành
1 clock_gettime nhanh gấp 8 lần getpid dù cả hai là 'syscall' — vì một cái không hề bẫy vào nhân Một lời gọi hàm thường tốn 0,8ns; một syscall getpid bẫy vào nhân tốn ~113ns (gấp 140 lần). Nhưng clock_gettime chỉ 15ns, và tôi suýt viết 'syscall có thể rất rẻ'. Sự thật: nó không phải syscall — nó chạy trong vDSO, và strace không hề thấy nó. Đo thật bằng C và strace. 03/09/2026 · 9 phút đọc 2 'fork nhờ COW nên gần như miễn phí' — nửa đúng nửa nguy: fork 1GB vẫn tốn gấp 50 lần fork 1MB fork() không sao chép dữ liệu nhờ copy-on-write (fork 1GB 1,6ms so với memcpy 490ms, nhanh hơn 310 lần), nhưng nó vẫn tốn O(bộ nhớ) vì phải sao chép bảng trang ngay: 33us ở 1MB lên 1613us ở 1GB. Và trên đường đo, gcc -O2 lặng lẽ xóa mất phép memcpy cho ra 0 giây. Đo thật. 03/09/2026 · 9 phút đọc 3 Con vừa fork xong đã hiện RSS 400 MB, tôi kêu 'COW hỏng rồi' — nhưng nó chưa ghi lấy một byte Sau fork, con ghi 0% thì chiếm riêng 0MB, ghi 100% thì 400MB — bộ nhớ chia sẻ thật tới đúng lúc ghi. Nhưng RSS đứng yên 400MB ở mọi mức, làm tôi hoảng tưởng COW hỏng. Vì RSS đếm cả trang chung; thước đúng là Private_Dirty/Pss. Đây là gốc nỗi hoảng '50 worker ăn 20GB'. Đo thật bằng smaps. 03/09/2026 · 9 phút đọc 4 1000 luồng làm VmSize vọt lên 8 GB, tôi hoảng 'luồng đắt kinh khủng' — rồi nhìn cột bên cạnh: RAM thật chỉ 8 MB Tạo một luồng tốn 42us, tạo một tiến trình 96us — nhanh hơn ~2 lần, không phải 100 lần như folklore. Và 1000 luồng làm VmSize +8GB nhưng RAM thật chỉ +8MB: 8MB mỗi luồng chỉ là địa chỉ ảo đặt trước cho ngăn xếp, không phải RAM. Đo thật. 03/09/2026 · 8 phút đọc 5 Cho hai luồng dùng hai nhân, tưởng nhanh hơn — hóa ra chậm gấp 16 lần, vì tôi đo nhầm hẳn một thứ khác Ghim hai luồng vào một nhân, chuyển ngữ cảnh chỉ 0,5us. Bỏ ghim để chúng dùng hai nhân — chậm gấp 16 lần (8,7us). Trực giác 'nhiều nhân nhanh hơn' sai bét: hai nhân không chuyển ngữ cảnh, cái tôi đo là chi phí cross-core (cache nảy + IPI). Đo thật bằng ping-pong. 03/09/2026 · 8 phút đọc 6 kill -9 mà tiến trình vẫn trơ ra: chữ 'D' trong ps giải thích tất cả — và load average không nhảy như tôi tưởng Tôi dựng từng trạng thái R/S/D/Z/T rồi đọc /proc: một dd ghi đồng bộ nằm trong D (ngủ không-ngắt-được, kill -9 cũng phải chờ) tới 72% thời gian. Rồi liếc load average tưởng nó vọt lên, nhưng nó đứng im — vì là trung bình trượt 1/5/15 phút, phản ứng chậm. Đo thật. 03/09/2026 · 9 phút đọc 7 Lỗi trang Chạm 200MB anon chỉ sinh 102 lỗi trang, không phải 51200 — vì trang lớn THP. Và đọc file lạnh từ đĩa chỉ đếm 1 major fault dù chậm gấp 27 lần cache — vì readahead. Đo thật bằng getrusage. 03/09/2026 · 7 phút đọc 8 mmap và read đo ra bằng nhau y hệt — cho tới khi tôi phát hiện chính vòng đo của mình là nút thắt Đọc file chạm mọi byte: mmap và read bằng nhau (~3700 MB/s), tôi suýt chốt 'zero-copy chỉ là huyền thoại'. Nhưng đó là vì vòng cộng byte của tôi mới là nút thắt. Đổi sang chạm thưa 1 byte/trang: mmap nhanh gấp 5 lần (190 vs 40 GB/s) — read bắt buộc chép cả file. Đo thật. 03/09/2026 · 8 phút đọc 9 Định đo 'đọc đĩa lần đầu' mà hai lần đọc bằng nhau y hệt — vì file tôi vừa ghi đã ấm sẵn trong cache Đọc lạnh từ đĩa 93ms, đọc ấm từ cache 18ms — nhanh gấp 5 lần. Nhưng lần đầu tôi đo, hai lần bằng nhau: hóa ra ghi file bằng dd đã nạp sẵn nó vào page cache, nên 'lần đầu' của tôi vốn đã ấm. Muốn đo đọc lạnh phải drop_caches. Đo thật, và cột Cached là đèn báo. 03/09/2026 · 8 phút đọc 10 Ghi gần một triệu bản ghi mỗi giây — rồi thêm fsync và nó sụp còn 2.578, chậm 388 lần write() không fsync đạt ~984.000 ghi/giây, tôi suýt reo 'ghi đĩa nhanh khủng khiếp'. Nhưng đó là tốc độ page cache, dữ liệu chưa bền. Thêm fsync ép xuống đĩa: sụp còn ~2.578 ghi/giây (388us mỗi lần). Gộp một fsync cho cả lô cứu lại tốc độ mà vẫn bền. Đo thật. 03/09/2026 · 8 phút đọc 11 Ghi 300MB xong, dữ liệu nằm chơ vơ trong RAM đúng 30 giây rồi mới chạm đĩa — đó là cửa sổ mất trắng write() không đưa dữ liệu xuống đĩa ngay. Tôi ghi 300MB không fsync và nhìn Dirty trong /proc/meminfo: nó ghim nguyên 300MB suốt 30 giây rồi mới về 0. Cửa sổ 30 giây đó — nếu mất điện là bay sạch — chính là lý do phải fsync. Đo thật, kèm một cái bẫy quan sát. 03/09/2026 · 8 phút đọc 12 Đọc tuần tự nhanh hơn ngẫu nhiên 23 lần — nhưng 'vì đĩa không seek' là lời giải thích sai trên SSD Đọc tuần tự 6701 MB/s, ngẫu nhiên 295 MB/s. Tôi định giải thích bằng 'seek' — rồi nhớ ra đây là SSD, không có đầu đọc để seek. Tách biến: đọc tuần tự y hệt mà TẮT readahead thì sụp còn 134 MB/s, chậm 50 lần. Thủ phạm thật là readahead, không phải seek. Đo thật. 03/09/2026 · 8 phút đọc 13 Chạm hết 2GB vừa cấp phát mà RAM không nhích một byte — vì tôi 'chạm' bằng đọc, không phải ghi Cấp phát 2GB tức thì: VmSize +2GB nhưng VmRSS +0. Tôi chạm hết 2GB đó để cấp RAM, mà RSS vẫn đứng im — vì đọc bộ nhớ mới chỉ trỏ vào một trang zero chung, không cấp RAM riêng. Chỉ GHI mới biến ảo thành thật. Đo thật bằng /proc/self/status. 03/09/2026 · 8 phút đọc 14 malloc 512 GB trên máy 8 GB vẫn báo THÀNH CÔNG — rồi tôi ghi 500 MB và bị bắn gục malloc 512 GB trên một máy 7,75 GB RAM vẫn trả về thành công tức thì, vì overcommit chỉ HỨA chứ chưa giao RAM. Tôi suýt tin 'cấp phát xong là có RAM'. Sự thật lộ khi chạm trang: OOM killer giết đúng ở giới hạn thật, mã thoát 137, còn xa lượng đã cấp phát. Đo thật. 03/09/2026 · 10 phút đọc 15 OOM killer Khi hết RAM, nhân giết ai? Không phải kẻ vừa xin làm tràn, mà KẺ TO NHẤT — một tiến trình ngoài cuộc đang ngồi yên. Đo thật bằng oom_score và dmesg. 03/09/2026 · 9 phút đọc 16 Trang lớn 2MB nhanh hơn trang 4KB 17% — nhưng lần đầu tôi đo ra bằng nhau, vì 'nền 4KB' âm thầm đã là 2MB Truy cập ngẫu nhiên trên 1 GB: trang 4KB mất 138 ns/lần, trang lớn 2MB chỉ 114 ns — phần chênh là chi phí lỗi TLB. Nhưng lần đầu hai bản nhanh y hệt, vì THP=always khiến cái 'nền 4KB' của tôi thực ra là trang lớn. Xác minh điều kiện, đừng suy từ ý định. Đo thật. 03/09/2026 · 9 phút đọc 17 Nạp lại một trang từ swap chậm hơn RAM 58 lần — trong khi mọi bộ đếm khăng khăng 'chẳng có gì xảy ra' Đọc lại một trang vừa bị đẩy ra swap tốn ~704 ns, chậm hơn đọc RAM 58 lần. Nhưng bộ đếm ru_majflt và pswpin đều báo 0 — chúng nói sự kiện tôi định đo không tồn tại, trong khi VmSwap và đồng hồ nói ngược lại. Tin đồng hồ hơn bộ đếm. Đo thật bằng MADV_PAGEOUT. 03/09/2026 · 10 phút đọc 18 Bộ nhớ chia sẻ đo ra 15 GB/s, checksum báo KHỚP — và cả hai đều đang nói dối tôi Chuyển 1 GB giữa hai tiến trình: pipe 3,1 GB/s, bộ nhớ chia sẻ ring nhiều đệm 9,8 GB/s (~3×). Nhưng shm một đệm lại chậm hơn pipe, và bản bỏ đồng bộ cho 15 GB/s với checksum 'KHỚP' đã suýt lừa tôi viết kết luận sai — vì đo nhầm đại lượng và một phép kiểm vô dụng. Đo thật. 03/09/2026 · 10 phút đọc 19 Pipe khai 64 KB, nhưng ghi lố 1 byte mỗi khối thì chỉ nhét được 44 KB — cách đo tự định hình con số Pipe báo dung lượng 64 KB. Nhưng ghi khối 4097 byte (lố đúng 1 byte qua ranh giới trang) chỉ nhét được 44 KB trước khi write chặn — mất một phần ba. 'Dung lượng pipe' không phải một số duy nhất; chính cỡ khối ghi định hình nó. Và khi đầy, write chặn đúng tới khi bên đọc rút. Đo thật. 03/09/2026 · 9 phút đọc 20 8 luồng tranh một khóa mà 99,86% số lần lock không hề chạm tới kernel — strace phơi bày điều tôi đoán sai Khóa mutex khi không ai tranh chỉ tốn 5,4 ns, thuần userspace, không syscall. Tôi tưởng tranh chấp sẽ đắt gấp trăm lần vì syscall futex() — nhưng đo ra chỉ chậm ~4 lần, và strace cho thấy 99,86% số lần khóa vẫn không vào kernel. Cái đắt hóa ra không phải syscall. Đo thật. 03/09/2026 · 10 phút đọc 21 Định đo bộ lập lịch chia lượt CPU, lần đầu chẳng thấy gì — vì tôi quên một biến, và kernel không phải CFS Mỗi lượt một tiến trình chạy ~3 ms rồi bị đẩy ra, và con số đó cố định dù 2 hay 8 tiến trình tranh CPU. Nhưng lần đầu tôi không thấy chia lượt gì cả (quên ghim CPU), và thứ tôi đang đo hóa ra là EEVDF chứ không phải CFS như tên bài. Đo thật hai cú vấp về bộ lập lịch. 03/09/2026 · 10 phút đọc 22 Một tiến trình 'nice 10' là ưu tiên cao hay thấp? Câu hỏi bẫy — con số đó một mình chẳng nói lên gì Hai tiến trình tranh một CPU chia phần theo nice: 0 vs 5 ra 75/25, 0 vs 19 ra 98,5/1,5. Tôi suýt chốt 'nice 10 = chậm'. Rồi đo lại: nice 10 vs 15 chia y hệt 0 vs 5, vì phần chia chỉ phụ thuộc HIỆU nice, không phải giá trị tuyệt đối. Ưu tiên là chuyện tương đối. Đo thật. 03/09/2026 · 9 phút đọc 23 Ghim tiến trình vào một core để 'giữ cache ấm' — đo ra chẳng nhanh hơn tí nào, vì scheduler đã làm hộ Ghim CPU vào một nhân nghe hợp lý: cache ấm, không bị dời, chạy nhanh hơn. Nhưng đo ra ghim không hơn tự do — máy rảnh, dưới tải, kể cả khi ép dời liên tục. Vì bộ lập lịch đã tự giữ cache ấm, và phần cứng chia sẻ cache khiến dời rẻ. Tôi đã đo cho một lợi ích đã có sẵn. Đo thật. 03/09/2026 · 9 phút đọc 24 Cấp container nguyên 1 CPU mà nó vẫn đứng hình 85 ms mỗi 100 ms — vì tôi quên đếm số luồng docker run --cpus=1 cho container đúng một CPU. Chạy 1 luồng thì mượt. Nhưng 4 luồng đốt chung quota nhanh gấp 4 rồi CẢ BỐN bị đóng băng 85 ms mỗi chu kỳ — dù CPU trung bình không vượt hạn. Đây là CFS throttling, nguồn đuôi trễ số một trong container. Đo thật. 03/09/2026 · 8 phút đọc 25 Container ở 255/256 MB — sắp chết hay khỏe re? Cùng con số, hai kết cục trái ngược Thấy container dùng 255 trên 256 MB, tôi hoảng 'sắp OOM'. Nhưng nếu chỗ đầy đó là page cache thì container khỏe re, oom_kill=0; nếu là anon thì exit 137, bị giết ngay. Con số 'đang dùng bao nhiêu' đứng một mình gần như vô dụng — chỉ thành phần mới nói. Đo thật. 03/09/2026 · 8 phút đọc 26 Phép đo thời gian ra -10 giây: khoảng thời gian âm bất khả, và quả bom hẹn giờ trong code của bạn Đo một khoảng thời gian bằng đồng hồ treo tường rồi bị NTP giật lùi 10 giây giữa chừng: kết quả ra -10 giây, một khoảng ÂM không thể tồn tại. Monotonic thì cho +3 micro giây đúng đắn, không bao giờ nhảy. Chọn nhầm đồng hồ là bug timing kín đáo nhất. Đo thật. 03/09/2026 · 9 phút đọc 27 Đồng hồ khai độ phân giải 1 nano giây, nhưng xin ngủ 1 micro giây thì ngủ thật 84 micro giây clock_getres báo 1 nano giây, tôi tin hệ thống định thời tới nano giây. Nhưng nanosleep(1µs) ngủ thật ~84 micro giây — gấp 84 lần. Có một sàn ~80us do độ trễ đánh thức của bộ lập lịch, không phải độ mịn timer. Độ phân giải tự khai khác độ chính xác thật. Đo thật. 03/09/2026 · 8 phút đọc 28 Tín hiệu Giao một tín hiệu tới handler tốn ~500 ns. Nhưng cú đắt hơn là EINTR: một tín hiệu tới giữa nanosleep(10 giây) làm nó trả về sau 0,055 giây — và nếu bỏ giá trị trả về, bạn không hề biết. Đo thật. 03/09/2026 · 8 phút đọc 29 Zombie chiếm 0 KB bộ nhớ nên tôi phán 'vô hại' — rồi cả máy không fork nổi một tiến trình dù RAM còn đầy Một tiến trình zombie có RSS ~0 KB, đo bộ nhớ thì tưởng vô hại. Nhưng nó giữ một slot PID, và PID là hữu hạn — đủ zombie thì cạn PID, fork() thất bại khắp máy dù RAM trống mênh mông. Tôi đã đo nhầm tài nguyên. Đo thật, kèm cái bẫy PID 1 trong container. 03/09/2026 · 8 phút đọc 30 Ba chữ 'in system' trong thông báo lỗi tôi đọc lướt qua — và mất cả buổi gõ ulimit vô ích 'Too many open files' (EMFILE, mỗi tiến trình) và '...in system' (ENFILE, toàn hệ thống) chỉ khác ba chữ, nhưng cần hai cách chữa khác hẳn. Tôi chạm cái thứ hai, phản xạ vá cái thứ nhất, gõ ulimit cả buổi không xong. Đo thật hai giới hạn fd và bài học đọc kỹ lỗi. 03/09/2026 · 8 phút đọc 31 epoll nhanh hơn poll 1860 lần — nhưng cái 'O(1) thần kỳ' tôi định viết là sai, và tôi đã cố định nhầm một biến Theo dõi 10.000 fd: poll tốn ~248 micro giây mỗi lần gọi, epoll chỉ ~130 nano giây. Tôi định chốt 'epoll là O(1)'. Rồi tôi tăng số fd sẵn sàng và nó lộ ra là O(số fd sẵn sàng), không phải hằng số. Ưu thế thật của epoll đến từ một giả định về tải. Đo thật. 03/09/2026 · 8 phút đọc 32 O_DIRECT tưởng là 'I/O nhanh', đo ra đọc-lại thua page cache 5 lần — và một máy ảo suýt lừa cả phép đo O_DIRECT bỏ qua page cache, nghe như tối ưu. Nhưng đọc lại từ cache buffered ấm nhanh gấp ~5 lần. O_DIRECT là I/O KHÔNG cache, không phải I/O nhanh. Và lần đo đầu trên bind-mount cho kết quả vô lý vì các tầng cache lồng nhau nuốt luôn cờ direct. Đo thật. 03/09/2026 · 9 phút đọc 33 sendfile gửi 512 MB trong đúng 1 syscall — mà đo thông lượng lại thua read+write, vì tôi đo nhầm chỗ sendfile gộp cả file 512 MB vào ĐÚNG 1 lời gọi thay vì 16.384 read+write. Nhưng trên loopback cache ấm, tốc độ thô của nó còn thua read+write đệm 64K — và tôi suýt kết luận nó vô dụng. Lỗi nằm ở chỗ tôi đo thông lượng, trong khi sendfile thắng ở CPU/syscall. Đo thật. 03/09/2026 · 8 phút đọc 34 Đo ra stack và heap tốn như nhau, tôi biết ngay mình đo sai — vì chính vòng đo nuốt mất kết quả Ai cũng biết cấp phát stack nhanh hơn malloc, nhưng phép đo đầu của tôi cho hai bên bằng nhau — dấu hiệu đo sai. Sửa lại: stack ~1 ns cho MỌI kích cỡ, malloc chậm 6-14 lần. Đổi lại stack giới hạn 8MB, tràn thì crash không báo; heap trả NULL. Đo thật. 03/09/2026 · 8 phút đọc 35 Ép malloc về một arena chỉ chậm nhẹ, tôi định chốt 'arena không quan trọng' — rồi tắt thêm một thứ, nó sập 24 lần Ép malloc về một arena chỉ làm 8 luồng chậm từ 556 xuống 434 triệu op/s — tưởng arena vô dụng. Nhưng có một biến ẩn đang gánh thay nó: tắt luôn tcache thì thông lượng sập còn 23. Hai lớp chống tranh khóa, mỗi lớp che hiệu ứng của lớp kia. Đo thật. 03/09/2026 · 9 phút đọc 36 Hai luồng ghi hai biến riêng, không chia sẻ gì — mà chậm 3,7 lần vì một lý do không có trong code Đọc mã nguồn cả ngày cũng không thấy tranh chấp: hai luồng, hai biến riêng, không đụng nhau. Vậy mà chậm ~3,7 lần chỉ vì hai biến tình cờ nằm chung một cache line. Nút thắt vô hình ở tầng mã nguồn, chỉ lộ khi nhìn xuống bố trí bộ nhớ. Đo thật, và cách săn nó. 03/09/2026 · 8 phút đọc 37 Đọc một file /proc tốn 1,5 micro giây, tôi định chốt 'rẻ' — file bên cạnh lại đắt gấp 8 lần và còn phình Đọc /proc/self/stat tốn ~1,5 us, tôi suýt khái quát 'mọi file /proc đều rẻ vậy'. Nhưng smaps tốn 12 us và còn phình theo bộ nhớ tiến trình, vì nhân phải đi bộ toàn bộ bảng trang. Cái rẻ và cái đắt nằm chung một thư mục trông y hệt nhau. Đo thật. 03/09/2026 · 8 phút đọc 38 Load 8 mà CPU vẫn rảnh: con số ai cũng liếc để đoán 'máy bận' đang lừa bạn Ai cũng tin 'load average = mức bận CPU'. Nhưng trên Linux nó đếm cả tiến trình kẹt chờ I/O — 4 tiến trình chờ đĩa đẩy load lên 4 dù CPU nhàn tênh. Cùng con số load 8, hai căn bệnh trái ngược. Và nó còn dâng chậm vì làm mượt. Đo thật hai lầm tưởng của chính tôi. 03/09/2026 · 8 phút đọc 39 Công cụ tôi tin nhất suốt cả sê-ri hóa ra là kẻ nói dối lớn nhất về thời gian Mỗi syscall dưới strace tốn ~80 micro giây thay vì 126 nano giây — chậm 630 lần. Nghĩa là mọi con số thời gian đo được khi strace đang bám đều sai bét. Chính cái thước tin cậy nhất của tôi lại là hiệu ứng người quan sát ở dạng thuần khiết nhất. Đo thật, và một maxim của sê-ri bị lật ngược. 03/09/2026 · 8 phút đọc 40 Định dùng perf để soi vì sao code chậm, máy ảo trả <not supported> — và cả demo kinh điển cũng bốc hơi PMU là cửa sổ duy nhất thấy 'chậm vì trượt cache hay đoán nhánh sai'. Nhưng máy ảo không lộ PMU nên perf trả <not supported>. Tôi phải quay về đồng hồ — nó vẫn lộ hiệu ứng cache chậm 200 lần. Rồi demo 'mảng sắp xếp nhanh hơn' lại không tái hiện, vì một lý do bất ngờ. Đo thật. 03/09/2026 · 8 phút đọc 41 Đổi mutex sang atomic được nhanh gấp 3 — rồi tôi phát hiện cú nhanh gấp 42 mình suýt bỏ lỡ Ai cũng khuyên 'bộ đếm đa luồng thì dùng atomic thay mutex cho nhanh'. Đo ra atomic nhanh gấp 3 thật — nhưng thêm luồng vào thì cả atomic lẫn CAS đều TỤT, vì cùng nảy một dòng cache. Cách thật sự scale là không chia sẻ, và nó nhanh hơn atomic 42 lần. Đo thật. 03/09/2026 · 8 phút đọc 42 Cùng một cú ghi vào trang chỉ đọc: lần thì SIGSEGV chết tươi, lần thì kernel âm thầm sửa hộ char *s="hello"; s[0]='H'; — biên dịch trót lọt, chạy thì chết. Tôi đo ba ca ghi vào trang chỉ đọc, cả ba SIGSEGV, suýt chốt 'ghi vào trang chỉ đọc thì chết'. Rồi ca thứ tư sau fork lật ngược: cùng cái bẫy phần cứng đó, kernel lại lặng lẽ copy trang chứ không giết. Đo thật trong container. 03/09/2026 · 10 phút đọc 43 fork một tiến trình 256MB chỉ tốn 133 micro giây — và tôi từng đổ oan cho nhầm con số Sao chép cả một tiến trình 256MB nghe phải mất hàng chục mili giây. Đo ra 133 micro giây, nhanh gấp trăm lần — vì fork nhân bảng trang chứ không chép byte. Rồi tôi suýt kết luận sai thủ phạm, cho tới khi tách 'đã cấp phát' khỏi 'đã chạm'. Và hóa ra fork là phần rẻ nhất; exec mới đắt. Đo thật trong container. 03/09/2026 · 10 phút đọc 44 Tôi giết PID cha bằng SIGKILL — cú mạnh nhất có thể — mà ba tiến trình con vẫn sống nhăn Trực giác nói 'cha chết thì con chết theo'. Tôi dựng cây tiến trình thật rồi bắn SIGKILL vào đúng PID cha: nó chết ngay, ba worker chẳng suy suyển. Vì tín hiệu không lan theo quan hệ cha–con. Muốn hạ cả cụm phải nhắm nhóm — kill(-pgid) một phát hạ tất. Đo thật trong container. 03/09/2026 · 9 phút đọc 45 Nâng trần fd của mình xong lại đụng một cái trần khác không phải của tôi — errno mới nói thật Chạm giới hạn fd cho lỗi EMFILE tự nâng được không cần root; nhưng nâng xong lại đụng một trần khác (ENFILE toàn hệ thống) không phải của mình. Đo thật trong container, và để errno nói thật đang chạm trần nào. 03/09/2026 · 9 phút đọc