web-dev-qa-db-ja.com

ソケット受け入れ-「開いているファイルが多すぎます」

私はマルチスレッドサーバーを作成する必要がある学校プロジェクトに取り組んでいます。現在、それに対していくつかのテストを実行して、Apacheと比較しています。私はそれを支援するために自動ベンチを使用していますが、いくつかのテストを実行した後、または接続を確立するのに高すぎるレート(約600+)を与えた場合、「Too many open files」エラーが表示されます。

リクエストの処理が完了した後、常にソケットでclose()を実行します。 shutdown()関数も使用しようとしましたが、何も役に立たないようです。これを回避する方法はありますか?

53
Scott

Linuxには、開くことができるファイル記述子の数に制限がある複数の場所があります。

以下を確認できます。

cat /proc/sys/fs/file-max

これにより、システム全体のファイル記述子の制限が与えられます。

シェルレベルでは、これにより個人的な制限がわかります。

ulimit -n

これは/etc/security/limits.confで変更できます-これはnofileパラメーターです。

ただし、ソケットを正しく閉じている場合は、多くの同時接続を開いていない限り、これを受け取るべきではありません。何かがソケットを適切に閉じるのを妨げているようです。それらが適切に処理されていることを確認します。

55
Reed Copsey

同様の問題がありました。迅速な解決策は次のとおりです。

ulimit -n 4096

説明は次のとおりです。各サーバー接続はファイル記述子です。 CentOS、Redhat、Fedora、おそらくその他では、ファイルのユーザー制限は1024です-理由はわかりません。次のように入力すると簡単に確認できます:ulimit -n

これは、システムの最大ファイル(/ proc/sys/fs/file-max)とはあまり関係がないことに注意してください。

私の場合、Redisに問題があったため、次のことを行いました。

ulimit -n 4096
redis-server -c xxxx

redisの代わりに、サーバーを起動する必要があります。

26
Nick

TCPには、接続が完全に閉じられるようにする「TIME_WAIT」という機能があります。ソケットが閉じられた後もしばらく待機するためには、接続の一端が必要です。

高性能サーバーでは、サーバーではなくクライアントがTIME_WAITになることが重要です。クライアントはポートを開く余裕がありますが、ビジーなサーバーではポートがすぐに不足したり、開いているFDが多すぎたりする可能性があります。

これを実現するために、サーバーは最初に接続を閉じないでください。クライアントが閉じるのを常に待つ必要があります。

15
Ed4

つかいます lsof -u `whoami` | wc -lユーザーが開いているファイルの数を調べる

10
Edson Medina

これは、同時に開くことができるファイルの最大数を意味します。

解決済み:

ファイル_/etc/security/limits.conf_の最後に、次の行を追加する必要があります。

_* soft nofile 16384
* hard nofile 16384
_

現在のコンソールでrootから(Sudoは機能しません)実行するには:

_ulimit -n 16384
_

これはオプションですが、サーバーを再起動できる場合。

_/etc/nginx/nginx.conf_ファイルで、新しい値_worker_connections_を登録し、_16384_を値_worker_processes_で除算します。

_ulimit -n 16384_を実行しなかった場合、再起動する必要があり、問題は後退します。

PS:

修復後がログに表示される場合error accept() failed (24: Too many open files)

Nginx構成では、propevia(たとえば):

_worker_processes 2;

worker_rlimit_nofile 16384;

events {
  worker_connections 8192;
}
_
7
shilovk

私もこの問題を抱えていました。ファイルハンドルリークがあります。これをデバッグするには、開いているすべてのファイルハンドルのリストを出力します(POSIXシステム)。

void showFDInfo()
{
   s32 numHandles = getdtablesize();

   for ( s32 i = 0; i < numHandles; i++ )
   {
      s32 fd_flags = fcntl( i, F_GETFD ); 
      if ( fd_flags == -1 ) continue;


      showFDInfo( i );
   }
}

void showFDInfo( s32 fd )
{
   char buf[256];

   s32 fd_flags = fcntl( fd, F_GETFD ); 
   if ( fd_flags == -1 ) return;

   s32 fl_flags = fcntl( fd, F_GETFL ); 
   if ( fl_flags == -1 ) return;

   char path[256];
   sprintf( path, "/proc/self/fd/%d", fd );

   memset( &buf[0], 0, 256 );
   ssize_t s = readlink( path, &buf[0], 256 );
   if ( s == -1 )
   {
        cerr << " (" << path << "): " << "not available";
        return;
   }
   cerr << fd << " (" << buf << "): ";

   if ( fd_flags & FD_CLOEXEC )  cerr << "cloexec ";

   // file status
   if ( fl_flags & O_APPEND   )  cerr << "append ";
   if ( fl_flags & O_NONBLOCK )  cerr << "nonblock ";

   // acc mode
   if ( fl_flags & O_RDONLY   )  cerr << "read-only ";
   if ( fl_flags & O_RDWR     )  cerr << "read-write ";
   if ( fl_flags & O_WRONLY   )  cerr << "write-only ";

   if ( fl_flags & O_DSYNC    )  cerr << "dsync ";
   if ( fl_flags & O_RSYNC    )  cerr << "rsync ";
   if ( fl_flags & O_SYNC     )  cerr << "sync ";

   struct flock fl;
   fl.l_type = F_WRLCK;
   fl.l_whence = 0;
   fl.l_start = 0;
   fl.l_len = 0;
   fcntl( fd, F_GETLK, &fl );
   if ( fl.l_type != F_UNLCK )
   {
      if ( fl.l_type == F_WRLCK )
         cerr << "write-locked";
      else
         cerr << "read-locked";
      cerr << "(pid:" << fl.l_pid << ") ";
   }
}

開いているすべてのファイルをダンプすることにより、ファイルハンドルリークがどこにあるかをすばやく把握できます。

サーバーがサブプロセスを生成する場合。例えば。これが「フォーク」スタイルのサーバーである場合、または他のプロセスを生成する場合(例えばcgi経由)、「cloexec」でファイルハンドルを作成する必要があります-実際のファイルとソケットの両方に対して。

Cloexecを使用しない場合、フォークまたはスポーンするたびに、開いているすべてのファイルハンドルが子プロセスで複製されます。

また、ネットワークソケットのクローズに失敗するのは非常に簡単です。リモートパーティが切断したときにそれらを放棄するだけです。これは、狂ったようにハンドルをリークします。

6
Rafael Baptista

閉じたソケットが実際に解放されるまでに少し時間がかかることがあります

lsofは開いているファイルをリストします

cat /proc/sys/fs/file-maxシステム制限があるかどうかを確認する

4
Partly Cloudy

CentOSに関する別の情報。この場合、「systemctl」を使用してプロセスを起動するとき。システムファイルを修正する必要があります==> /usr/lib/systemd/system/processName.serviceファイル内のこの行:

LimitNOFILE=50000

そして、システムconfをリロードするだけです:

systemctl daemon-reload
2
franck U

私は同じ問題を抱えていて、close()呼び出しの戻り値をチェックすることを気にしませんでした。戻り値のチェックを開始すると、問題は不思議なことに消えました。

コンパイラの最適化の不具合(私の場合はgcc)しか想定できず、close()呼び出しには副作用がなく、戻り値が使用されない場合は省略できると想定しています。

1
dwk

プログラムに開いているファイルのulimitより多くの開いている記述子がある場合(ulimit -aにリストされます)、カーネルはそれ以上のファイル記述子を開くことを拒否します。ファイル記述子のリークがないことを確認します-たとえば、しばらく実行してから、アイドル状態のときに余分なfdsがまだ開いているかどうかを確認し、問題がある場合は、nofile ulimitを変更します/etc/security/limits.confのユーザー

1
bdonlan

MacOSでは、制限を表示します。

launchctl limit maxfiles

次のような結果:maxfiles 256 1000

数値(ソフト制限とハード制限)が低すぎる場合、上限を設定する必要があります。

Sudo launchctl limit maxfiles 65536 200000
0
letanthang