我正在開發的一個Linux內核驅動程序是在內核中使用網絡通信(sock_create()
,sock->ops->bind()
等)。模擬內核套接字編程中select()和poll()的效果
問題是會有多個套接字接收數據。所以我需要一些能夠在內核空間中模擬select()
或poll()
的東西。由於這些函數使用文件描述符,除非我使用系統調用來創建套接字,否則我不能使用系統調用,但由於我在內核中工作,這似乎沒有必要。
所以我想在我自己的處理程序(custom_sk_data_ready()
)中包裝默認sock->sk_data_ready
處理程序,這將解鎖信號量。然後,我可以編寫我自己的kernel_select()
函數來嘗試鎖定信號量並進行阻塞,直到它打開。這樣內核函數進入休眠狀態,直到信號被解鎖custom_sk_data_ready()
。一旦kernel_select()
獲得鎖定,它將解鎖並呼叫custom_sk_data_ready()
重新鎖定它。因此,唯一的附加初始化是在綁定套接字之前運行custom_sk_data_ready()
,以便第一次調用custom_select()
時不會錯誤觸發。
我看到一個可能的問題。如果發生多個接收,則多次調用custom_sk_data_ready()
將嘗試解鎖信號量。所以爲了不丟失多個呼叫並跟蹤正在使用的sock
,必須有一個表或指向正在使用的套接字的指針列表。並且custom_sk_data_ready()
將不得不在表/列表中標記它傳遞的套接字。
這種方法聽起來嗎?或者,在使用標準系統調用時,我是否應該爲用戶/內核空間問題而煩惱?
初步調查結果:
在sock
結構中的所有回調函數被調用在中斷上下文。這意味着他們無法入睡。爲了讓主內核線程在就緒套接字列表中休眠,使用互斥鎖,但custom_sk_data_ready()
必須像互斥鎖一樣在互斥鎖上重複(重複調用mutex_trylock()
)。這也意味着任何動態分配都必須使用GFP_ATOMIC
標誌。
其他可能性:
對於每一個開放式插槽,替換每個插座的sk_data_ready()
有一個自定義(custom_sk_data_ready()
)和創建工作(struct work_struct
)和工作隊列(struct workqueue_struct
)。一個普通的process_msg()
函數將用於每個工人。創建一個內核模塊級別的全局列表,其中每個列表元素都有一個指向套接字的指針幷包含worker結構。當數據在套接字上準備就緒時,custom_sk_data_ready()
將執行並使用相同的套接字找到匹配的列表元素,然後使用list元素的工作隊列和worker調用queue_work()
。然後將調用process_msg()
函數,並且可以通過struct work_struct *
參數(地址)的內容找到匹配的列表元素,或者使用container_of()
宏來獲取保存worker結構的列表結構的地址。
哪種技術最健全?
難道你不能有一個用戶空間幫助程序做'poll'嗎?使用'poll'或'select'複用輸入與調度程序有關(因爲暫停過程是空閒的,所以其他進程可以運行),所以我不會在內核中這樣做! –
@BasileStarynkevitch:這就是爲什麼我只是試圖模擬'poll()'和'select()'的睡眠阻塞。使用來自內核的這兩個系統調用是最後的手段。我懷疑在用戶空間助手中運行'poll()'和'select()'有問題。該助手必須能夠訪問文件描述符(這不是在'sock_create()'中完成的),並且可能有權訪問駐留在內核空間中的套接字。所以現在套接字的創建必須發生在用戶空間助手中,並且模塊必須根據用戶空間文件描述符找到套接字。現在它變得更加複雜。 – Joshua
你不應該在內核中做任何事情。 – mpe