[英]Call a userspace function from within a Linux kernel module
我正在編寫一個簡單的 Linux 字符設備驅動程序,以通過 I/O 端口將數據輸出到一個硬件。 我有一個函數可以執行浮點運算來計算硬件的正確輸出; 不幸的是,這意味着我需要將此函數保留在用戶空間中,因為 Linux 內核不能很好地處理浮點運算。
這是設置的偽表示(請注意,此代碼不執行任何特定操作,它僅顯示我的代碼的相對布局):
用戶空間功能:
char calculate_output(char x){
double y = 2.5*x;
double z = sqrt(y);
char output = 0xA3;
if(z > 35.67){
output = 0xC0;
}
return output;
}
內核空間代碼:
unsigned i;
for(i = 0; i < 300; i++){
if(inb(INPUT_PORT) & NEED_DATA){
char seed = inb(SEED_PORT);
char output = calculate_output(seed);
outb(output, OUTPUT_PORT);
}
/* do some random stuff here */
}
我想過使用ioctl
從用戶空間函數傳入數據,但我不確定如何處理函數調用處於循環中並且在下一次調用calculate_output
之前執行更多代碼的事實。
我設想這項工作的方式是:
ioctl
)ioctl
?),然后再次阻塞那么我如何在內核空間和用戶空間之間進行通信,並且還有阻塞,這樣我就不會讓用戶空間不斷輪詢設備文件以查看它是否需要發送數據?
一個警告:雖然定點算法在我的示例代碼中工作得很好,但在實際代碼中它不是一個選項; 我需要浮點提供的大范圍 - 即使沒有 - 我擔心重寫代碼以使用定點算法會混淆未來維護者的算法。
我認為最簡單的解決方案是在您的內核驅動程序中創建一個字符設備,並為虛擬文件使用您自己的文件操作。 然后用戶空間可以打開這個設備O_RDWR
。 您必須實現兩個主要的文件操作:
read
——這是內核如何將數據傳回用戶空間。 此函數在調用read()
系統調用的用戶空間線程的上下文中運行,在您的情況下,它應該阻塞,直到內核有另一個需要知道輸出的種子值。
write
——這是用戶空間將數據傳遞到內核的方式。 在您的情況下,內核只會獲取對先前讀取的響應並將其傳遞給硬件。
然后你在用戶空間得到一個簡單的循環:
while (1) {
read(fd, buf, sizeof buf);
calculate_output(buf, output);
write(fd, output, sizeof output);
}
內核中根本沒有循環——一切都在驅動事物的用戶空間進程的上下文中運行,內核驅動程序只負責將數據移入/移出硬件。
根據您在內核方面“在這里做一些隨機的事情”是什么,可能不可能這么簡單地做到這一點。 如果您確實需要內核循環,那么您需要創建一個內核線程來運行該循環,然后在input_data
、 input_ready
、 output_data
和output_ready
行中output_data
一些變量,以及幾個等待隊列和您需要的任何鎖定。
當內核線程讀取數據時,您將數據放入input_ready
並設置input_ready
標志並向輸入等待隊列發出信號,然后執行wait_event(<output_ready is set>)
。 read
文件操作會執行一個wait_event(<input_ready is set>)
並在數據准備好時將數據返回給用戶空間。 類似地, write
文件操作會將它從用戶空間獲取的數據放入output_data
並設置output_ready
並向輸出等待output_ready
發出信號。
另一種(更丑陋,不太便攜)的方法是使用ioperm
、 iopl
或/dev/port
東西來完全在用戶空間中完成所有操作,包括低級硬件訪問。
我建議您將執行所有“繁重工作”的代碼移至用戶模式 - 即一次性計算所有 300 個值,並將它們傳遞給內核。
我什至不確定您是否可以讓任意一段代碼從內核調用用戶模式。 我確信這是可能的,因為這就是例如“信號”所做的,但我遠不相信你可以“以你喜歡的任何方式”做到這一點(幾乎可以肯定,有一些限制,例如,什么你可以在那個函數中做)。 這當然看起來不是一個好主意,而且多次回調到用戶模式肯定會很慢。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.