MENU

selectとepollの違いと実装例

目次

selectとepollとは

selectepoll は、いずれもでI/O多重化を行うための関数です。

使い方としては、複数のファイルディスクリプタの状態を監視し、どのファイルディスクリプタが読み込み可能、書き込み可能、またはエラー状態にあるかを判別するために使用されます。

select

select の方がepollと比べて古い関数で、以下のような特徴があります。

特徴

使用法

ファイルディスクリプタの集合を3つのセット(読み取り、書き込み、例外)に分類し、それぞれのセットにファイルディスクリプタを追加して監視します。

select関数を呼び出すと、指定されたタイムアウト期間内にいずれかのファイルディスクリプタが状態変化を起こした場合に応答を返します。

制限

監視できるファイルディスクリプタの数がシステムによって制限されており、デフォルトでは FD_SETSIZE というマクロで決まります。通常は1024です。

また、多くのファイルディスクリプタを監視する場合には非効率です。

移植性

古くからある関数のため、広くサポートされており、古いシステムでも動作します。

epoll

epoll は、Linuxで提供されている新しいI/O多重化機構で、特に高いスケーラビリティを持つアプリケーションに適しています。

特徴

使用法

イベントを監視するための「監視セット」を作成し、このセットにファイルディスクリプタを追加・削除することができます。epoll_wait 関数を使って、監視セットに対するイベントの発生を待ちます。

利点
  • 膨大な数のディスクリプタを効率的に管理でき、特に大量の接続を処理するサーバーアプリケーションでパフォーマンスが向上します。
  • エッジトリガー(イベントが一度発生したら通知)とレベルトリガー(イベントが発生し続ける限り通知)の2つのモードが利用可能です。
  • 状態変化があった部分だけを通知するため、select のように全てのファイルディスクリプタを再スキャンする必要がありません。
制限

Linux固有の機能であり、移植性が低いため、他のUnix系システムやWindowsでは利用できません。

適用例

小規模なプロジェクトや古い環境であれば、select の方が簡単で移植性も高いため、適している場合が多いです。

大量のソケットを扱うサーバーアプリケーションパフォーマンスが求められる環境では、epoll の方が効率的です。

実装例

2つのファイルディスクリプタを監視する簡単な実装例を、selectepoll それぞれで紹介します。

select

#include <sys/select.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd1 = 0; // 標準入力 (通常はFD 0)
    int fd2 = 1; // 標準出力 (通常はFD 1)
    fd_set readfds;

    while (1) {
        FD_ZERO(&readfds); // fd_setをクリア
        FD_SET(fd1, &readfds); // fd1をセットに追加
        FD_SET(fd2, &readfds); // fd2をセットに追加

        int max_fd = fd1 > fd2 ? fd1 : fd2; // 最大のFDを取得

        // selectで監視
        int activity = select(max_fd + 1, &readfds, NULL, NULL, NULL);

        if (activity < 0) {
            perror("select error");
            break;
        }

        if (FD_ISSET(fd1, &readfds)) {
            char buffer[1024];
            int bytes = read(fd1, buffer, sizeof(buffer) - 1);
            if (bytes > 0) {
                buffer[bytes] = '\0';
                printf("fd1 (stdin) から読み込み: %s\n", buffer);
            }
        }

        if (FD_ISSET(fd2, &readfds)) {
            printf("fd2 (stdout) にイベント発生\n");
        }
    }

    return 0;
}

epoll

#include <sys/epoll.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd1 = 0; // 標準入力 (通常はFD 0)
    int fd2 = 1; // 標準出力 (通常はFD 1)
    int epoll_fd = epoll_create1(0);
    struct epoll_event event, events[2];

    // fd1 を epoll に登録
    event.events = EPOLLIN;
    event.data.fd = fd1;
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd1, &event);

    // fd2 を epoll に登録
    event.data.fd = fd2;
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd2, &event);

    while (1) {
        int num_fds = epoll_wait(epoll_fd, events, 2, -1);

        for (int i = 0; i < num_fds; i++) {
            if (events[i].data.fd == fd1) {
                char buffer[1024];
                int bytes = read(fd1, buffer, sizeof(buffer) - 1);
                if (bytes > 0) {
                    buffer[bytes] = '\0';
                    printf("fd1 (stdin) から読み込み: %s\n", buffer);
                }
            }

            if (events[i].data.fd == fd2) {
                printf("fd2 (stdout) にイベント発生\n");
            }
        }
    }

    close(epoll_fd);
    return 0;
}
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

20代の組み込みソフトウェアエンジニア
主な使用言語はC++

---------------------資格---------------------
応用情報技術者
ネットワークスペシャリスト

目次