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 驅動開發:select/poll 非阻塞 I/O 實現指南

編輯:Linux 教學

在字符設備驅動中實現 select/poll 支持,讓用戶空間應用能夠通過非阻塞方式監聽設備就緒事件。本文詳細介紹 fd_set 操作宏、select 系統調用接口,以及驅動中 file_operations.poll 的實現模式。

用戶空間:fd_set 與 select 接口

fd_set 本質是文件描述符位圖,標準操作宏:

  • FD_ZERO(&set):清零集合
  • FD_SET(fd, &set):添加 fd
  • FD_CLR(fd, &set):移除 fd
  • FD_ISSET(fd, &set):檢測 fd 是否在集合中

select 原型:

int select(int nfds, fd_set *readset, fd_set *writeset,
           fd_set *exceptset, struct timeval *timeout);
  • nfds:最大 fd 值 + 1
  • 三個 fd_set 分別監聽可讀、可寫、異常狀態
  • timeout:NULL 永久等待;tv_sec=tv_usec=0 非阻塞輪詢
  • 返回就緒 fd 總數,就緒 fd 保留在對應集合中

內核空間:驅動實現 poll 操作

在 file_operations 中註冊 poll 回調:

static struct file_operations fops = {
    .owner   = THIS_MODULE,
    .open    = my_open,
    .release = my_release,
    .poll    = my_poll,
    // ...
};

標準 poll 實現模板

static __poll_t my_poll(struct file *filp, poll_table *wait)
{
    struct my_device *dev = filp->private_data;
    __poll_t mask = 0;

    // 1. 將當前進程加入等待隊列
    poll_wait(filp, &dev->wq, wait);

    // 2. 檢查設備狀態,設置返回掩碼
    if (dev->data_ready)
        mask |= POLLIN | POLLRDNORM;   // 可讀
    if (dev->buffer_space)
        mask |= POLLOUT | POLLWRNORM;  // 可寫

    return mask;
}

關鍵點

  1. poll_wait:將進程掛載到設備等待隊列(wait_queue_head_t),不會立即睡眠。內核會在適當時機調用隊列喚醒函數
  2. 返回掩碼:POLLIN/POLLRDNORM(可讀)、POLLOUT/POLLWRNORM(可寫)、POLLERR(錯誤)、POLLHUP(掛起)、POLLPRI(高優先級數據)
  3. 喚醒機制:數據到達或緩衝區可寫時,調用 wake_up_interruptible(&dev->wq)

完整示例:等待數據就緒

static __poll_t my_poll(struct file *filp, poll_table *wait)
{
    struct my_dev *dev = filp->private_data;
    __poll_t mask = 0;

    poll_wait(filp, &dev->read_q, wait);
    poll_wait(filp, &dev->write_q, wait);

    if (dev->rp != dev->wp)          // 環形緩衝區有數據
        mask |= POLLIN | POLLRDNORM;
    if (space_available(dev))        // 有寫入空間
        mask |= POLLOUT | POLLWRNORM;

    return mask;
}

// 中斷處理或數據接收路徑
static void data_received(struct my_dev *dev)
{
    // ... 處理數據 ...
    wake_up_interruptible(&dev->read_q);  // 喚醒等待讀的進程
}

注意事項

  • timeout 語義差異:Linux 修改 timeout 返回剩餘時間(System V 風格),BSD 不修改。移植代碼需重置 timeout
  • 高併發場景考慮使用 epoll(用戶空間)或 fasync(信號驅動 I/O)
  • 驅動必須正確處理 poll_wait 的並發安全,通常配合自旋鎖保護等待隊列

總結

實現 poll 回調是字符設備驅動支持非阻塞 I/O 的標準方式。核心在於:poll_wait 註冊等待隊列,業務邏輯檢查條件返回事件掩碼,數據路徑調用 wake_up 喚醒。這讓 select/poll/epoll 能夠統一監聽設備就緒事件。

Copyright © Windows教程網 All Rights Reserved