質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Socket.IO

Socket.IOはNode.js上で動くライブラリであり、すべてのブラウザとモバイルデバイスでリアルタイムのアプリを作動させる事を目的としています。

TCP

TCP(Transmission Control Protocol)とは、トランスポート層のプロトコルで、コネクション型のデータサービスです。

UNIX

UNIXとは、AT&Tのベル研究所で開発されたコンピューター用のマルチユーザー・マルチタスクのオペレーションシステム(OS)です。政府や教育機関や研究所で広範囲に採用されています。

WebSocket

WebSocketとは双方向・全二重コミュニケーションのためのAPIでありプロトコルのことを指します。WebSocketはHTML5に密接に結びついており、多くのウェブブラウザの最新版に導入されています。

Q&A

解決済

1回答

1108閲覧

preforkサーバーの挙動について(forkの挙動、オープンファイル記述の共有の謎)

domidomi

総合スコア34

Socket.IO

Socket.IOはNode.js上で動くライブラリであり、すべてのブラウザとモバイルデバイスでリアルタイムのアプリを作動させる事を目的としています。

TCP

TCP(Transmission Control Protocol)とは、トランスポート層のプロトコルで、コネクション型のデータサービスです。

UNIX

UNIXとは、AT&Tのベル研究所で開発されたコンピューター用のマルチユーザー・マルチタスクのオペレーションシステム(OS)です。政府や教育機関や研究所で広範囲に採用されています。

WebSocket

WebSocketとは双方向・全二重コミュニケーションのためのAPIでありプロトコルのことを指します。WebSocketはHTML5に密接に結びついており、多くのウェブブラウザの最新版に導入されています。

0グッド

0クリップ

投稿2019/10/05 07:50

背景

プロセスについて説明されているサイト(preforkサーバーを作ってみよう)を読んでいました。

その中でpreforkサーバが触れられており、以下のコードが示されていました。

ruby

1# -*- coding: utf-8 -*- 2require "socket" 3 4number_of_workers = 3 5listening_socket = TCPServer.open(12345) 6 7number_of_workers.times do 8 pid = fork 9 10 if pid 11 # 親`プロセス`は次々に fork で子`プロセス`を作る 12 next 13 else 14 # 子`プロセス` 15 16 # クライアント受け入れ無限地獄 17 loop do 18 puts "`accept`ing..." 19 # 子`プロセス`は全部ここでブロックする。 20 socket = listening_socket.`accept` 21 puts "`accept`ed a client!" 22 23 # クライアントの入力受け取り無限地獄 24 loop do 25 line = socket.gets 26 line.gsub!(/[\r\n]/,"") 27 28 if line == "exit" 29 socket.close 30 puts "closed a connection!" 31 break 32 end 33 34 puts line 35 end 36 end 37 end 38end 39 40# 子`プロセス`は無限ループしてるからここには届かない 41# 親`プロセス`では子`プロセス`の終了を待ち続ける 42Process.waitall

このサーバーは以下の流れで動いているかと思います。

流れ
  1. リスニングソケットを作られる
  2. forkして子プロセスを三つつくる
  3. 3つ全ての子プロセスリスニングソケットaccept(接続待ち状態)に以降する
  4. プロセス達はリスニングソケットオープンファイル記述が共有されている事から、外部Aから接続が来た場合、3つのうち1つのプロセスだけ接続される

(一番処理が先に進んだどれかのリスニングソケットに接続されると、オープンファイル記述に外部Aと接続してるよ!と書かれるから、その他の子プロセスリスニングソケットは「なんだもう外部Aとは接続されてるのか」となり、特段なにもしない)
5. 外部Bから接続された時は、余っている子プロセスリスニングソケットが対応して同じような事をする

これを読んでいて疑問がいくつかわきました。

疑問

その1

流れ3にて一番処理が速かったプロセスリスニングソケットaccept(接続待ち状態)に以降した時に、オープンファイル記述には「今俺accept(接続待ち状態)しているよ!」という旨の文言が書かれてしまい、他のプロセス達はaccept(接続待ち状態)していないにも関わらず自分の事をaccept(接続待ち状態)していると勘違いしないのか?
accept(接続待ち状態)処理をしようとすると既にaccept(接続待ち状態)していますエラーが出たりしないのか?)

その2

最初に外部Aと接続した子プロセスリスニングソケットは、接続済みソケットを生成したあと再び待ち状態になるかと思うけど、この子プロセスリスニングソケットが再び接続を受け取ってしまう事はないのか?

もしリスニングソケットが一度接続済みソケットを生成したら休止状態に入るとするならば、ファイルオープン記述を共有している他の子プロセスリスニングソケットもみんな休止状態に入ってしまい結局一つしか接続できない事になってしまう為、rubyのソケットも一般的なTCPソケットのaccept挙動と同じく、接続済みソケットを生成したあと再びaccept(接続町状態)になると考えています。

所感

オープンファイル記述で共有される内容がよくわからない・・・

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

疑問がわくのは恐らく、OSのカーネル(Linuxカーネル)側の処理を失念されている為かな、と思いました。


疑問その1について

他のプロセス達はaccept(接続待ち状態)していないにも関わらず自分の事をaccept(接続待ち状態)していると勘違いしないのか?

勘違いしません。勘違いする余地が無いようカーネルが管理し、処理を割り振りしています。

(accept(接続待ち状態)処理をしようとすると既にaccept(接続待ち状態)していますエラーが出たりしないのか?

エラーにはなりません。これもエラーにならないよう、カーネルが管理、処理の割り振りをしています。forkで起動した子プロセス同士で同時にacceptを実行しても問題ありません。更に言えば、同一プロセス内で複数のスレッドを起動し、それぞれでacceptを実行しても問題なく動作します。
(Linux ubuntu1604-x64 カーネルバージョン4.15.x 上で、Rubyスクリプトではなく、C言語ベースでfork, pthreadを利用して確認しました)

親プロセスでlisten後にforkされた子プロセス群は、acceptを実行するときにそれぞれがacceptの処理を"自分で"行っているように見えるかもしれません。しかし実態はacceptと言うシステムコールを呼び出した時点で処理がカーネルに委ねられ、プロセスが停まります。(その為、クライアント側から接続するまでは処理がブロックしているように見えます)その後の処理はカーネルが引き継ぎます。

acceptシステムコールは言うなれば「接続を受け付けてください。それまで私は寝ますので、接続されたら起こしてください」とカーネルに依頼するシステムコールです。別の子プロセスが既に接続待ちしていても、接続時に割り振りが変わるだけです。

カーネルのプロセス管理機構は親プロセスと親プロセスからforkした子プロセスの関係を把握していますから、TCPクライアントから接続があると、acceptを実行したそれぞれの子プロセスの内、適切なものを選んでその子プロセスのacceptから返り、その子プロセスのみが実行を再開するようにします。他の子プロセスはカーネルにより停止させられたままです。他の子プロセスが他のクライアントからの送受信を行っている場合でも、矛盾がないようにカーネルが管理しているので、疑問のような問題は起きません。


疑問その2について

この子プロセスのリスニングソケットが再び接続を受け取ってしまう事はないのか?

無いです。疑問その1への回答と同様、そうならないようカーネルが処理しています。疑問を更に生じさせている原因のひとつは、forkシステムコールで生成された子プロセスでは、親プロセスのforkを実行した時点のプロセスメモリの内容やカーネルリソースの一部がコピーされ、分岐して起動されることにあるかもしれません。forkはUNIXの初期からあるシステムコールですが、不思議に思えるかもしれない挙動で、カーネルも矛盾がないように管理して動作させています。

オープンファイル記述で共有される内容がよくわからない・・・

「オープンファイル記述」と言う単語自体はLinuxのmanページですとopenシステムコールの説明に出てきますね。今回はソケットの話ですが。

linux man(2) - open

open() を呼び出すと、「オープンファイル記述」 (open file description) が作成される。ファイル記述とは、システム全体のオープン中のファイルのテーブルのエントリーである。 このオープンファイル記述は、ファイルオフセットとファイル状態フラグ (下記参照) が保持する。 ファイルディスクリプターはオープンファイルっ記述への参照である。

「オープンファイル記述」 の内容の詳細はLinuxのカーネルの中身を探ってみないと分かりませんが、親プロセスが作成した「オープンファイル記述」の原本データを、カーネルはforkシステムコールの実行時にコピーや参照用のインデックスを作成して、プロセス内や子プロセスを含む外部プロセスとの関係で矛盾が無いよう、管理しているわけです。

投稿2019/10/06 07:32

dodox86

総合スコア9183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

domidomi

2019/10/13 03:04

詳細なご回答大変感謝します。 私は「オープンファイル記述」の事を現在プロセスがどんな状態にあるのか記述されているメタデータだと勘違いしていたようです。 確認なのですが、 今回の場合、リスニングソケット(ソケットはファイルであると認識しています)の状態(接続積みであるとか未接続であるとか)はオープンファイル記述に記載されているものではなく、カーネルがその状態を理解しているもので有る為、オープンファイル記述が共有されていても特段問題なく複数TCPクライアントの接続を受け付ける事が出来るという認識であっていますでしょうか?
dodox86

2019/10/13 03:52

メタデータと読んで良いかどうか判断つきかねますが、「オープンファイル記述」と称することの出来るデータは存在するはずです。それはあくまでプロセスと結びついて現在オープンしているファイルを間接的に指し示すものであるはずで、その範囲では、ご認識の通りだと思います。 リスニングソケット含めソケット=ファイル、と言うのは上位のプロセスから見てあるところまでは合っているのですが、末端ではそのソケットはTCP/IPスタックやデバイスドライバーが関係していて、低レベルでのデータ構造では違っています。「ファイル」として扱えるよう、デバイスドライバーと連携し、調停して、さも「ファイル」として扱えるように振舞っています。反対に、「ファイル」としてみた場合にはソケットとしては限界もあって、ソケットの作成はopenではなくsocketですし、接続が確立したソケットのファイルディスクリプターに対して読み書きはread/writeも使えますが、ソケットとして使う場合には機能が足りない場合もあり、そんな場合はソケット用の読み書きシステムコールであるrecv/send、その他様々なフラグやオプションを使う必要があります。「ファイル」として扱える部分では「オープンファイル記述」を用いて読み書きはじめ共有や排他、他プロセスとの矛盾なきアクセスの機能をカーネルは提供している、と言うことになります。 > カーネルがその状態を理解しているもので有る為、オープンファイル記述が共有されていても特段問題なく複数TCPクライアントの接続を受け付ける事が出来るという認識であっていますでしょうか? はい。実際にどこまで共有しているかそれともコピーなのかまでは私自身は分かりませんが、確かに言えることとしては、ファイルディスクリプターと「オープンファイル記述」の内容と管理に責任を持つのはシステムコールを呼び出した後のカーネルなので、任せて問題なく動作します。(と、言いますかUNIX系OSはもともとマルチプロセス、マルチタスクのOSとしてそのように発展してきた)
dodox86

2019/10/13 04:02

しかしながらそもそも、「どうしてこれでちゃんと動作するのか?」と言うご質問に対して「ちゃんと動くよう、OSがそう造られているから。」と言う、何かごまかしたような回答だったとは私自身も自覚しているので、長い説明の割りにはいまいちかな、思っていました。だまされたような気になったとしたらすみません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問