Mặc định chương trình trên Linux chỉ mở được giới hạn file nhất định. Nếu chạy từ shell (như bash) giá trị này thường có thể xem từ lệnh

$ ulimit -n
1024

giá trị này có thể thay đổi ngay trên shell

$ ulimit -n 9999
$ ulimit -n
9999

Set process max open files trên Systemd

Khi chạy bằng systemd, các service sẽ kết thừa giá trị limit của systemd hoặc tự set bằng LimitNOFILE

$ systemd --version
systemd 249 (249.11-0ubuntu3.12)

Theo man 2 setrlimit

A child process created via fork(2) inherits its parent's resource limits. Resource limits are preserved across execve(2).

Xem giá trị theo 2 cách: dùng lệnh systemctl show và xem trên /proc filesystem

% systemctl show cron| grep NOFILE   
LimitNOFILE=524288
LimitNOFILESoft=1024

% systemctl status cron | grep PID
   Main PID: 464 (cron)
% cat /proc/464/limits 
Limit                     Soft Limit           Hard Limit           Units     
Max cpu time              unlimited            unlimited            seconds   
Max file size             unlimited            unlimited            bytes     
Max data size             unlimited            unlimited            bytes     
Max stack size            8388608              unlimited            bytes     
Max core file size        0                    unlimited            bytes     
Max resident set          unlimited            unlimited            bytes     
Max processes             61289                61289                processes 
Max open files            1024                 524288               files     
Max locked memory         8388608              8388608              bytes     
Max address space         unlimited            unlimited            bytes     
Max file locks            unlimited            unlimited            locks     
Max pending signals       61289                61289                signals   
Max msgqueue size         819200               819200               bytes     
Max nice priority         0                    0                    
Max realtime priority     0                    0                    
Max realtime timeout      unlimited            unlimited            us   

Soft limit vs hard limit

Theo man 5 limits.conf:

hard
for enforcing hard resource limits. These limits are set by the superuser and enforced
by the Kernel. The user cannot raise his requirement of system resources above such
values.

soft
for enforcing soft resource limits. These limits are ones that the user can move up or
down within the permitted range by any pre-existing hard limits. The values specified
with this token can be thought of as default values, for normal system usage.

Theo man 2 getrlimit

The  soft  limit  is  the value that the kernel enforces for the corre
sponding resource.  The hard limit acts  as  a  ceiling  for  the  soft
limit:  an  unprivileged process may set only its soft limit to a value
in the range from 0 up to the hard limit, and (irreversibly) lower  its
hard   limit.    A  privileged  process  (under  Linux:  one  with  the
CAP_SYS_RESOURCE capability in the initial user namespace) may make ar
bitrary changes to either limit value.

Hard limit là giá trị admin set tối đa, để người dùng khác trên cùng hệ thống không set vượt quá được (dùng ulimit). Với system service, không có "người dùng" set giá trị (người dùng là admin??!), giá trị có ý nghĩa duy nhất ở đây là soft limit, thường được set bằng luôn với hard limit.

Đọc code tìm giá trị tối đa cho Max open files LimitNOFILE

Lấy code tại tag v249 về:

% git clone --depth 1 --branch v249 https://github.com/systemd/systemd
Cloning into 'systemd'...
remote: Enumerating objects: 4600, done.
remote: Counting objects: 100% (4600/4600), done.
remote: Compressing objects: 100% (4063/4063), done.
remote: Total 4600 (delta 765), reused 1588 (delta 287), pack-reused 0
Receiving objects: 100% (4600/4600), 11.05 MiB | 2.92 MiB/s, done.
Resolving deltas: 100% (765/765), done.
Note: switching to 'f6278558da0304ec6b646bb172ce4688c7f162a5'.
...

Tìm giá trị config LimitNOFILE trong các file code C

% grep -Rin LimitNOFILE | grep '.c:' | grep -v test
grep: test/testdata: warning: recursive directory loop
src/core/main.c:681:                { "Manager", "DefaultLimitNOFILE",           config_parse_rlimit,                RLIMIT_NOFILE, arg_default_rlimit         },
src/core/dbus-manager.c:2706:        SD_BUS_PROPERTY("DefaultLimitNOFILE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
src/core/dbus-manager.c:2707:        SD_BUS_PROPERTY("DefaultLimitNOFILESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
src/core/dbus-execute.c:1073:        SD_BUS_PROPERTY("LimitNOFILE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
src/core/dbus-execute.c:1074:        SD_BUS_PROPERTY("LimitNOFILESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),

Xem RLIMIT_NOFILE trong src/core/main.c thấy systemd set giá trị tối đa số file có thể mở tới giá trị kernel giới hạn:

static int bump_rlimit_nofile(struct rlimit *saved_rlimit) {
        struct rlimit new_rlimit;
        int r, nr;

        /* Get the underlying absolute limit the kernel enforces */
        nr = read_nr_open();

        /* Calculate the new limits to use for us. Never lower from what we inherited. */
        new_rlimit = (struct rlimit) {
                .rlim_cur = MAX((rlim_t) nr, saved_rlimit->rlim_cur),
                .rlim_max = MAX((rlim_t) nr, saved_rlimit->rlim_max),
        };

        /* Shortcut if nothing changes. */
        if (saved_rlimit->rlim_max >= new_rlimit.rlim_max &&
            saved_rlimit->rlim_cur >= new_rlimit.rlim_cur) {
                log_debug("RLIMIT_NOFILE is already as high or higher than we need it, not bumping.");
                return 0;
        }

        /* Bump up the resource limit for ourselves substantially, all the way to the maximum the kernel allows, for
         * both hard and soft. */
        r = setrlimit_closest(RLIMIT_NOFILE, &new_rlimit);
        if (r < 0)
                return log_warning_errno(r, "Setting RLIMIT_NOFILE failed, ignoring: %m");

        return 0;
}

Giá trị tối đa tuyệt đối được Linux kernel quyết định

        /* Get the underlying absolute limit the kernel enforces */
        nr = read_nr_open();

Tìm function này:

 grep -Rin read_nr_open
src/basic/fd-util.h:106:int read_nr_open(void);
src/basic/rlimit-util.c:382:                limit = read_nr_open();
src/basic/fd-util.c:701:int read_nr_open(void) {
src/test/test-fd-util.c:183:static void test_read_nr_open(void) {
src/test/test-fd-util.c:184:        log_info("nr-open: %i", read_nr_open());
src/test/test-fd-util.c:291:        test_read_nr_open();
src/core/main.c:1228:                k = read_nr_open();
src/core/main.c:1260:        nr = read_nr_open();
src/core/main.c:2273:                nr = read_nr_open();

Trong src/basic/fd-util.c có định nghĩa function int read_nr_open(void) {

int read_nr_open(void) {
        _cleanup_free_ char *nr_open = NULL;
        int r;

        /* Returns the kernel's current fd limit, either by reading it of /proc/sys if that works, or using the
         * hard-coded default compiled-in value of current kernels (1M) if not. This call will never fail. */

        r = read_one_line_file("/proc/sys/fs/nr_open", &nr_open);
        if (r < 0)
                log_debug_errno(r, "Failed to read /proc/sys/fs/nr_open, ignoring: %m");
        else {
                int v;

                r = safe_atoi(nr_open, &v);
                if (r < 0)
                        log_debug_errno(r, "Failed to parse /proc/sys/fs/nr_open value '%s', ignoring: %m", nr_open);
                else
                        return v;
        }

        /* If we fail, fall back to the hard-coded kernel limit of 1024 * 1024. */
        return 1024 * 1024;
}

Function này đọc giá trị kernel set từ /proc/sys/fs/nr_open hoặc nếu fail sẽ dùng giá trị 1024 * 1024. Xem thử file này trên máy Ubuntu 22.04:

% cat /proc/sys/fs/nr_open
1048576

Cũng bằng với 1024 * 1024.

Tìm function gọi function read_nr_open:

$ grep -Rin read_nr_open
src/basic/fd-util.h:106:int read_nr_open(void);
src/basic/rlimit-util.c:382:                limit = read_nr_open();
src/basic/fd-util.c:701:int read_nr_open(void) {
src/test/test-fd-util.c:183:static void test_read_nr_open(void) {
src/test/test-fd-util.c:184:        log_info("nr-open: %i", read_nr_open());
src/test/test-fd-util.c:291:        test_read_nr_open();
src/core/main.c:1228:                k = read_nr_open();
src/core/main.c:1260:        nr = read_nr_open();
src/core/main.c:2273:                nr = read_nr_open();

Xem src/basic/rlimit-util.c

int rlimit_nofile_bump(int limit) {
        int r;

        /* Bumps the (soft) RLIMIT_NOFILE resource limit as close as possible to the specified limit. If a negative
         * limit is specified, bumps it to the maximum the kernel and the hard resource limit allows. This call should
         * be used by all our programs that might need a lot of fds, and that know how to deal with high fd numbers
         * (i.e. do not use select() — which chokes on fds >= 1024) */

        if (limit < 0)
                limit = read_nr_open();

        if (limit < 3)
                limit = 3;

        r = setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(limit));
        if (r < 0)
                return log_debug_errno(r, "Failed to set RLIMIT_NOFILE: %m");

        return 0;
}

Khi limit < 0, limit sẽ nhận giá trị của read_nr_open(), khi limit >=3, gọi setrlimit_closest

int setrlimit_closest(int resource, const struct rlimit *rlim) {
        struct rlimit highest, fixed;
...
        if (setrlimit(resource, rlim) >= 0)
                return 0;

        if (errno != EPERM)
                return -errno;

        /* So we failed to set the desired setrlimit, then let's try
         * to get as close as we can */

        if (getrlimit(resource, &highest) < 0)
                return -errno;

        /* If the hard limit is unbounded anyway, then the EPERM had other reasons, let's propagate the original EPERM
         * then */
        if (highest.rlim_max == RLIM_INFINITY)
                return -EPERM;

        fixed = (struct rlimit) {
                .rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max),
                .rlim_max = MIN(rlim->rlim_max, highest.rlim_max),
        };
...
}

Giá trị được set là MIN(rlim->rlim_cur, highest.rlim_max), nên người dùng không thể set cao hơn giá trị highest.rlim_max, tức giá trị max hard limit kế thừa từ systemd, hay kết quả của function read_nr_open, ở ví dụ này là 1024*1024.

Tạo service chạy Python thử set hard limit lớn hơn 1024*1024

Python3.10 hiển thị giá trị RLIMIT_NOFILE của Python process:

% python3 -c 'import resource; print(resource.getrlimit(resource.RLIMIT_NOFILE))' 
(1024, 1048576)

Tạo 1 systemd service tên pymi lần lượt set LimitNOFILE=5242880 và 5000

# systemctl  cat pymi.service 
# /lib/systemd/system/pymi.service
[Unit]
Description=FAMILUG.org rlimit test
After=remote-fs.target nss-user-lookup.target

[Service]
ExecStart=python3 -c 'import resource; print(resource.getrlimit(resource.RLIMIT_NOFILE))'
LimitNOFILE=5242880
IgnoreSIGPIPE=false
KillMode=process

[Install]
WantedBy=multi-user.target
# systemctl daemon-reload
# systemctl start pymi
# systemctl status pymi
root@mini:~# systemctl status pymi
 pymi.service - FAMILUG.org rlimit test
     Loaded: loaded (/lib/systemd/system/pymi.service; disabled; vendor preset: enabled)
     Active: inactive (dead)

Thg 3 15 00:49:13 mini systemd[1]: Started FAMILUG.org rlimit test.
Thg 3 15 00:49:13 mini python3[215501]: (1048576, 1048576)
Thg 3 15 00:49:13 mini systemd[1]: pymi.service: Deactivated successfully.
Thg 3 15 00:49:33 mini systemd[1]: Started FAMILUG.org rlimit test.
Thg 3 15 00:49:34 mini python3[216937]: (5000, 5000)
Thg 3 15 00:49:34 mini systemd[1]: pymi.service: Deactivated successfully.

Kết quả:

  • Khi set 5242880 (5 triệu), giá trị này bị set về giá trị tối đa của kernel set 1048576.
  • khi set 5000, chương trình Python thấy cả 2 soft và hard limit đều = 5000.

Kết luận

Tại systemd v249, Không thể set LimitNOFILE lớn hơn giá trị trong /proc/sys/fs/nr_open.

Nhưng giá trị trong nr_open có thể thay đổi bằng sysctl:

# sysctl -a | grep nr_open
fs.nr_open = 1048576

Hết.

HVN at http://pymi.vn and https://www.familug.org.

Ủng hộ tác giả 🍺



Published

Category

frontpage

Tags

Contact