12
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Linuxで開ける最大のfile descriptorの数について調べたメモ

1サーバで大量の常時接続コネクションを維持するにはlinuxのfile descriptorの上限がボトルネックになるんじゃないかと思って調べた。
最初に注意として、以下に解説する設定を下手に変更してしまうとシステムが動かなくなる場合があるので変更する場合は慎重に。当たり前ですが責任は負えません。


file descriptorの上限には以下の2つがある。

  • 1プロセスが開ける上限
  • システム全体で開ける上限

1プロセスが開ける上限

1プロセスが開ける上限にはsoft limit, hard limitと呼ばれる2種類ある。またこれらはプロセス毎に設定される。
自分のsoft limitは0〜hard limitの範囲で変更できる。
自分のhard limitは小さくする分には自由に変更できるが、大きくするには CAP_SYS_RESOURCE という専用の権限がいる。自分以外のプロセスの設定するにも同様の権限がいる。

コマンドラインからはbashのbuilt-inコマンドであるulimitコマンドがよく使われる。

  • ulimit -n あるいは ulimit -Sn でsoft limitを参照。引数つけたら変更。
  • ulimit -Hn でhard limitを参照。引数つけたら変更。

このulimitコマンドはgetrlimit/setrlimitシステムコールを使って実装されている。多分。
で、 ulimit -n コマンドではgetrlimit/setrlimitでRLIMIT_NOFILEという項目を取得/変更している。
このRLIMIT_NOFILEのデフォルトはsoft limitは1024, hard limitは4096。

file descriptorについてググってると、INR_OPENを変更してlinux kernelをrebuildすれば上限を変更できる、という記事が見つかるが、これは古いlinux kernelのRLIMIT_NOFILEのsoft limit, hard limitの初期値を変更する、という話の模様。今はINR_OPENという変数はない。

で、このRLIMIT_NOFILEは前述の通りulimitコマンドなどで変更できるが、これはkernelパラメータのfs.nr_openが上限になる。nr_openのデフォルトは1048576(=1024*1024)。
これも変更できるが、上限が次の式で表されていた。普通の64bit環境だと2147483584(=(2^31-1)&-64)になると思う多分

unsigned int sysctl_nr_open_max =
    __const_min(INT_MAX, ~(size_t)0/sizeof(void *)) & -BITS_PER_LONG;

システム全体の上限

システム全体の上限はkernelパラメータのfs.file-max。

当然nr_open <= file-maxになるものかと思ったけど、priviledged userはfile-max以上のファイルを開けるっぽい。びっくり。

初期値はここで決まるっぽい。正確には理解してないけど、メモリ(kb)の10%弱になるみたい。

最大値はunsigned longの上限までいける。

まとめ

設定可能な1プロセスあたりの開ける最大のfile descriptorの数は以下の式で表され、一般的な64bit環境だと2147483584。

設定可能なシステム全体で開ける最大のfile descriptorの数はULONG_MAXで、一般的な64bi環境だと18446744073709551615。
調べ始めた動機の1サーバで捌けるコネクション数という意味だと、設定すれば1プロセスで20億超捌けるのでこれがボトルネックになるということはなさそう。

linuxのコードベースに詳しくないのでソースコード引用したところ間違ってたらすいません。

あと最初に変更する場合は注意と書いたけど、特にfs.file-maxは注意しないといけない。これを小さすぎる値にすると新しいfile descriptorが作れなくなって、rootユーザでなければbashからの実行ファイルの実行が全て失敗する模様1。つまりfs.file-maxの値を戻すsysctlコマンドもrebootも失敗する。擬似端末作ることができないから新規にログインも出来ない。またunsigned longの上限を超える値を設定するとオーバーフローするらしくULONG_MAXを少し超えた値を設定しようとしても同様のことが起こる。実際にやってみるとこんな感じになる。

vagrant@ubuntu-xenial:~$ sudo sysctl fs.file-max=18446744073709551617
fs.file-max = 18446744073709551617
vagrant@ubuntu-xenial:~$ ls
-bash: start_pipeline: pgrp pipe: Too many open files in system
-bash: /bin/ls: Too many open files in system

VM等、外から再起動が可能ならともかくデータセンターの物理サーバとかだと多分現場で物理電源ボタンを押すか電源供給を止めて終了してから起動し直すなどするしかない。
RLIMIT_NOFILEはプロセスごとなのでshellならログインし直したらリセットされるし、特定のプロセスを別プロセスから変更することも可能。nr_openはminimumが設定されていて64bitマシンだと最小64までしか設定できない。file-maxと比べると間違った操作してもまだまし。


  1. 実行ファイルを実行するたびにpipeを作ってるみたい。 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
12
Help us understand the problem. What are the problem?