# 機能設計書 10-イベント通知（kqueue）

## 概要

本ドキュメントは、FreeBSDにおけるkqueue/keventイベント通知機能の設計を記述する。ファイルディスクリプタ、シグナル、タイマー、プロセスイベントなどを効率的に監視するカーネル内イベント通知機構を対象とする。

### 本機能の処理概要

**業務上の目的・背景**：kqueueはFreeBSD起源の高性能イベント通知機構であり、Linuxのepoll、SolarisのEvent Portsに相当する。大量のファイルディスクリプタやイベントソースを単一のインタフェースで効率的に監視でき、高性能ネットワークサーバ（nginx, haproxy等）やイベント駆動アプリケーションの基盤となる。select(2)/poll(2)のO(n)スキャンに対し、kqueueはO(1)のイベント通知を実現する。

**機能の利用シーン**：高性能Webサーバのコネクション管理、データベースサーバのI/O多重化、ファイルシステムの変更監視（EVFILT_VNODE）、プロセスの状態監視（EVFILT_PROC）、タイマーイベント（EVFILT_TIMER）、シグナル待機（EVFILT_SIGNAL）で利用される。

**主要な処理内容**：
1. **kqueue生成**: sys_kqueue()/sys_kqueuex()によるkqueueファイルディスクリプタの生成。kqueuexではKQUEUE_CLOEXECフラグをサポート。
2. **keventイベント登録**: sys_kevent()によるイベントフィルタの登録・変更・削除。kqueue_register()が個々のkeventの登録を処理する。
3. **イベントスキャン**: kqueue_scan()による登録済みイベントの発火チェック。発火したイベントをユーザ空間にcopyoutする。
4. **各種フィルタ**: EVFILT_READ/WRITE（I/O）、EVFILT_VNODE（ファイル変更）、EVFILT_PROC（プロセス）、EVFILT_SIGNAL（シグナル）、EVFILT_TIMER（タイマー）、EVFILT_USER（ユーザ定義）等のフィルタ実装。
5. **knote管理**: struct knoteによる個々の監視エントリの管理。knlistによるknoteのリスト管理。

**関連システム・外部連携**：ソケットサブシステム（EVFILT_READ/WRITE）、VFS（EVFILT_VNODE）、プロセス管理（EVFILT_PROC）、シグナルサブシステム（EVFILT_SIGNAL）、jail/jaildesc（EVFILT_JAIL）との連携。

**権限による制御**：kqueue自体は一般ユーザが利用可能。ただし監視対象のファイルディスクリプタへのアクセス権限が必要。Capsicumケイパビリティモードとの互換性あり。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 該当なし | - | kqueueはカーネルAPIであり、直接的なUI画面は存在しない |

## 機能種別

カーネル基盤機能（イベント駆動I/O多重化）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| kqueue_args（なし） | - | - | kqueue()は引数なし | - |
| kqueuex_args.flags | unsigned int | No | KQUEUE_CLOEXEC, KQUEUE_CPONFORK | 未定義フラグはEINVAL |
| kevent_args.fd | int | Yes | kqueue FD | 有効なkqueue FD |
| kevent_args.changelist | struct kevent * | No | 登録するイベント配列 | - |
| kevent_args.nchanges | int | Yes | 登録イベント数 | 非負 |
| kevent_args.eventlist | struct kevent * | No | 発火イベント受取配列 | - |
| kevent_args.nevents | int | Yes | 最大受取イベント数 | 非負 |
| kevent_args.timeout | struct timespec * | No | タイムアウト | NULL=無限待ち |
| kevent.ident | uintptr_t | Yes | イベント識別子（FD等） | フィルタ依存 |
| kevent.filter | short | Yes | EVFILT_READ/WRITE/VNODE等 | 有効なフィルタ |
| kevent.flags | u_short | Yes | EV_ADD/EV_DELETE/EV_ENABLE等 | 有効なフラグ |

### 入力データソース

kqueue()/kevent()システムコール経由のユーザ空間要求。libc/libpthreadのkeventラッパー経由。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| kqueue戻り値 | int | kqueueファイルディスクリプタ |
| kevent戻り値 | int | 発火したイベント数 |
| eventlist | struct kevent[] | 発火したイベントの配列 |
| kevent.data | intptr_t | フィルタ固有データ（読み取り可能バイト数等） |

### 出力先

ユーザ空間のkevent構造体配列。

## 処理フロー

### 処理シーケンス

```
1. kqueue生成
   ├─ sys_kqueue() / sys_kqueuex()
   │    ├─ kqueue構造体割り当て
   │    ├─ ファイルディスクリプタ割り当て
   │    └─ kqueue FD返却

2. イベント登録
   ├─ sys_kevent() → kevent_copyops経由
   │    ├─ changelist処理ループ
   │    │    └─ kqueue_register(): 個々のkevent登録
   │    │         ├─ フィルタ別f_attach()呼び出し
   │    │         ├─ knoteの作成・登録
   │    │         └─ knlistへの追加

3. イベント待機・取得
   ├─ sys_kevent() → kqueue_scan()
   │    ├─ kqueue->kq_knlistのスキャン
   │    ├─ 各knoteのf_event()チェック
   │    ├─ 発火イベントのcopyout
   │    └─ タイムアウト処理
```

### フローチャート

```mermaid
flowchart TD
    A[kqueue 生成] --> B[kevent 登録]
    B --> C[kqueue_register]
    C --> D[フィルタ f_attach]
    D --> E[knote作成・knlist追加]
    E --> F[kevent 待機]
    F --> G[kqueue_scan]
    G --> H{発火イベントあり?}
    H -->|Yes| I[copyout: イベント返却]
    H -->|No| J{タイムアウト?}
    J -->|No| G
    J -->|Yes| K[0返却]
    I --> L[発火イベント数返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | O(1)イベント通知 | 監視FD数に依存しないイベント通知 | 全kevent操作時 |
| BR-02 | EV_ONESHOTセマンティクス | EV_ONESHOT付きイベントは1回発火後に自動削除 | EV_ONESHOTフラグ設定時 |
| BR-03 | EV_CLEARセマンティクス | EV_CLEAR付きイベントは取得後に状態リセット | EV_CLEARフラグ設定時 |
| BR-04 | EV_DISPATCHセマンティクス | EV_DISPATCH付きイベントは取得後に自動無効化 | EV_DISPATCHフラグ設定時 |
| BR-05 | フィルタ固有data | EVFILT_READはdata=読取可能バイト数を返す | EVFILT_READ発火時 |
| BR-06 | kqueuex CLOEXEC | KQUEUE_CLOEXECでexec時自動クローズ | kqueuex()使用時 |

### 計算ロジック

kqueue_scan()（2083行目）ではkqueue上の登録済みknoteを走査し、各knoteのフィルタ固有f_event()関数を呼び出してイベント発火を判定する。発火したイベントはマーカーknoteを使用した安全なリスト走査で順次処理される。

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象データ構造 | 操作種別 | 概要 |
|-----|-------------|---------|------|
| EV_ADD | knote | INSERT | 新規監視エントリの登録 |
| EV_DELETE | knote | DELETE | 監視エントリの削除 |
| EV_ENABLE | knote | UPDATE | 無効化されたエントリの再有効化 |
| EV_DISABLE | knote | UPDATE | エントリの一時無効化 |
| kqueue_scan | knote list | SELECT | 発火イベントの走査 |

### テーブル別操作詳細

#### struct knote

| 操作 | 項目（フィールド名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | kn_id | イベント識別子（FD等） | ユーザ指定 |
| INSERT | kn_filter | フィルタ種別 | EVFILT_READ等 |
| INSERT | kn_flags | イベントフラグ | EV_ADD/ONESHOT等 |
| UPDATE | kn_data | フィルタ固有データ | f_event()で更新 |
| UPDATE | kn_status | 内部状態 | KN_ACTIVE等 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ENOMEM | メモリ不足 | knote割り当て失敗 | メモリ解放後に再試行 |
| EBADF | 不正FD | 無効なkqueue FD | 有効なkqueue FDを指定 |
| EINVAL | 引数不正 | 無効なフィルタ・フラグ指定 | 有効なフィルタ・フラグを使用 |
| ENOENT | 不存在 | EV_DELETE対象のknoteが見つからない | 登録済みのidentを指定 |
| ESRCH | プロセス不存在 | EVFILT_PROC対象PIDが存在しない | 有効なPIDを指定 |

### リトライ仕様

kevent()のタイムアウトはユーザが指定する。EINTRで中断された場合、残りのタイムアウト時間を計算して再呼び出しが可能。

## トランザクション仕様

kqueueはkqueue固有ロック（KQ_LOCK）で保護される。knoteの操作はknlistロックで保護される。kevent()呼び出し中のchangelist処理とeventlist取得は、changelist処理が先に行われ、その後にkqueue_scan()が実行される。changelist内のエラーはeventlistにEV_ERRORとして返される。

## パフォーマンス要件

- kqueue生成: マイクロ秒オーダー
- kevent登録: O(1)（ハッシュテーブルベース）
- イベント取得: O(発火イベント数)（発火イベントのみスキャン）
- select/pollに対する優位性: 監視FD数が多い場合にO(n)→O(1)

## セキュリティ考慮事項

- kqueueはファイルディスクリプタとして管理されるため、通常のFDセキュリティモデルに従う
- Capsicumケイパビリティモードでの使用が可能
- EVFILT_PROCによるプロセス監視はpidに対するアクセス権限が必要
- jail環境での制限（EVFILT_JAIL）

## 備考

- kern_event.cは3377行の大規模ファイルであり、kqueue/keventの全実装を含む
- sys_kqueue()は1194行目に定義され、kern_kqueue()を呼び出す
- sys_kqueuex()は1201行目に定義され、KQUEUE_CLOEXEC/CPONFORKフラグをサポート
- sys_kevent()は1291行目に定義されている
- kqueue_register()は1652行目に定義され、個々のkevent登録処理を行う
- kqueue_scan()は2083行目に定義され、イベントスキャンの主要ロジックを実装
- FreeBSD 11互換（COMPAT_FREEBSD11）のkeventサポートが含まれる

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | event.h | `sys/sys/event.h` | struct keventの定義。ident、filter、flags、fflags、data、udataフィールド |
| 1-2 | eventvar.h | `sys/sys/eventvar.h` | struct kqueue、struct knoteの内部定義 |
| 1-3 | event.h | `sys/sys/event.h` | EVFILT_*定数、EV_*フラグ定数の定義 |

**読解のコツ**: kqueueの中核は「knote」（kernel note）構造体であり、個々の監視エントリを表す。各knoteはフィルタ固有の`filterops`（f_attach, f_detach, f_event）を持ち、イベントソースの種別に応じた処理を行う。kqueue構造体はknoteのリストを保持し、kqueue_scan()で発火チェックを行う。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | kern_event.c | `sys/kern/kern_event.c` | sys_kqueue()（1194行目）、sys_kqueuex()（1201行目）、sys_kevent()（1291行目） |

**主要処理フロー**:
1. **1194行目**: sys_kqueue()はkern_kqueue()を引数なしで呼び出す
2. **1201-1206行目**: sys_kqueuex()はフラグバリデーション後にkern_kqueue()を呼び出す
3. **1291-1296行目**: sys_kevent()はkevent_copyopsを初期化してkevent_internal()を呼び出す

#### Step 3: イベント登録を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | kern_event.c | `sys/kern/kern_event.c` | kqueue_register()（1652行目）: フィルタ選択、knote作成/更新、f_attach()呼び出し |

**主要処理フロー**:
- **1652-1654行目**: kqueue_register()の引数（kqueue, kevent, thread, mflag）
- **1655行目**: fops（filterops）の取得
- knoteのハッシュテーブルまたはFDインデックスからの検索

#### Step 4: イベントスキャンを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | kern_event.c | `sys/kern/kern_event.c` | kqueue_scan()（2083行目）: 発火イベントの走査とユーザ空間へのcopyout |

**主要処理フロー**:
- **2083-2088行目**: kqueue_scan()の引数（kqueue, maxevents, copyops, timespec, keva, thread）
- マーカーknoteを使用した安全なリスト走査

### プログラム呼び出し階層図

```
sys_kqueue() / sys_kqueuex()
    |
    +-- kern_kqueue()
            +-- kqueue構造体割当
            +-- FD割当

sys_kevent()
    |
    +-- kevent_internal()
            |
            +-- [changelist処理] kqueue_register() × nchanges回
            |       +-- filterops->f_attach() [フィルタ登録]
            |       +-- knote作成/更新
            |
            +-- [eventlist取得] kqueue_scan()
                    +-- knoteリスト走査
                    +-- filterops->f_event() [発火チェック]
                    +-- kevent_copyout() [ユーザ空間へ]

フィルタ固有処理:
    +-- filt_soread() [EVFILT_READ: ソケット]
    +-- filt_sowrite() [EVFILT_WRITE: ソケット]
    +-- filt_vnode() [EVFILT_VNODE: ファイル変更]
    +-- filt_proc() [EVFILT_PROC: プロセス]
    +-- filt_signal() [EVFILT_SIGNAL: シグナル]
    +-- filt_timer() [EVFILT_TIMER: タイマー]
```

### データフロー図

```
[入力]                  [処理]                         [出力]

kqueue()呼出 ────> kern_kqueue() ──────────────────> kqueue FD

changelist ──────> kqueue_register() ──────────────> knote作成/更新
(struct kevent[])  f_attach()                        フィルタ登録

kevent(fd,NULL,    kqueue_scan() ─────────────────> eventlist
  0,evlist,n,ts)   f_event() [発火チェック]           (struct kevent[])
                   kevent_copyout()                   発火イベント数
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| kern_event.c | `sys/kern/kern_event.c` | ソース | kqueue/kevent主要実装（3377行） |
| event.h | `sys/sys/event.h` | ヘッダ | struct kevent、EVFILT_*、EV_*定義 |
| eventvar.h | `sys/sys/eventvar.h` | ヘッダ | struct kqueue、struct knote内部定義 |
