File Descriptor and Open File


春節前一週個週末,因為一個服務系統異常,讓我又重新 review 關於 File Descriptor (以下簡稱 FD) 的概念。上次整理 已經是兩年前的,再重新讀了相關資料,整理以下部分:

  • FD 基本概念
  • 設定層級,以及各個層級的詳細設定方法和關係
  • FD 應用場景與問題

File Descriptor

依照 wiki 的描述,File Descriptor 是 Unix-like OS 裡的抽象處理標記 (indicator, handle),一般用來處理檔案或者輸入輸出 (input / output) 的資源,像是 pipe 或者 network socket

FD 源自於 POSIX, 他是非負數的 integer, 通常是 C 語言的 int type. 每一個 Unix process 預期有三個 POSIX FD:

  1. stdin: value 0
  2. stdout: value 1
  3. stderr: value 2

在 terminal 每一個指令在執行的時候,都會產生 process 以及對應的 PID。這些 process 執行的時候,他們相關的資料,在 Linux 會存放在 /proc/PID/ 底下。

底下是取的 PID 相關資訊的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
## 取的當前的 PID
~$ echo $$
25737

## find process runtime variables.
~$ ls /proc/25737
attr/ coredump_filter gid_map mem oom_adj sched statm
autogroup cpuset io mountinfo oom_score schedstat status
auxv cwd@ latency mounts oom_score_adj sessionid syscall
cgroup environ limits mountstats pagemap setgroups task/
clear_refs exe@ loginuid net/ personality smaps timers
cmdline fd/ map_files/ ns/ projid_map stack uid_map
comm fdinfo/ maps numa_maps root@ stat wchan

# ls FD
~$ ls /proc/25737/fd
0 1 2 255

# find limit of process
~$ cat /proc/25737/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 7862 7862 processes
Max open files 1024 4096 files
Max locked memory 65536 65536 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 7862 7862 signals
Max msgqueue size 819200 819200 bytes
Max nice priority 0 0
Max realtime priority 0 0
Max realtime timeout unlimited unlimited us

透過 /proc/PID 裡的資訊,可以知道很多 process 的資訊,通常找問題,或者資源不足時,可以先從這裡下手。


FD 的設定層級範圍

分成 Shell SessionUser LevelSystem Level 三個層級:

  • Shell Session: 調整當前的 FD 數,指令為 ulimit
    • 一班使用者只能減小不能變大
    • root 則不受限制
  • User Level: 指定特定使用者的預設值,設定檔在 /etc/security/limits.conf
  • System Level: 系統開機時 OpenFiles 的設定值
    • 這是軟設定,可以隨時調整、或者小於 User Level / Shell Session
    • 設定值存在 /proc/sys/fs/file-max

底下分別說明三個的差異。


Shell Session: ulimit

ulimit 是使用者登入時 shell session 的 limit,可以查詢或者修改當前的 session 的各種 limit,包含 open file descriptors, amount of cpu time, number of processes, … 等一堆。manual 的第一句描述如下:

ulimit provides control over the resources available to the shell and to processes started by it, on systems that allow such control.

注意,不是限制單一 process 的數量,而是一個 shell 或者特定 user 的身份。

針對 OpenFiles 常用的參數就是 Soft and Hard limit, 底下顯示 open file 的狀況:

1
2
3
4
5
# 顯示最大 `Hard Limit`
ulimit -Hn

# 顯示最大 `Soft Limit`
ulimit -Sn

臨時調整 open file limit:

1
ulimit -n 10000

但是這個設定,離開 Shell Session 就無效了。

底下做了一個實驗,流程大概是:

  1. 取得現在 bash 的 PID,不更改 Number of Open Files (NOF)
  2. fork 第二層 bash process,更改 NOF
  3. fork 第三層 bash process,確認 NOF
  4. 離開第三層,確認 NOF
  5. 離開第二層,確認 NOF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
## 取的當前 PID
~$ echo $$
26941

## 顯示當前 NOF
~$ ulimit -n
1024

## fork child bash
~$ bash

## 顯示 NOF
~$ ulimit -n
1024

## 更改 open file limit
~$ ulimit -n 2048

## 確認
~$ ulimit -n
2048

## 再 fork child bash
~$ bash

## 顯示 NOF
~$ ulimit -n
2048

## 顯示當前 process tree
~$ ps f -g26941
PID TTY STAT TIME COMMAND
26941 pts/1 Ss 0:00 -bash
27211 pts/1 S 0:00 \_ bash
27489 pts/1 S 0:00 \_ bash
27786 pts/1 R+ 0:00 \_ ps f -g26941


## 離開第三層 bash
~$ exit
exit

## 第二層的 NOF
~$ ulimit -n
2048

## 顯示當前 process tree
~$ ps f -g26941
PID TTY STAT TIME COMMAND
26941 pts/1 Ss 0:00 -bash
27211 pts/1 S 0:00 \_ bash
27787 pts/1 R+ 0:00 \_ ps f -g26941

## 離開第二層 bash,回到第一層
~$ exit
exit

## 顯示 NOF
~$ ulimit -n
1024

這個實驗得到幾個結論:

  • ulimit 的設定是 session 的,離開就會失效
  • forked process 會使用 ulimit 的設定,取代 limits.conf 的值

User Level: limits.conf

這是 PAM (Pluggable Authentication Modules) 的設定,主要可以針對使用者、群組,然後設定不同資源的限制。底下是 man limits.conf 的描述:

The pam_limits.so module applies ulimit limits, nice priority and number of simultaneous login sessions limit to user login sessions. This description of the configuration file syntax applies to the /etc/security/limits.conf file and *.conf files in the /etc/security/limits.d directory.

limits.conf 的設定位置: /etc/security/limits.conf,內容主要有底下四個欄位:

[domain] [type] [item] [value]

  • domain: a username, group, wildcard
  • type:
    • 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.
    • -: for enforcing both soft and hard resource limits together.
  • item:
    • nofile: number of file
    • nproc: number of processes
    • fsize: maximum filesize (KB)
    • 很多 … 略
  • value: -1, unlimited or infinity
    • 預設是 1024

修改完 /etc/security/limits.conf,不用重開機,只要重新登入就會生效。

使用者登入時,如果 limits.conf 有設定,則使用,沒有設定,就是預設 1024。需要臨時調整則使用 ulimit -n [num]


System Level: file-max

man proc 找到 file-max 的說明如下:

This file defines a system-wide limit on the number of open files for all processes. System calls that fail when encountering this limit fail with the error ENFILE. (See also setrlimit(2), which can be used by a process to set the per-process limit, RLIMIT_NOFILE, on the number of files it may open.) If you get lots of error messages in the kernel log about running out of file handles (look for “VFS: file-max limit reached”), try increasing this value:

echo 100000 > /proc/sys/fs/file-max

The kernel constant NR_OPEN imposes an upper limit on the value that may be placed in file-max.
Privileged processes (CAP_SYS_ADMIN) can override the file-max limit.

目前系統最大的 FD 值,這個值在開機的時候會被設定。

  • 預設值為 8192
  • 如果記憶體的十分之一大於預設值,那就不會是 8192,會是記憶體的十分之一大小。
  • 需要臨時調整大小,可以直接 echo 100000 > /proc/sys/fs/file-max,會馬上生效。
  • 查詢 file-max 的方法:
    1. cat /proc/sys/fs/file-max
    2. sysctl fs.file-max

除了直接改 /proc/sys/fs/file-max,也可以修改 /etc/sysctl.conf,如下:

/etc/sysctl.conf
1
fs.file-max = 100000

然後下 sysctl -p 就會馬上生效,或者:

1
sysctl -w fs.file-max=100000

查詢某個 Process 的 FD Limit

直接查詢 /proc/[PID]/limits 就可以知道,設定是否正確被 process 吃到,如下 Max open files 的值就是修改後的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
~$ cat /proc/26643/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 65535 65535 processes
Max open files 65535 65535 files
Max locked memory 65536 65536 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 15741 15741 signals
Max msgqueue size 819200 819200 bytes
Max nice priority 0 0
Max realtime priority 0 0
Max realtime timeout unlimited unlimited us

相關應用與問題

  • limits.conf 的值可以大於 file-max?
    • 可以, 因為 file-max 只是建議值
  • FD 跟什麼應用有關係?
    • TCP Connection, Log
  • Hard Limit 的上限是多少?跟硬體資源有關係?
  • 同上,Soft Limit 上限?
  • Windows 有 File Descriptor 或者 limit 這種概念?
  • file-max 改很小系統會怎樣?

相關工具

  • ulimit
  • lsof: list open file
  • sysctl: system control
  • fuser
  • netstat

延伸閱讀

參考資料


Comments