中斷

中斷是指裝置在需要服務時產生的非同步事件。 例如,當序列埠有可用資料時,就會產生中斷情形。 或是乙太網路封包送達 「中斷」可讓驅動程式庫在事件發生時立刻得知消息 但無法讓驅動程式庫花時間輪詢 (主動等待)。

使用中斷的驅動程式庫一般架構是 中斷處理執行緒 (IHT) 會在驅動程式庫啟動 / 繫結期間建立 作業。 此執行緒會等待中斷事件,並在中斷時執行 也就是採取的策略

以序列埠驅動程式庫為例。 系統可能會因為以下任一事件而中斷服務:

  • 一或多個字元已送達
  • 就可以傳送一或多個字元
  • 控制線 (例如 DTR) 已變更狀態。

幹擾值會喚醒 IHT。 IHT 會判斷事件的原因,通常是透過讀取某些狀態暫存器。 然後,它會執行適當的服務函式來處理事件。 完成後,IHT 就會回到休眠狀態,等待下一個中斷情形。

例如,假設角色抵達,IHT 會喚醒,並讀取狀態暫存器 會指出「有可用的資料」接著呼叫會排除所有可用的 從序列埠 FIFO 傳入驅動程式庫的緩衝區。

不需要核心層級程式碼

您可能熟悉其他採用「中斷」功能的作業系統 服務日常安排 (ISR)。 這些是在特權模式下執行的核心層級處理常式,以及使用 中斷控制器硬體

在 Fuchsia 中,核心會處理中斷中具有特殊權限的部分 ,並提供執行緒層級的函式供驅動程式庫使用。

差別在於 IHT 是在執行緒層級執行,ISR 則是 並存在於核心層級相當有限 (有時可能很脆弱) 主要優點是當 IHT 當機時,只會擷取 而失敗的 ISR 可能會佔用整個作業系統。

附加到中斷時

目前,唯一提供幹擾的公車是 PCI 匯流排。 支援兩種類型:舊版和郵件信號中斷 (MSI)。

因此,為了在 PCI 上使用中斷式:

  1. 判斷您的裝置支援的裝置類型 (舊版或 MSI),
  2. 將中斷模式設為相符
  3. 取得裝置的中斷向量 (通常是一個,但也可能有多個)
  4. 啟動 IHT 背景執行緒
  5. 安排 IHT 執行緒等待中斷 (在步驟 3 的控制代碼上)。

步驟 12 是由 `Pci::ConfigureInterruptMode 輔助函式處理:

// Configure interrupt mode.
uint32_t irq_cnt = 1;
fuchsia_hardware_pci::InterruptMode mode;
zx_status_t status = pci.ConfigureInterruptMode(irq_cnt, &mode);
if (status != ZX_OK) {
  // handle error
}

Pci::ConfigureInterruptMode() 需要兩個引數:

#include <lib/device-protocol/pci.h>

zx_status_t Pci::ConfigureInterruptMode(uint32_t requested_irq_count,
                                        fpci::InterruptMode* out_mode);

第一個引數 requested_irq_count 是您想要的中斷次數。

第二個引數是裝置支援的中斷模式的退出參數。

設定中斷式支援後,您可以呼叫 Pci::MapInterrupt() 建立所選中斷情形的控制代碼。請注意, Pci::MapInterrupt() 具有下列原型:

#include <lib/device-protocol/pci.h>

zx_status_t Pci::MapInterrupt(uint32_t which_irq, zx::interrupt* out_interrupt);

第一個引數:which_irq 表示您需要的裝置相關的中斷號碼,而第二個引數 指向已建立的中斷物件的指標。

您現在有一個中斷控點。

請注意,大多數裝置只有一個幹擾,因此只需傳遞 which_irq 的「0」屬於正常值。 如果裝置造成多次中斷,常見的做法是 for 迴圈中的 pci::MapInterrupt() 函式 以及將控點繫結至每次中斷情形

正在等待中斷

在 IHT 中,您可以呼叫 zx::interrupt::wait() 等待中斷情形 適用的原型如下:

zx_status_t zx::interrupt::wait(zx::time* out_timestamp);

第一個參數可以是 nullptr (一般),也可以是時間的指標 戳記,指出中斷情形的觸發時間 (以奈秒表示 相對於使用擷取的單調時針來源 zx_clock_get_monotonic())。

因此,典型的 IHT 形狀如下:

int MyDevice::IrqThread() {
    for (;;) {
        zx_status_t status = dev->irq_.wait(nullptr);
        // do stuff
    }
}

裝置類別中有成員 (這裡 irq_) 是您取得的物件 Pci::MapInterrupt()

邊緣與等級中斷模式

中斷硬體可在兩種模式中運作;「邊緣」或「level」

在邊緣模式下,在主動式邊緣 (當硬體故障時) 信號會從閒置狀態轉為啟用中) 且可做為一次性測試。 也就是說,信號必須改回停用狀態,系統才能再次辨識該信號。

在層級模式下,當硬體訊號處於 啟用狀態

一般而言,在中斷的情況下,系統會使用邊緣模式,而水平模式為 由多部裝置共用中斷時使用 (因為因為需要 直到所有裝置移除要求線為止)。

Zircon 核心可視情況自動遮蓋及解除乾擾狀態。 對於層級觸發的硬體中斷 zx::interrupt::wait() 在傳回之前將中斷情況遮蓋,並在下次呼叫時將其解除遮罩。 針對邊緣觸發的中斷,中斷情形會保持解除遮罩。

IHT 不應執行任何長時間執行的工作。 對於執行長時間工作的驅動程式,請使用背景工作執行緒。

關閉使用中斷的驅動程式庫

若要徹底關閉使用中斷的驅動程式庫,您可以使用 zx::interrupt::destroy() 取消 zx::interrupt::wait() 呼叫。

概念是,當前景執行緒判定驅動程式庫應該要 關閉,只會刪除中斷控點,導致 IHT 關閉:

void MyDevice::DdkRelease() {
  // destroy the handle, this will cause zx_interrupt_wait() to pop
  irq_.destroy();
  irq_thread_.join();
  ...
}

int MyDevice::IrqThread() {
    ...
    for(;;) {
        zx_status_t status = irq_.wait(nullptr);
        if (status == ZX_ERR_CANCELED) {
            // we are being shut down, do any cleanups required
            ...
            break;
        }
        ...
    }
}

主執行緒在要求關閉時,會刪除中斷控點。 這會導致 IHT 的 zx::interrupt::wait() 呼叫來喚醒應用程式。 IHT 會查看錯誤代碼 (在本例中為 ZX_ERR_CANCELED),然後 最終決定結束 與此同時,主要執行緒正在等待加入 IHT 通話 thread::join()。 IHT 離開後,thread::join() 傳回,以及 執行緒可以完成處理程序

進階閱讀器已受邀查看其他中斷情形 可用的函式: