epollとkqueueによる非同期I/O
Linuxではepoll、BSDやmacOSではkqueueを使って、多数の接続に対応します。
「epollとkqueueによる非同期I/O」はCoddyKit上の無料C++ Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC++ Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C++ Academyコースには全4レッスンが含まれています。
なぜ非同期I/Oなのか
接続ごとに1つのスレッドを割り当てる方式は、数千、数百万のクライアントには対応できません。非同期I/Oを使うと、複数のソケットのいずれかが読み取り可能または書き込み可能になるのを待ちながら、1つのスレッドで多数のソケットを処理できます。
Linux:epoll
LinuxのスケーラブルなイベントAPIです。ファイルディスクリプターをepollインスタンスに追加し、そのいずれかで発生するイベントを待ちます。
#include <sys/epoll.h>
int epfd = epoll_create1(0);
epoll_event ev{};
ev.events = EPOLLIN;
ev.data.fd = sock;
epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev);イベントを待つ
epoll_waitはイベントが利用可能になるまでブロックし、その後、準備が整ったディスクリプターを返します。
epoll_event events[64];
int n = epoll_wait(epfd, events, 64, -1);
for (int i = 0; i < n; ++i) {
int fd = events[i].data.fd;
handle_io(fd);
}エッジトリガーとレベルトリガー
モードは2つあります。
- レベルトリガー(デフォルト)— 条件が成立している間、epollは報告を続けます
- エッジトリガー(
EPOLLET)— 状態が変化したときだけ報告します。毎回、ソケットを完全に読み出す必要があります
macOSとBSD:kqueue
kqueueはBSDにおけるepoll相当の仕組みで、macOSでも利用できます。概念は似ていますが、APIは異なります。
#include <sys/event.h>
int kq = kqueue();
struct kevent ev;
EV_SET(&ev, sock, EVFILT_READ, EV_ADD, 0, 0, nullptr);
kevent(kq, &ev, 1, nullptr, 0, nullptr);Windows:IOCP
WindowsではI/O Completion Ports(IOCP)を使用します。epollやkqueueの準備完了モデルではなく、完了時に通知する完了ベースのモデルです。
ノンブロッキングソケット
fcntl(fd, F_SETFL, O_NONBLOCK)を使って、ソケットをノンブロッキングに設定します。そうしないと、読み取りや書き込みによってイベントループ全体がブロックする可能性があります。
イベントループパターン
永続的に実行するループは、次のようになります。
- 準備が整ったイベントを待つ
- 各イベントについて、ブロックする状態になるまでI/Oを実行する
- 繰り返す
多数のクライアントの処理
1つのepollスレッドで、同時に数万の接続を処理できます。これはC10K問題への対応です。現在のシステムでは、数百万接続まで対応することもあります。
よくある落とし穴
次の点に注意してください。
- ノンブロッキングモードの設定を忘れる
- ソケットを読み出し切らずにエッジトリガーを使用する
- クローズされたFDをepollセットから削除しない
- 接続ごとのバッファーをリークさせる
高レベルのラッパー
epollループを手作業で記述すると、エラーが発生しやすくなります。本番コードではlibuv、libevent、またはBoost.Asioを使用してください。これらはプラットフォーム間の違いを隠蔽し、テスト済みのI/Oループを提供します。
非同期とスレッド
非同期I/Oはよりスケーラブルですが、記述は難しくなります。数千程度の接続を扱うサーバーでは、接続ごとにスレッドを割り当てるモデルのほうが単純です。想定する負荷に基づいて選択してください。
確認問題
epollにおけるエッジトリガーモードとレベルトリガーモードの違いは何ですか。
まとめ
非同期I/Oは、1つのスレッドで多数のソケットを処理することにより、大規模な同時実行数に対応できます。Linuxではepoll、BSDとmacOSではkqueue、WindowsではIOCPを使用します。ソケットはノンブロッキングに設定してください。本番環境では、イベントループを手作業で実装せず、ライブラリ(Asio、libuv、libevent)を使用します。
よくある質問
「epollとkqueueによる非同期I/O」レッスンは無料ですか?
はい。「epollとkqueueによる非同期I/O」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C++ Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C++ Academyコースには全4レッスンが含まれています。
「epollとkqueueによる非同期I/O」で何を学びますか?
Linuxではepoll、BSDやmacOSではkqueueを使って、多数の接続に対応します。 ブラウザで直接実行するハンズオンコードでC++ Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C++ Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC++ Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「epollとkqueueによる非同期I/O」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC++ Academyレッスンでコードを書いて実行できますか?
はい。すべてのC++ Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- C++のBSDソケットAPI
- TCPエコーサーバーの構築
- epollとkqueueによる非同期I/O
- Boost Asioで現代的な非同期I/Oを使う