**Linuxにおけるハードウェア・ホットプラグイベント ― 詳細解説**

2026/03/02 18:26

**Linuxにおけるハードウェア・ホットプラグイベント ― 詳細解説**

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

概要:
Libusb の Linux ホットプラグシステムは、

linux_netlink.c
linux_udev.c
という 2 つのバックエンドに依存しています。デフォルトでは
--with-udev=yes
が設定されており、udev を無効にするとプレーンな netlink バックエンドが使用されます。
カーネルデバイスイベントは Netlink プロトコル 15(
NETLINK_KOBJECT_UEVENT
)を介して到達し、ヌル終端文字列として
add@/devices/...
のようなアクション行から始まり、
ACTION=add
SUBSYSTEM=usb
などのキー/バリュー ペアが続きます。udev はこれらのメッセージを受信し解析して、カスタムパケット形式でマルチキャストグループ 2(
MONITOR_GROUP_UDEV
)に再送信します。
udev パケットは
"libudev"
というマジック文字列から始まり、ビッグエンディアンのバージョンワード
0xfeedcafe
を持ち、次にネイティブエンディアンで格納された複数フィールド(
header_sz
properties_off
properties_len
subsystem_hash
devtype_hash
tag_bloom_hi
tag_bloom_lo
)が続きます。ハッシュは
SUBSYSTEM=
DEVTYPE=
の値に対して MurmurHash2 を用いて計算され、2 つの Bloom フィルタワードは
TAGS=
キーから導出されたビットをエンコードします。その後パケットには元のキー/バリュー文字列と、
SO_PASSCRED
経由で送られる Unix 認証情報(pid/uid/gid)が含まれます。カーネルメッセージはゼロ認証情報を持つため、libudev は有効な認証情報がないパケットを拒否します。
プロトコルバージョンは固定で
0xfeedcafe
となっており、後方互換性や前方互換性に関する保証は文書化されていません。そのため、パケットレイアウト、フィルタリングロジック、または認証情報処理の変更は libusb と udev の両方で協調して更新を行う必要があり、ホットプラグイベントに依存するアプリケーションのデバイス検出、安定性、セキュリティに影響を与える可能性があります。

本文

TL;DR – “ここへ行く”


なぜ libusb でホットプラグサポートが必要なのか?

Linux 上では、USB デバイスの挿入・取り外しを検知するために libusb が 2 つのバックエンドを提供しています。

バックエンド使用しているもの
udev
libudev
+ systemd(現在はデフォルト)
netlinkカーネルの raw netlink ソケット (
NETLINK_KOBJECT_UEVENT
)

ホットプラグサポートを追加した最初のコミットでは、選択理由が説明されています。

「Linux バックエンドにホットプラグサポートを追加します。
Linux 用には udev と netlink の 2 通りの構成方法があります。
udev を利用しているシステムでは udev サポートを使用することが強く推奨されます。
この推奨に従い、configure 時に

--with-udev=yes
がデフォルトになります。
netlink サポートを有効にしたい場合は
--with-udev=no
を指定してください。
udev サポートが有効になっていると、すべてのデバイス列挙は udev によって行われます。」

libusb が udev を好む理由は、競合状態を回避できる点にあります。udev はデバイスイベントを処理する際に権限変更やファームウェアアップロード、モードスイッチングなどを実施します。カーネルに直接リッスンすると、これらの変更が見逃されてしまう可能性があります。


1. Netlink – 基本的な IPC

Netlink は Linux 固有の「ネットワークプロトコル」であり、カーネルとユーザースペース(および

NETLINK_KOBJECT_UEVENT
を使えばユーザースペース同士)間で通信するために使用されます。UDP に似た挙動をし、BSD ソケット上でデータグラムの送受信が可能です。

主な特徴:

  • マルチキャスト – 複数のプログラムが同じグループをリッスンできます。
  • 付随データ – 例:Unix 認証情報 (
    SO_PASSCRED
    )。
  • プロトコル ID
    NETLINK_KOBJECT_UEVENT
    (値 15)。

2. サンプルプログラム

以下は、カーネルまたは udev のイベントをリッスンするための簡潔な自己完結型サンプルです。
バッファ拡張や認証チェックは省略しています。

/* コンパイル例: gcc -Wall -O2 -o netlink_example netlink_example.c */
#define _GNU_SOURCE
#include <ctype.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <arpa/inet.h>
#include <sys/socket.h>

/* Netlink 定数 – Linux ヘッダは不要 */
#define NETLINK_KOBJECT_UEVENT 15
#define MONITOR_GROUP_KERNEL   1
#define MONITOR_GROUP_UDEV     2

/* sockaddr_nl の最小定義 */
struct sockaddr_nl {
    sa_family_t nl_family;
    unsigned short nl_pad;
    uint32_t nl_pid;
    uint32_t nl_groups;
};

/* ---------- ヘルパー関数 --------------------------------------- */

void print_kern_uevent_pkt(void *buf, size_t bufsz) {
    while (bufsz) {
        int this_sz = printf("%s\n", (char *)buf);
        buf += this_sz;
        bufsz -= this_sz;
    }
}

struct udev_packet_header {
    char libudev_magic[8];
    uint32_t magic;          /* 0xfeedcafe, ビッグエンディアン */
    uint32_t header_sz;      /* ネイティブエンディアン */
    uint32_t properties_off; /* ネイティブエンディアン */
    uint32_t properties_len; /* ネイティブエンディアン */
    uint32_t subsystem_hash;
    uint32_t devtype_hash;
    uint32_t tag_bloom_hi;
    uint32_t tag_bloom_lo;
};

void print_udev_pkt(void *buf, size_t bufsz) {
    struct udev_packet_header hdr;
    if (bufsz < sizeof(hdr)) { printf("Invalid packet!\n"); return; }
    memcpy(&hdr, buf, sizeof(hdr));

    /* ビッグエンディアンフィールドを変換 */
    hdr.magic        = ntohl(hdr.magic);
    hdr.subsystem_hash = ntohl(hdr.subsystem_hash);
    hdr.devtype_hash   = ntohl(hdr.devtype_hash);
    hdr.tag_bloom_hi   = ntohl(hdr.tag_bloom_hi);
    hdr.tag_bloom_lo   = ntohl(hdr.tag_bloom_lo);

    if (memcmp(hdr.libudev_magic, "libudev", 8) ||
        hdr.magic != 0xfeedcafe) {
        printf("Invalid packet magic!\n"); return;
    }

    /* ペイロード文字列を表示 */
    print_kern_uevent_pkt((char *)buf + hdr.properties_off,
                          hdr.properties_len);
}

/* --------------------------------------------------------------------- */

int main(int argc, char **argv) {
    if (argc < 2) { printf("Usage: %s kernel|udev\n", argv[0]); return 1; }
    bool udev_mode = !strcmp(argv[1], "udev");

    /* Netlink ソケットをオープン */
    int nlsock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC,
                        NETLINK_KOBJECT_UEVENT);
    if (nlsock == -1) { perror("socket"); return 1; }

    /* 必要なマルチキャストグループにバインド */
    struct sockaddr_nl sa_nl;
    memset(&sa_nl, 0, sizeof(sa_nl));
    sa_nl.nl_family = AF_NETLINK;
    sa_nl.nl_groups = udev_mode ? MONITOR_GROUP_UDEV
                                 : MONITOR_GROUP_KERNEL;
    if (bind(nlsock, (struct sockaddr *)&sa_nl, sizeof(sa_nl)) == -1) {
        perror("bind"); return 1;
    }

    /* 受信ループ */
    char *buf = NULL; size_t buf_sz = 0;
    while (true) {
        ssize_t pkt_sz = recv(nlsock, NULL, 0,
                             MSG_PEEK | MSG_TRUNC);
        if (pkt_sz == -1) { perror("peek"); return 1; }

        if ((size_t)pkt_sz > buf_sz) {
            buf = realloc(buf, pkt_sz); buf_sz = pkt_sz;
        }

        /* パケットを読み込む */
        struct iovec iov = {.iov_base = buf, .iov_len = pkt_sz};
        struct msghdr msg = {.msg_iov = &iov, .msg_iovlen = 1};
        if (recvmsg(nlsock, &msg, 0) != pkt_sz) { perror("recv"); return 1; }

        /* ディスパッチ */
        printf("\n--- %s event ---\n", udev_mode ? "udev" : "kernel");
        if (udev_mode)
            print_udev_pkt(buf, pkt_sz);
        else
            print_kern_uevent_pkt(buf, pkt_sz);
    }
}
  • カーネルイベント – マルチキャストグループ

    1

    ペイロードは NUL 終端文字列の連続 (
    ACTION=add
    ,
    SUBSYSTEM=usb
    など)。

  • udev 再送信イベント – マルチキャストグループ

    2

    ペイロードは先頭に 8 バイト
    "libudev"
    のマジックが入り、バイナリヘッダと同じ文字列リストが続きます。


3. udev パケットの構造

00000000: 6c 69 62 75 64 65 76 00 fe ed ca fe 28 00 00 00
...
  • ヘッダ – 32 バイト(
    magic
    ,
    subsystem_hash
    等以外はリトルエンディアン)。
  • ペイロード – NUL 終端文字列で、カーネルパケットと同一。

ヘッダに含まれるフィールド:

フィールド意味
libudev_magic
"libudev"
+ NUL
magic
0xfeedcafe(ビッグエンディアン)
header_sz
ヘッダサイズ(ネイティブエンディアン)
properties_off
,
properties_len
文字列ブロックのオフセットと長さ
subsystem_hash
SUBSYSTEM=
の MurmurHash2 値
devtype_hash
DEVTYPE=
の MurmurHash2 値
tag_bloom_hi/lo
TAGS=
エントリの 64 ビット Bloom フィルタ

これらのハッシュにより、BPF プログラムはカーネルがプロセスを起動する前にメッセージを事前フィルタリングできます。


4. セキュリティ

  • 認証情報 – udev は
    SO_PASSCRED
    を介して PID, UID, GID を送信します。
    libudev はこれらが無いパケットを拒否し、カーネルパケットはすべてゼロになります。
  • 許可された送信者 – 通常は root(またはユーザー名前空間内のプロセス)だけが udev マルチキャストグループに送信できます。

5. 要点まとめ

条件推奨バックエンド
udev
を利用しているシステムで確実な USB ホットプラグ検知が必要
udev (
libudev
)
カーネルイベントを直接受け取りたい
AF_NETLINK
ソケット、プロトコル
NETLINK_KOBJECT_UEVENT
を使用し、グループ 1 にバインド
udev の再送信イベントを受信したいグループ 2 にバインドし、上記のバイナリヘッダを解析

以上が libusb が採用しているホットプラグバックエンドと、それに関わる IPC メカニズムの概要です。

同じ日のほかのニュース

一覧に戻る →

2026/03/06 1:04

多数の管理者アカウントが侵害された後、ウィキペディアは読み取り専用モードになった。

## Japanese Translation: **概要:** 本書は、2026年2月20日から3月5日にかけて Wiki サービスに影響を与えた一連の技術的インシデントを記録しています。 - **2月20日:** 19:28 UTC に問題が確認され、19:44 UTC に修正が適用されました。23:33 UTC に監視を再開しました。 - **2月25日:** 16:40 UTC に調査が開始され、17:24 UTC に解決しました。 - **2月26日:** 16:25 UTC に修正で問題が解消され、16:58 UTC に監視を開始しました。 - **3月3日:** 10:09 UTC にデータベースサーバーの問題が検知され、10:24 UTC に修正が適用されました。10:17 UTC に監視更新が行われました。 - **3月5日:** 16:11 UTC に問題が確認され、17:09 UTC に初期修正(読み書き復旧)が実施されました。さらに編集は17:36 UTC の追加修正まで無効のままでした。18:36 UTC に監視を継続し更新しました。 3月4日、2月1–2、2月27–28、2月22–24、または2月21日はインシデントが報告されていません。 インシデントは運用上の問題、性能低下、一部停止、大規模停止、およびメンテナンスカテゴリにわたります(ただし各イベントに対する具体的なカテゴリは割り当てられていません)。 すべての修正後、チームは安定性を確認するために継続的監視を実施し、完全回復を宣言しました。ユーザーは一時的な読み取り専用アクセスと編集制限を経験し、継続的な Wiki 利用が必要な企業や教育グループの協力に影響を与える可能性があります。新たな症状が出現した場合に備えて、引き続き観測が行われることが示唆されています。 *この拡張版がご要望に合致する場合は、元の概要を置き換えることができます。*

2026/03/06 2:44

**「ブランドの時代」**

## 日本語訳: **要約:** スイスの時計業界は、1970年代に起こったクォーツ危機によって軌道を変えました。この危機は日本企業の競争とフランス・米ドル為替レートの急騰が引き金となり、ユニット販売数は1970年代初頭から1980年代初頭にかけて約3分の2減少しました。その結果、多くのメーカーは破綻または買収を余儀なくされました。残存した数社は純粋な技術的精密さから**ブランド主導のラグジュアリー**へとシフトしました。 視覚的マイルストーンがこの転換を確固たるものにしました:パテック・フィリップの1968年「ゴールデン・エリプス」ケース、オーデム・ピゲの1972年ロイヤルオーク(ジェラルド・ゲンタ設計)、そして1976年のノーティラスはすべて技術的洗練よりも瞬時に認識できるデザインを強調しました。1984年にはパテックの広告代理店長レネ・ビッテルが「ホブナイル・カラトラバ」(3919)を提唱し、手巻き機構と独特な模様が投資銀行家の注目を集め、1987年までに売上を急増させました。 メカニカル時計は**高級アクセサリー**として再登場しました。大きさと視覚的インパクトが男性の「ユーピー」(若手社会人)に富を披露するために理想的だったためです。ブランド時代は現在、オーバーサイズで独特な形状のケース、人工的希少性、および二次市場(例:パテックの時計買い戻し)の積極的管理によって定義されます。 主要ブランドは階層化された製品ラインを割り当てる持株会社に統合されました。独立ブティックは、パテック・オーデム・ピゲ、ロレックスなどの数少ないフラッグシップハウスでのみ存続しています。業界がステータスシンボルに焦点を当てることで、メーカーによって管理される**資産バブルに似たビジネスモデル**が生まれました。 **教訓:** ブランド力は収益性を推進しますが、過度の依存はイノベーションを抑制するリスクがあります。次の「黄金時代」は、名声を売るだけでなく、本当に興味深い問題に取り組むことで生まれる可能性が高いです。

2026/03/04 5:14

**柑橘系統図の三元プロット**

## Japanese Translation: **メインメッセージ:** ほぼすべての栽培されたシトラス果実は、3つの祖先種—ミカン(*Citrus reticulata*)、パムーロ(*Citrus maxima*)、そしてシトロン(*Citrus medica*)のハイブリッドであり、現代の育種によりほとんどの品種が甘みが強くミカン成分が豊富なプロファイルへと推進されてきました。 **証拠:** 遺伝子解析では、元の3種が三角形プロットの頂点に配置され、それぞれの間の距離は果実のゲノム内で各種が占める割合を示します。人為的選択により遺伝子混合が増幅され、孤立した枝ではなく重なり合う再結合クラスターが生成されます。 **歴史的背景:** 3つの主要種は、5百万年以上前の気候変動イベントで分岐し、その結果、キムクワットやカッファイルームなどへの進化放散を引き起こしました。シトラスの家畜化はアジアで始まり、オーストロネシアン航路を経て地中海へ広がり、そこでさらに選択が行われて現在馴染み深い市場グループが形成されました。 **チャート特性:** 三角形プロットは年代順ではなく、苦味/甘味の特徴クラスター、歴史的偶然、および隠れた方向性を視覚的に示します。追加の原種(例:サムヤオ)が* Citrus × citrus* のようなハイブリッドを通じてゲノムプールに入り込み、単純な3角形モデルを複雑化しています。キーレモンなどのハイブリッド例は、厳密な3種関係を曖昧にします。 **将来展望:** プロット内の混雑した領域は隠れた方向性を示し、現在スーパーマーケットで支配的な品種がなぜ選ばれるかを説明しています。これは今後も甘みが強くミカン成分が豊富な果実への育種が継続されることを意味し、この傾向は人気のあるシトラスタイプの範囲を狭め、供給チェーンや品種開発戦略、そしてブリーダーに利用可能な遺伝的多様性に影響を与える可能性があります。 **参考文献:** Wu et al. (2018) *Nature* 554:311–316; Langgut (2017) *HortScience* 52:814–822; Velasco & Licciardello (2014) *Nat. Biotechnol.* 32:640–642; Curk et al. (2016) *Ann. Bot.*; Barkley et al. (2006) *Genome*; Ollitrault et al. (2012) *Ann. Bot.*; Curk et al. (2015) *Mol. Ecol.*

**Linuxにおけるハードウェア・ホットプラグイベント ― 詳細解説** | そっか~ニュース