Linux

將數據從核心模組保存到使用者空間

  • April 14, 2019

我一直在玩核心程式,想用一些自定義硬體創建這個簡單的數據採集介面。為了便攜性和可重用性,我在 Raspberry Pi 上完成了所有工作。

該項目具有挑戰性的部分是有一個高速 ADC(並行)連接到 GPIO,並有一個核心模組,該模組使用來自 ADC 的硬體中斷來獲取每個樣本並將其儲存在緩衝區中,然後可以通過 chardevice 訪問該緩衝區。

我目前的設置(有效)如下:

  • 我有一個通過 SPI 控制我的硬體的使用者空間 C 程序。如果我發送所需的命令,它會開始獲取模擬數據並將它們發送到 ADC。
  • 每當 ADC 完成轉換時,它會將相應的信號推送到 GPIO 上的“低”,並且我在核心模組(綁定到該 GPIO)內獲得中斷。ISR 收集其他 12 個 GPIO(它是一個 12 位 ADC)的值並將其放入緩衝區,然後通過 /dev/mydevice 訪問該緩衝區。
  • 我有另一個單獨的使用者空間程序,它執行一個永無止境的 while 循環,從 /dev/mydevice 讀取,然後寫入“out_data.dat”(使用者空間文件)。
  • 通過這種粗略的設置(2 個使用者空間程序和核心模組載入),我每秒可以將超過 130 000 個樣本寫入我的文件(不會失去任何內容)。

我現在想看看我能做到多快,有兩件事需要考慮:

  1. 我在“通常”方式之上概述的設置是如何完成這樣的事情的?我到處都讀到核心不建議直接文件 I/O,所以我沒有這樣做。當然,應該可以在 ISR 期間將其寫入某個“永久”位置。在我看來,這似乎是一個常見問題,試圖使用中斷將數據從某些硬體獲取到電腦中。
  2. 在不更改我上面的設置的情況下,有什麼方法可以禁用其他中斷以使其盡可能平滑?在數據採集過程中,我真的不需要任何東西,只需要某種方法來阻止它。任何其他中斷(無線、監視器刷新等)都可以禁用,因為數據採集僅執行幾分鐘。之後,一切都會恢復,可以執行要求更高的 python 程式碼來分析和視覺化數據(至少這是我的簡單看法)。

對於使用者空間數據採集程序,死循環有什麼問題?只要你使用poll系統呼叫,它應該是高效的:https ://stackoverflow.com/questions/30035776/how-to-add-poll-function-to-the-kernel-module-code/44645336#44645336 ?

永久數據儲存

我不確定最好的方法是什麼,為什麼不直接從使用者態的投票中寫入文件?我想您擔心的是,如果到達的數據過多,數據就會失去,是這樣嗎?

但我懷疑在這種情況下限制因素是核心與使用者空間的通信,而是永久儲存設備的緩慢,所以我認為在使用者空間進行操作不會有任何區別。無論如何,僅核心的解決方案在以下位置有一個引人注目的問題:https ://stackoverflow.com/questions/1184274/how-to-read-write-files-within-a-linux-kernel-module而我沒有不認為你會在這裡得到更好的解決方案。

禁用中斷

您確定它會有所作為,特別是考慮到瓶頸可能會出現嗎?我希望如果您的設備實際上產生大量中斷,那麼無論如何這些中斷都會支配任何其他中斷。是否值得冒險弄亂其他硬體的狀態?您的硬體設備的規格是否表明它可以在物理上提供比您目前擁有的更大的數據頻寬?

我自己不知道該怎麼做,但是如果您想要答案,最好的辦法是提出一個標題為“如何禁用 Linux 核心模組的所有中斷?”的單獨問題。LDD2 提到了cli()函式http://www.xml.com/ldd/chapter/book/ch09.html但它似乎已被棄用:https ://notes.shichao.io/lkd/ch7/#no-more- global-cli該文本然後建議local_irq_disablelocal_irq_save

我也會嘗試用你找到的任何方法來禁用中斷,看看它是否變得更有效率,然後再進一步尋找是否存在一個好的方法。

在模擬器上,快速:

static int myinit(void)
{
   pr_info("hello init\n");
   unsigned long flags;
   local_irq_save(flags);
   return 0;
}

失敗:

returned with disabled interrupts

顯然來自 v4.16 do_one_initcall,因此有專門的錯誤處理!

然後,我天真地嘗試從工作執行緒中執行此操作:

static int work_func(void *data)
{
   unsigned long flags;
   local_irq_save(flags);
   return 0;
}

static int myinit(void)
{
   kthread = kthread_create(work_func, NULL, "mykthread");
   wake_up_process(kthread);
   return 0;
}

但我仍然無法觀察到任何效果,因此中斷必須由其他東西啟用,可以從以下推斷:

watch -n 1 grep i8042 /proc/interrupts

它不斷更新 tty 或 muse / 鍵盤中斷。

與其他入口點(例如 fops)相同,或者如果我嘗試使用 raw asm("cli"). 我們將需要一些更有教育意義的方法。

引用自:https://unix.stackexchange.com/questions/443286