Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Linux 系統程式設計:使用 Netlink Socket 監聽 U 盤熱插拔事件 (含完整 C 代碼範例)

編輯:Linux 教學

前言

傳統 hotplug 機制透過呼叫用戶空間程式、環境變數傳遞參數,效率較低。現代 Linux 核心 (2.6+) 採用 Netlink Socket (協議 NETLINK_KOBJECT_UEVENT) 實現核心空間與用戶空間非同步高效通訊。本文提供完整 C 語言範例,示範如何建立 Netlink 監聽器即時接收 U 盤插拔等 uevent 事件。

核心概念

  • Netlink Socket:核心與用戶空間雙向通訊機制,支援多播群組。
  • NETLINK_KOBJECT_UEVENT:專門傳遞 kobject 狀態變更事件 (裝置增減、綁定/解綁驅動等)。
  • 多播群組 1:預設 uevent 廣播群組,監聽者加入即可收到所有裝置事件。

完整監聽器原始碼 (uevent_listener.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include <errno.h>

#define UEVENT_BUFFER_SIZE 2048

static int init_hotplug_sock(void) {
    struct sockaddr_nl snl;
    const int buffersize = 16 * 1024 * 1024;  // 16MB 接收緩衝,防止事件丟失
    int retval;

    memset(&snl, 0, sizeof(struct sockaddr_nl));
    snl.nl_family = AF_NETLINK;
    snl.nl_pid = getpid();        // 使用進程 PID 作為端口 ID
    snl.nl_groups = 1;            // 加入多播群組 1 (uevent)

    int hotplug_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
    if (hotplug_sock == -1) {
        perror("error getting socket");
        return -1;
    }

    // 設定接收緩衝區大小 (需 CAP_NET_ADMIN 權限)
    if (setsockopt(hotplug_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize)) < 0) {
        perror("setsockopt SO_RCVBUFFORCE failed");
        // 非致命,繼續使用預設緩衝
    }

    retval = bind(hotplug_sock, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl));
    if (retval < 0) {
        perror("bind failed");
        close(hotplug_sock);
        return -1;
    }
    return hotplug_sock;
}

int main(int argc, char *argv[]) {
    int hotplug_sock = init_hotplug_sock();
    if (hotplug_sock == -1) return EXIT_FAILURE;

    printf("Listening for kernel uevents... (Ctrl+C to quit)\n");

    while (1) {
        char buf[UEVENT_BUFFER_SIZE * 2] = {0};
        ssize_t len = recv(hotplug_sock, &buf, sizeof(buf), 0);
        if (len > 0) {
            // 核心發送的 uevent 為多行 "KEY=VALUE" 格式,以雙 \0 結尾
            printf("--- Event (%zd bytes) ---\n%s\n", len, buf);
        } else if (len == -1) {
            perror("recv failed");
            break;
        }
    }

    close(hotplug_sock);
    return EXIT_SUCCESS;
}

編譯與執行

gcc -o uevent_listener uevent_listener.c
sudo ./uevent_listener

需 root 權限 (設定 SO_RCVBUFFORCE 及綁定 Netlink)。

輸出範例 (插入 U 盤)

--- Event (312 bytes) ---
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:1d.7/usb2/2-4/2-4:1.0/host6/target6:0:0/6:0:0:0/block/sdb
SUBSYSTEM=block
DEVNAME=sdb
DEVTYPE=disk
MAJOR=8
MINOR=16
SEQNUM=1234
...

--- Event (298 bytes) ---
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:1d.7/usb2/2-4/2-4:1.0/host6/target6:0:0/6:0:0:0/block/sdb/sdb1
SUBSYSTEM=block
DEVNAME=sdb1
DEVTYPE=partition
MAJOR=8
MINOR=17
SEQNUM=1235
...

關鍵字段解析

字段含義
ACTIONadd (插入/裝置出現)、remove (拔出/裝置消失)、change (屬性變更)
SUBSYSTEM子系統類別:block、usb、scsi、net 等
DEVNAME/dev/ 下的裝置節點名 (如 sdb, sdb1)
DEVPATHsysfs 中裝置路徑,唯一標識裝置層級關係
ID_FS_TYPE檔案系統類型 (如 vfat, ext4) - 需 udev 規則生成
ID_VENDOR, ID_MODELUSB 廠商/產品名稱

進階應用:解析事件並自動掛載

# 簡易解析邏輯 (Python 示範)
import subprocess, os

sock = init_netlink_socket()  # 同 C 邏輯
while True:
    data = sock.recv(65536).decode()
    env = dict(line.split('=',1) for line in data.split('\n') if '=' in line)
    if env.get('ACTION') == 'add' and env.get('SUBSYSTEM') == 'block' and env.get('DEVTYPE') == 'partition':
        dev = '/dev/' + env['DEVNAME']
        fstype = subprocess.check_output(['blkid', '-o', 'value', '-s', 'TYPE', dev]).decode().strip()
        if fstype:
            mp = '/media/usb_' + env['DEVNAME']
            os.makedirs(mp, exist_ok=True)
            subprocess.call(['mount', '-t', fstype, dev, mp])
            print(f"Mounted {dev} ({fstype}) to {mp}")

與 udev 整合 (生產環境建議)

手寫 Netlink 監聽器適合嵌入式/無 udev 環境或特殊需求。標準桌面/伺服器系統建議:

  1. 編寫 /etc/udev/rules.d/99-usb-automount.rules 規則匹配裝置屬性。
  2. 調用 systemd-mount 或自訂腳本處理掛載/卸載。
  3. udev 內部即基於 Netlink 接收 uevent,並提供持久裝置命名、權限管理、資料庫查詢等完整功能。

結語

Netlink Socket 提供了核心事件直達用戶空間的高效通道。掌握 NETLINK_KOBJECT_UEVENT 監聽與解析技術,能開發出輕量級的裝置監控工具、自動化備份觸發器或嵌入式系統熱插拔管理邏輯,深入理解 Linux 裝置模型運作機制。

Copyright © Windows教程網 All Rights Reserved