7

(lwt githubの問題にクロス投稿)

ファイル記述子をリークするこのコード サンプルの使用法を煮詰めました。

あなたが持っていると言う:

#require "lwt.unix"

open Lwt.Infix

let echo ic oc = Lwt_io.(write_chars oc (read_chars ic))

let program =
  let server_address = Unix.(ADDR_INET (inet_addr_loopback, 2000)) in

  let other_addr = Unix.(ADDR_INET (inet_addr_loopback, 2001)) in

  let server = Lwt_io.establish_server server_address begin fun (tcp_ic, tcp_oc) ->
      Lwt_io.with_connection other_addr begin fun (nc_ic, nc_oc) ->

        Lwt_io.printl "Created connection" >>= fun () ->
        echo tcp_ic nc_oc <&> echo nc_ic tcp_oc >>= fun () ->
        Lwt_io.printl "finished"

      end
      |> Lwt.ignore_result

    end
  in
  fst (Lwt.wait ())

let () =
  Lwt_main.run program

次に、次を使用して単純なサーバーを作成します。

nc -l 2001

そしてOCamlコードを起動しましょう utop example.ml

次に、クライアントを開きます

nc localhost 2000
blah blah
^c

次に、lsof を使用してポート 2000 の接続を見ると、

ocamlrun 71109 Edgar    6u  IPv4 0x7ff3e309cb80aead      0t0  TCP 127.0.0.1:callbook (LISTEN)
ocamlrun 71109 Edgar    7u  IPv4 0x7ff3e309c9dc8ead      0t0  TCP 127.0.0.1:callbook->127.0.0.1:54872 (CLOSE_WAIT)

実際、 を使用するたびに、lsof の使用から残りのレコードをnc localhost 2000取得します。CLOSE_WAIT

最終的に、これはシステムがファイル記述子を使い果たすことにつながります。これは、ほとんど迷惑なことにプログラムをクラッシュさせることはありませんが、Lwt がハングアップすることにつながります。

私が何か間違ったことをしているのか、それともこれが本物のバグなのかはわかりません。いずれにせよ、これは私にとって深刻なバグであり、10時間以内にファイル記述子を使い果たします...

編集:問題は、接続の一方が閉じられているが、もう一方が閉じられていないことだと思われます。with_connectionどちらかの側が閉じるたびに、別名いつでも閉じても、クリーンアップ/クローズアップnc_icする必要があると思いましたnc_oc

編集 II: で記述子を手動で閉じるあらゆる方法を試しましたLwt_io.closeが、まだ CLOSE_WAIT メッセージが表示されます。

編集 III: Lwt_unix.closewith_connection のオプションのfd引数に与えられた raw fd で使用されても、同様の悪い結果が得られます。

編集IV:最も陰湿なのは、私が使用する場合Lwt_daemon.daemonize、この問題は一見消えます

4

2 に答える 2

5

<&>まず、 choose ではなくjoin を使用する理由が明確ではありません<?>。両側のいずれかが接続を閉じたい場合は、接続を閉じる必要があると思います。

関係:サーバーからクライアントへCLOSE_WAITのハーフクローズド接続です。utopnc

TCP 接続は 2 つの半接続で構成され、それらは個別に閉じられます。ncにより、クライアントからサーバーへの接続utopが閉じられncましたCtrl-C。ただし、出力ストリームを閉じることにより、サーバー側で反対の接続を明示的に閉じる必要があります。なぜLwt.establish_server自動的に閉じないのかわかりません。可能性があります。これは設計上の問題です。

これはCentOS 7で機能します:

Lwt_io.printl "Created connection" >>= fun () ->
echo tcp_ic nc_oc <?> echo nc_ic tcp_oc >>= fun () ->
Lwt_io.close tcp_oc >>= fun () ->
Lwt_io.printl "finished"

また、問題を再現するための簡略化されたコード スニペットがあります。

#require "lwt.unix"

let program =
  let server_address = Unix.(ADDR_INET (inet_addr_loopback, 2000)) in

  let _server = Lwt_io.establish_server server_address begin fun (ic, oc) ->
    (* Lwt_io.close oc |> Lwt.ignore_result; *) ()
  end
  in
  fst (Lwt.wait ())

let () =
  Lwt_main.run program

nc localhost 2000数回実行して、CLOSE_WAIT状態の接続を取得します。問題を解決するには、コードのコメントを外します。

于 2016-01-13T07:29:04.393 に答える