第2章: 情報理論とバイナリ通信

2.1 Claude Shannon と情報理論の誕生

2.1.1 "A Mathematical Theory of Communication" (1948)

1948年、Bell Labsの数学者 Claude Shannon は、論文 "A Mathematical Theory of Communication" を発表し、デジタル通信の数学的基礎を確立しました。この論文は、コンピュータ科学史上最も重要な論文の一つとされています。

> "The fundamental problem of communication is that of reproducing at one point either exactly or approximately a message selected at another point." > — Claude Shannon, 1948

Shannon が解決した問題:

【通信の本質的問題】

送信者 ─────→ 通信路 ─────→ 受信者
   M           ↓           M'
              ノイズ

問題: M' = M となることをどう保証するか?

Shannon の洞察:
1. すべてのメッセージは「情報」として数学的に扱える
2. 情報は「ビット」で測定できる
3. 通信路には「容量」がある
4. 適切な符号化で信頼性を高められる

2.1.2 情報エントロピーの定義

Shannon は「情報量」を数学的に定義しました:

定義: 情報エントロピー H

確率 p のイベントが持つ情報量:
  I(p) = -log₂(p) ビット

情報源 S = {s₁, s₂, ..., sₙ} のエントロピー:
  H(S) = -Σ p(sᵢ) log₂ p(sᵢ)

例: 公平なコイン投げ
  p(表) = p(裏) = 0.5
  H = -[0.5 × log₂(0.5) + 0.5 × log₂(0.5)]
    = -[0.5 × (-1) + 0.5 × (-1)]
    = 1 ビット

例: 偏ったコイン (p(表) = 0.9, p(裏) = 0.1)
  H = -[0.9 × log₂(0.9) + 0.1 × log₂(0.1)]
    ≈ 0.47 ビット

Minitalkへの示唆:

SIGUSR1 と SIGUSR2 は等確率で使用される
→ 1シグナルあたり 1ビット の情報を伝達可能
→ これは情報理論的に最適な符号化

2.1.3 シリアル通信の理論

通信は大きく「並列」と「直列(シリアル)」に分類されます:

【並列通信】
        ┌─── ビット7 ───┐
        ├─── ビット6 ───┤
        ├─── ビット5 ───┤
送信側 ─┼─── ビット4 ───┼─ 受信側
        ├─── ビット3 ───┤
        ├─── ビット2 ───┤
        ├─── ビット1 ───┤
        └─── ビット0 ───┘

→ 高速だが、多数の通信線が必要


【シリアル通信】

送信側 ─────────────────────────────── 受信側
        b7 → b6 → b5 → b4 → b3 → b2 → b1 → b0
        ←──────── 時間 ──────────→

→ 1本の通信線で済むが、低速


Minitalkはシリアル通信:
  - 通信「線」= シグナル
  - 8ビットを順番に送信
  - 1文字 = 8回のシグナル送信

2.2 ビットとバイトの数学的基礎

2.2.1 位取り記数法(Positional Notation)

私たちが日常使う10進法と、コンピュータが使う2進法は、どちらも「位取り記数法」に基づいています:

位取り記数法の一般形:

N = Σ aᵢ × bⁱ  (i = 0, 1, 2, ...)

  N : 表現される数
  b : 基数(base)
  aᵢ: 位 i の係数(0 ≤ aᵢ < b)

10進法(b = 10):
  254 = 2×10² + 5×10¹ + 4×10⁰
      = 200 + 50 + 4

2進法(b = 2):
  11111110 = 1×2⁷ + 1×2⁶ + 1×2⁵ + 1×2⁴ + 1×2³ + 1×2² + 1×2¹ + 0×2⁰
           = 128 + 64 + 32 + 16 + 8 + 4 + 2 + 0
           = 254

なぜコンピュータは2進法を使うのか:

  • 物理的単純さ: 電圧の高低(ON/OFF)で表現できる
  • ノイズ耐性: 2状態なら判別が容易
  • 論理演算との親和性: AND, OR, NOT が直接実装可能
  • 2.2.2 ビット位置と重み

    1バイト(8ビット)の各位置には「重み」があります:

    ビット位置:    7      6      5      4      3      2      1      0
    重み(2進):  2⁷     2⁶     2⁵     2⁴     2³     2²     2¹     2⁰
    重み(10進): 128    64     32     16      8      4      2      1
    
    例: 'A' の ASCII コード 65
    
    ビット:        0      1      0      0      0      0      0      1
    重み:        128 +  64  +  32  +  16  +   8  +   4  +   2  +   1
    計算:          0 +  64  +   0  +   0  +   0  +   0  +   0  +   1 = 65
    
    
    MSB (Most Significant Bit) = ビット7 (最上位ビット)
    LSB (Least Significant Bit) = ビット0 (最下位ビット)
    

    2.2.3 バイトオーダー(エンディアン)

    複数バイトのデータを格納・送信する際、バイトの順序が問題になります:

    【エンディアン問題】
    
    32ビット整数 0x12345678 をメモリに格納:
    
    ビッグエンディアン(Big-Endian):
      アドレス:  0x00   0x01   0x02   0x03
      内容:      0x12   0x34   0x56   0x78
      (上位バイトが低いアドレスに)
      採用: ネットワーク、Motorola 68k、SPARC
    
    リトルエンディアン(Little-Endian):
      アドレス:  0x00   0x01   0x02   0x03
      内容:      0x78   0x56   0x34   0x12
      (下位バイトが低いアドレスに)
      採用: x86, x64, ARM (通常)
    
    語源: Jonathan Swift の『ガリヴァー旅行記』
          卵の割り方をめぐるリリパット国の争い
    

    Minitalkにおけるバイトオーダー:

    Minitalk では1バイトずつ送信するため、
    バイトオーダーは問題にならない。
    
    ただし、ビット送信順序は統一が必要:
      - MSB first: 人間が読みやすい(一般的)
      - LSB first: 一部のプロトコルで使用
    

    2.3 ASCII - 文字符号化の標準

    2.3.1 ASCII の歴史(1963年)

    ASCII(American Standard Code for Information Interchange)は、1963年にASA(American Standards Association、現ANSI)によって制定されました。

    ASCII 以前の問題:

    1960年代初頭の状況:
    - IBM: EBCDIC
    - Teletype: Baudot code
    - その他多数の独自コード
    
    問題: 異なるシステム間でテキストを共有できない!
    
    解決: 標準化された文字コードの制定 → ASCII
    

    ASCII の設計思想:

  • 7ビット: 128文字を定義(当時のテレタイプに合わせて)
  • 制御文字: 0-31(通信制御用)
  • 印刷可能文字: 32-126
  • 順序の合理性: 数字・アルファベットが連続
  • 2.3.2 ASCII コード表

    【ASCII コード表(抜粋)】
    
    Dec  Hex  Char    Dec  Hex  Char    Dec  Hex  Char
    ─────────────────────────────────────────────────
      0  00   NUL      32  20   SP       64  40   @
      1  01   SOH      33  21   !        65  41   A
      2  02   STX      34  22   "        66  42   B
      7  07   BEL      48  30   0        90  5A   Z
      8  08   BS       49  31   1        97  61   a
      9  09   HT       50  32   2        122 7A   z
     10  0A   LF       57  39   9        127 7F   DEL
     13  0D   CR
    
    重要な特徴:
    - '0'-'9' は連続(48-57): 数字 - '0' で数値に変換可能
    - 'A'-'Z' は連続(65-90): 大文字
    - 'a'-'z' は連続(97-122): 小文字
    - 大文字と小文字の差は32(ビット5が異なる)
    

    2.3.3 制御文字の意味

    ASCII の最初の32文字は「制御文字」です:

    【重要な制御文字】
    
    Dec  Name   意味                    Minitalkでの扱い
    ────────────────────────────────────────────────────
      0  NUL    Null(文字列終端)       メッセージ終了の検出
     10  LF     Line Feed(改行)        そのまま出力
     13  CR     Carriage Return         そのまま出力
     27  ESC    Escape                  エスケープシーケンス開始
    127  DEL    Delete                  通常の文字として扱う
    
    Minitalk では:
    - NUL (0x00) を受信 → メッセージ終了
    - その他の制御文字 → write() でそのまま出力
    

    2.4 ビット操作の代数

    2.4.1 ブール代数の基礎

    George Boole が1854年に確立した「ブール代数」は、ビット操作の数学的基盤です:

    【ブール代数の基本演算】
    
    NOT (否定):
      ¬0 = 1
      ¬1 = 0
    
    AND (論理積):
      0 ∧ 0 = 0
      0 ∧ 1 = 0
      1 ∧ 0 = 0
      1 ∧ 1 = 1
    
    OR (論理和):
      0 ∨ 0 = 0
      0 ∨ 1 = 1
      1 ∨ 0 = 1
      1 ∨ 1 = 1
    
    XOR (排他的論理和):
      0 ⊕ 0 = 0
      0 ⊕ 1 = 1
      1 ⊕ 0 = 1
      1 ⊕ 1 = 0
    
    【代数法則】
    結合律: (A ∧ B) ∧ C = A ∧ (B ∧ C)
    交換律: A ∧ B = B ∧ A
    分配律: A ∧ (B ∨ C) = (A ∧ B) ∨ (A ∧ C)
    ド・モルガンの法則: ¬(A ∧ B) = ¬A ∨ ¬B
                        ¬(A ∨ B) = ¬A ∧ ¬B
    

    2.4.2 C言語のビット演算子

    ブール代数は C言語では以下の演算子で表現されます:

    /*
     * C言語のビット演算子
     */
    
    /* NOT (ビット反転) */
    ~x
    
    /* AND */
    x & y
    
    /* OR */
    x | y
    
    /* XOR */
    x ^ y
    
    /* 左シフト */
    x << n   /* x を n ビット左へ */
    
    /* 右シフト */
    x >> n   /* x を n ビット右へ */
    
    
    /* 具体例 */
    unsigned char a = 0b11001010;  // 202
    unsigned char b = 0b10101100;  // 172
    
    ~a        = 0b00110101  // 53 (8ビットの場合)
    a & b     = 0b10001000  // 136
    a | b     = 0b11101110  // 238
    a ^ b     = 0b01100110  // 102
    a << 2    = 0b00101000  // 40 (上位2ビットは消失)
    a >> 2    = 0b00110010  // 50
    

    2.4.3 ビットマスクのテクニック

    特定のビットを操作するための基本パターン:

    /*
     * ビット操作の基本パターン
     */
    
    /* 1. 特定ビットの取得(読み取り) */
    int get_bit(unsigned char byte, int pos)
    {
        return (byte >> pos) & 1;
        /*
         * 例: byte = 0b01000001, pos = 6
         * byte >> 6 = 0b00000001
         * & 1       = 0b00000001 = 1
         */
    }
    
    /* 2. 特定ビットを1にセット */
    void set_bit(unsigned char *byte, int pos)
    {
        *byte |= (1 << pos);
        /*
         * 例: byte = 0b00000001, pos = 6
         * 1 << 6    = 0b01000000
         * |=        → 0b01000001
         */
    }
    
    /* 3. 特定ビットを0にクリア */
    void clear_bit(unsigned char *byte, int pos)
    {
        *byte &= ~(1 << pos);
        /*
         * 例: byte = 0b01000001, pos = 0
         * 1 << 0    = 0b00000001
         * ~         = 0b11111110
         * &=        → 0b01000000
         */
    }
    
    /* 4. 特定ビットを反転(トグル) */
    void toggle_bit(unsigned char *byte, int pos)
    {
        *byte ^= (1 << pos);
    }
    
    /* 5. 複数ビットの抽出 */
    unsigned char extract_bits(unsigned char byte, int start, int len)
    {
        unsigned char mask = (1 << len) - 1;
        return (byte >> start) & mask;
        /*
         * 例: byte = 0b11010110, start = 2, len = 3
         * mask = 0b00000111
         * byte >> 2 = 0b00110101
         * & mask    = 0b00000101 = 5
         */
    }
    

    2.5 ビットシリアライゼーション

    2.5.1 シリアライゼーションの概念

    「シリアライゼーション」(serialization)とは、データ構造を連続したバイト列に変換する処理です:

    【シリアライゼーションの一般概念】
    
    メモリ上のデータ構造    シリアライズ    バイト列
    ┌─────────────┐       ─────────→    ┌─────────────┐
    │ struct {    │                     │ byte 0      │
    │   int x;    │                     │ byte 1      │
    │   char y;   │                     │ byte 2      │
    │   ...       │                     │ ...         │
    │ }           │                     │ byte n      │
    └─────────────┘       ←─────────    └─────────────┘
                         デシリアライズ
    
    
    Minitalkでのビットシリアライゼーション:
    
    文字 'A'              ビット列           シグナル列
    ┌─────┐             ┌─────────┐        ┌────────────────┐
    │ 65  │ ──────→    │01000001 │ ───→  │U1 U2 U1 U1 U1  │
    └─────┘             └─────────┘        │U1 U1 U2       │
                                           └────────────────┘
    

    2.5.2 MSB First シリアライゼーション

    最上位ビットから順に送信する方法(Minitalkで一般的):

    /*
     * MSB (Most Significant Bit) First
     * 人間が読みやすい順序
     */
    
    void send_char_msb(pid_t pid, unsigned char c)
    {
        int bit_pos;
    
        bit_pos = 7;  /* 最上位ビットから開始 */
        while (bit_pos >= 0)
        {
            /* ビット位置を右シフトして最下位に持ってくる */
            if ((c >> bit_pos) & 1)
                kill(pid, SIGUSR2);  /* 1 */
            else
                kill(pid, SIGUSR1);  /* 0 */
    
            usleep(100);
            bit_pos--;
        }
    }
    
    /*
     * 送信例: 'A' = 65 = 0b01000001
     *
     * bit_pos=7: c >> 7 = 0b0, & 1 = 0 → SIGUSR1
     * bit_pos=6: c >> 6 = 0b01, & 1 = 1 → SIGUSR2
     * bit_pos=5: c >> 5 = 0b010, & 1 = 0 → SIGUSR1
     * bit_pos=4: c >> 4 = 0b0100, & 1 = 0 → SIGUSR1
     * bit_pos=3: c >> 3 = 0b01000, & 1 = 0 → SIGUSR1
     * bit_pos=2: c >> 2 = 0b010000, & 1 = 0 → SIGUSR1
     * bit_pos=1: c >> 1 = 0b0100000, & 1 = 0 → SIGUSR1
     * bit_pos=0: c >> 0 = 0b01000001, & 1 = 1 → SIGUSR2
     *
     * 送信順: U1, U2, U1, U1, U1, U1, U1, U2
     */
    

    2.5.3 MSB First デシリアライゼーション

    受信側でビットを再構成する方法:

    /*
     * MSB First 受信
     * 左シフトしながらビットを追加
     */
    
    static unsigned char g_char = 0;
    static int g_bit_count = 0;
    
    void receive_bit_msb(int signum)
    {
        /* 既存のビットを左にシフト(次のビットの場所を作る) */
        g_char <<= 1;
    
        /* 受信したビットを最下位に追加 */
        if (signum == SIGUSR2)
            g_char |= 1;
    
        g_bit_count++;
    
        if (g_bit_count == 8)
        {
            write(1, &g_char, 1);
            g_char = 0;
            g_bit_count = 0;
        }
    }
    
    /*
     * 受信例: 'A' の復元
     *
     * 初期: g_char = 0b00000000
     *
     * 受信1 (U1=0): 0b00000000 << 1 = 0b00000000, |= 0 → 0b00000000
     * 受信2 (U2=1): 0b00000000 << 1 = 0b00000000, |= 1 → 0b00000001
     * 受信3 (U1=0): 0b00000001 << 1 = 0b00000010, |= 0 → 0b00000010
     * 受信4 (U1=0): 0b00000010 << 1 = 0b00000100, |= 0 → 0b00000100
     * 受信5 (U1=0): 0b00000100 << 1 = 0b00001000, |= 0 → 0b00001000
     * 受信6 (U1=0): 0b00001000 << 1 = 0b00010000, |= 0 → 0b00010000
     * 受信7 (U1=0): 0b00010000 << 1 = 0b00100000, |= 0 → 0b00100000
     * 受信8 (U2=1): 0b00100000 << 1 = 0b01000000, |= 1 → 0b01000001
     *
     * 結果: 0b01000001 = 65 = 'A'
     */
    

    2.5.4 LSB First の場合

    比較のため、LSB First も説明します(一部のプロトコルで使用):

    /*
     * LSB (Least Significant Bit) First
     * 一部のハードウェアプロトコルで使用
     */
    
    /* 送信 */
    void send_char_lsb(pid_t pid, unsigned char c)
    {
        int bit_pos;
    
        bit_pos = 0;  /* 最下位ビットから開始 */
        while (bit_pos < 8)
        {
            if ((c >> bit_pos) & 1)
                kill(pid, SIGUSR2);
            else
                kill(pid, SIGUSR1);
    
            usleep(100);
            bit_pos++;
        }
    }
    
    /* 受信 */
    void receive_bit_lsb(int signum)
    {
        /* 受信したビットを現在の位置にセット */
        if (signum == SIGUSR2)
            g_char |= (1 << g_bit_count);
    
        g_bit_count++;
    
        if (g_bit_count == 8)
        {
            write(1, &g_char, 1);
            g_char = 0;
            g_bit_count = 0;
        }
    }
    
    /*
     * 注意: 送信側と受信側で同じ方式を使う必要がある!
     */
    

    2.6 文字列の送信プロトコル

    2.6.1 プロトコル設計の考慮事項

    文字列全体を送信するには、追加の考慮が必要です:

    【文字列送信の問題】
    
    問題1: 文字列の終端をどう知らせるか?
      → 方法A: 長さを先に送信
      → 方法B: 終端文字(NUL)を送信 ← Minitalkで使用
    
    問題2: エラー検出・訂正は必要か?
      → Minitalkでは通常不要(短距離、同一マシン)
      → ボーナスでチェックサム追加も可能
    
    問題3: 複数クライアントへの対応は?
      → 基本: 1クライアントのみ
      → ボーナス: 複数クライアント対応
    

    2.6.2 Minitalk の基本プロトコル

    【Minitalk 基本プロトコル】
    
    ┌───────────────────────────────────────────────────────────┐
    │                    メッセージフレーム                       │
    ├───────────────────────────────────────────────────────────┤
    │ [文字1] [文字2] [文字3] ... [文字n] [NUL]                  │
    │  8bit    8bit    8bit        8bit   8bit                 │
    └───────────────────────────────────────────────────────────┘
    
    各文字は8つのシグナルで構成:
      SIGUSR1 = ビット0
      SIGUSR2 = ビット1
    
    終端:
      NUL文字(0x00)を受信したらメッセージ終了
    
    
    例: "Hi" を送信
    
      'H' = 0x48 = 0b01001000 → U1 U2 U1 U1 U2 U1 U1 U1
      'i' = 0x69 = 0b01101001 → U1 U2 U2 U1 U2 U1 U1 U2
      NUL = 0x00 = 0b00000000 → U1 U1 U1 U1 U1 U1 U1 U1
    

    2.6.3 文字列送信の実装

    /*
     * 完全な文字列送信関数
     */
    
    void send_string(pid_t server_pid, const char *str)
    {
        while (*str)
        {
            send_char_msb(server_pid, (unsigned char)*str);
            str++;
        }
        /* 終端の NUL を送信 */
        send_char_msb(server_pid, '\0');
    }
    
    /*
     * クライアントのmain関数
     */
    int main(int argc, char **argv)
    {
        pid_t server_pid;
    
        if (argc != 3)
        {
            write(2, "Usage: ./client <server_pid> <message>\n", 39);
            return (1);
        }
    
        server_pid = ft_atoi(argv[1]);
        if (server_pid <= 0)
        {
            write(2, "Error: Invalid PID\n", 19);
            return (1);
        }
    
        send_string(server_pid, argv[2]);
        return (0);
    }
    

    2.7 SIGUSR1 と SIGUSR2 の選択理由

    2.7.1 ユーザー定義シグナルの意義

    なぜ Minitalk は SIGUSR1 と SIGUSR2 を使うのでしょうか?

    【シグナルの分類】
    
    1. システム予約シグナル(使用不可)
       SIGKILL (9)  - 強制終了(キャッチ不可)
       SIGSTOP (19) - 停止(キャッチ不可)
    
    2. イベント通知シグナル(OS が使用)
       SIGINT (2)   - Ctrl+C
       SIGTERM (15) - 終了要求
       SIGCHLD (17) - 子プロセス状態変化
    
    3. エラー通知シグナル(OS が使用)
       SIGSEGV (11) - セグメンテーション違反
       SIGFPE (8)   - 浮動小数点例外
    
    4. ユーザー定義シグナル(自由に使用可能)
       SIGUSR1 (10) - ユーザー定義1
       SIGUSR2 (12) - ユーザー定義2
    
    Minitalkで SIGUSR1/2 を使う理由:
      - OSと干渉しない
      - 他のプログラムと競合しにくい
      - 2つあるので2進数の0/1を表現できる
    

    2.7.2 シグナル番号の確認

    実際のシグナル番号はシステムによって異なる場合があります:

    #include <signal.h>
    #include <stdio.h>
    
    int main(void)
    {
        printf("SIGUSR1 = %d\n", SIGUSR1);
        printf("SIGUSR2 = %d\n", SIGUSR2);
        return (0);
    }
    
    /*
     * 典型的な出力:
     *
     * Linux x86_64:
     *   SIGUSR1 = 10
     *   SIGUSR2 = 12
     *
     * macOS:
     *   SIGUSR1 = 30
     *   SIGUSR2 = 31
     *
     * → マジックナンバーではなく、
     *   必ずシンボル(SIGUSR1/SIGUSR2)を使うこと!
     */
    

    2.7.3 シグナルの一意性保証

    SIGUSR1 と SIGUSR2 は「キューイングされない」という特性があります:

    【キューイングの問題】
    
    同じシグナルが連続で送られた場合:
    
    送信: SIGUSR1 → SIGUSR1 → SIGUSR1
             ↓
    受信:    SIGUSR1 (1回だけ)
    
    理由: 標準シグナルは「ビットマスク」で管理されるため、
          同じシグナルは1つしか保留できない
    
    
    対策:
      1. 十分な待機時間(usleep)を設ける
      2. ACKメカニズムで確実に受信を確認(ボーナス)
    

    2.8 信頼性と同期

    2.8.1 シグナルロスの数学的分析

    シグナルロスの確率を分析してみましょう:

    【シグナルロスのモデル】
    
    仮定:
      - シグナル処理時間: T_h マイクロ秒
      - シグナル送信間隔: T_s マイクロ秒
    
    シグナルロスの条件:
      T_s < T_h の場合、ロストの可能性あり
    
    例:
      T_h = 50 μs(ハンドラ処理時間)
      T_s = 30 μs(送信間隔)
      → シグナルロストの可能性が高い
    
    安全な設定:
      T_s > T_h + マージン
      例: T_s = 100 μs(T_h の2倍以上)
    

    2.8.2 適切な待機時間の決定

    /*
     * 待機時間の経験則
     */
    
    /* 短すぎる(危険) */
    #define DELAY_DANGEROUS  10   /* μs - ロスト頻発 */
    
    /* 最小限(リスクあり) */
    #define DELAY_MINIMUM    50   /* μs - 負荷が高いとロスト */
    
    /* 推奨(安全) */
    #define DELAY_SAFE      100   /* μs - 通常はこれで十分 */
    
    /* 確実(低速) */
    #define DELAY_RELIABLE  500   /* μs - ほぼ確実だが遅い */
    
    /*
     * 送信速度の計算:
     *
     * DELAY = 100 μs の場合:
     *   1文字 = 8ビット × 100μs = 800μs = 0.8ms
     *   1秒で送れる文字数 = 1,000,000 / 800 = 1,250 文字
     *   → 約1.2KB/秒
     *
     * 参考: 現代のネットワークは GB/秒のオーダー
     *       シグナル通信は極めて低速
     */
    

    2.8.3 ACKメカニズム(ボーナス用)

    より確実な通信のための確認応答メカニズム:

    /*
     * ACK (Acknowledgment) プロトコル
     *
     * 動作:
     *   1. クライアント: ビットを送信
     *   2. サーバー: 受信したらACKを返信
     *   3. クライアント: ACKを受け取ったら次のビットを送信
     */
    
    /* クライアント側 */
    volatile sig_atomic_t g_ack = 0;
    
    void ack_handler(int sig)
    {
        (void)sig;
        g_ack = 1;
    }
    
    int send_bit_reliable(pid_t pid, int bit)
    {
        int timeout;
    
        g_ack = 0;
    
        if (bit)
            kill(pid, SIGUSR2);
        else
            kill(pid, SIGUSR1);
    
        /* ACKを待つ(タイムアウト付き) */
        timeout = 1000;  /* 最大1000回 × 10μs = 10ms */
        while (!g_ack && timeout > 0)
        {
            usleep(10);
            timeout--;
        }
    
        return (g_ack);
    }
    
    /* サーバー側 */
    void handler_with_ack(int sig, siginfo_t *info, void *ctx)
    {
        (void)ctx;
    
        /* ビット処理 */
        g_char <<= 1;
        if (sig == SIGUSR2)
            g_char |= 1;
        g_bit_count++;
    
        /* ACKを送信 */
        kill(info->si_pid, SIGUSR1);
    
        if (g_bit_count == 8)
        {
            write(1, &g_char, 1);
            g_char = 0;
            g_bit_count = 0;
        }
    }
    

    2.9 まとめ

    この章では、Minitalkの通信基盤となる理論を学びました:

    学んだこと

  • Claude Shannon の情報理論(1948年)
- 情報はビットで測定できる - 2つのシグナルで1ビットを伝達

  • 位取り記数法とビット
- 2進法の数学的基礎 - MSB と LSB の概念

  • ASCII 文字コード(1963年)
- 7ビットで128文字を定義 - 制御文字と印刷可能文字

  • ブール代数とビット操作
- AND, OR, XOR, NOT - シフト演算とマスク

  • シリアライゼーション
- データを順次送信する方法 - MSB First vs LSB First

  • 信頼性と同期
- シグナルロスの問題 - 待機時間と ACK

次章の予告

第3章では、シグナルハンドラと sigaction() について学びます:

  • sigaction() の詳細な使い方
  • SA_SIGINFO フラグと siginfo_t 構造体
  • シグナルマスクの活用
  • 堅牢なサーバー実装

---

参考文献:

  • Shannon, C. E. (1948). "A Mathematical Theory of Communication". Bell System Technical Journal, 27(3), 379-423.
  • Boole, G. (1854). An Investigation of the Laws of Thought. Walton and Maberly.
  • American National Standards Institute. (1963). ASCII - American Standard Code for Information Interchange. ANSI X3.4.
  • Kernighan, B. W., & Ritchie, D. M. (1988). The C Programming Language (2nd ed.). Prentice Hall.