fd 3
保存済みの内容を読み書きする対象を示します。
データを待つスレッドと、多数の接続を監視する仕組み
ファイルや通信のデータは、プログラムが必要とした瞬間に届いているとは限りません。データを待つスレッドを休ませる流れから、多数の接続を一つのスレッドで監視するepollまでを追います。
プログラムがファイルを開いたり、ネットワーク接続を作ったりすると、Linuxカーネルはその対象を管理します。プログラムには、対象を指定するための小さな整数が返されます。この番号をファイルディスクリプタファイルディスクリプタプロセスが、開いたファイルや通信接続を指定するために使う整数。と呼びます。
ネットワーク通信の接続口としてカーネルが管理する対象をsocketと呼びます。read(fd, buffer, size)は、fdで対象を指定し、受け取ったデータをbufferで示したメモリ領域へ書き込みます。戻り値は、実際に読み込んだバイト数です。
fd 3保存済みの内容を読み書きする対象を示します。
fd 4通信相手から届いたデータを読み、送信する対象を示します。
fd 5新しい接続を受け付ける対象を示します。
待ち受けsocketをサービスへ引き継ぐsocket activationを見る
通信接続から読む場合、相手がまだデータを送っていなければ、すぐには結果を返せません。結果が得られるまで呼び出し元のスレッドを待機させる読み書きをblocking I/Oと呼びます。待機中のスレッドはCPUへ割り当てる候補から外れるため、CPUはほかの実行可能なスレッドに使われます。
read(fd, buffer, size)データが届くまで、このスレッドは実行可能な状態から外れます。待っているあいだもCPUを使い続けるわけではありません。
データが到着すると、カーネルは待っていたスレッドを実行可能な状態へ戻します。スケジューラがそのスレッドをCPUへ割り当てた後、read()の処理が続き、アプリへ結果が返ります。
一つの接続でread()を待ち続けると、そのスレッドはほかの接続を処理できません。そこで、複数のファイルディスクリプタのうち、現在I/Oを進められるものをカーネルへ問い合わせます。この状態をI/O readinessI/O readinessreadやwriteなどのI/O操作を、現在は待たずに進められる状態。と呼びます。
監視するファイルディスクリプタを集合に入れてselect()へ渡します。LinuxではFD_SETSIZEによる1024本の上限があり、man pageは現代のアプリにpollまたはepollを勧めています。
ファイルディスクリプタと待ちたいイベントをpollfdの配列にしてpoll()へ渡します。selectの固定上限はありません。
epoll_ctl()で監視対象を登録します。カーネルは監視対象と、現在I/Oを進められる対象を別のlistで管理し、epoll_wait()で後者を返します。
epollepoll多数のファイルディスクリプタから、現在I/Oを進められる対象を受け取るLinuxのAPI。では、interest listが監視対象を、ready listが現在I/Oを進められる対象への参照を保持します。アプリは毎回すべての監視対象を渡し直すのではなく、ready listに入った対象をepoll_wait()から受け取ります。
epoll_ctl()で登録アプリが監視したいファイルディスクリプタを保持します。
現在I/Oを進められる対象への参照が入ります。
epoll_wait()ready listにある対象をまとめて受け取ります。
上の接続を選ぶと、読み込み可能な状態を切り替えられます。
epollの標準動作です。一度にすべてを読まなくても、読み込み可能な状態が続く間は、次のepoll_wait()でも通知されます。
EPOLLETを指定すると、状態の変化を契機に通知されます。通常は、データを待たずにEAGAINを返すnonblocking I/Oに設定し、read()がEAGAINを返すまで読み進めます。
epollが返すのは、「このファイルディスクリプタなら、現在は読み書きなどを進められる」という通知です。通知を受けたアプリは、その後にread()やwrite()を呼びます。
io_uringio_uringI/O操作の依頼と完了結果を、共有メモリ上の二つのqueueで受け渡すLinuxのAPI。では、アプリが読み込みや書き込みなどの操作をSubmission Queueへ登録します。カーネルは処理結果をCompletion Queueへ登録します。複数の操作をまとめて依頼でき、完了した順に結果を受け取れます。
読み込み、書き込み、接続受付などの要求を記述します。
アプリがI/O要求を追加
カーネルが結果を追加
要求を処理し、完了した順に結果を返します。
epoll_wait()はI/Oを進められるファイルディスクリプタを返します。io_uringは、I/O操作の依頼と完了結果を二つのqueueで受け渡します。