Linux 将设备驱动程序实现为一组内核模块

Linux 将设备驱动程序实现为一组内核模块,linux,architecture,kernel,kernel-module,Linux,Architecture,Kernel,Kernel Module,我有一个用户空间库,用于通过串行端口与设备通信。该库相当复杂,可以执行许多任务,包括将头放在有效负载上、分割消息、发送消息、处理响应、初始化设备等 我想把这个库转移到内核空间,并考虑将库分解成几个定义良好的内核模块,以便 我可以在将来为交换层敞开大门(例如,用SPI层交换USART层) 发展会更容易。如果它被分割,我可以将库一块一块地迁移到内核空间 以前有人做过这样的事吗?在内核模块之间通信的最佳方式(效率方面)是什么?有什么我应该避免的陷阱吗 最后,是否有一些很好的驱动程序示例是作为一组内

我有一个用户空间库,用于通过串行端口与设备通信。该库相当复杂,可以执行许多任务,包括将头放在有效负载上、分割消息、发送消息、处理响应、初始化设备等

我想把这个库转移到内核空间,并考虑将库分解成几个定义良好的内核模块,以便

  • 我可以在将来为交换层敞开大门(例如,用SPI层交换USART层)
  • 发展会更容易。如果它被分割,我可以将库一块一块地迁移到内核空间
以前有人做过这样的事吗?在内核模块之间通信的最佳方式(效率方面)是什么?有什么我应该避免的陷阱吗


最后,是否有一些很好的驱动程序示例是作为一组内核模块实现的?

不确定这是否有帮助,但ALSA由许多不同的模块组成,每个不同的声音芯片都有不同的模块,以及其他芯片所依赖的几个常见模块:

$ lsmod | grep snd
snd_hrtimer            12744  1 
snd_hda_codec_realtek   330769  1 
snd_hda_intel          33390  2 
snd_usb_audio         118064  0 
snd_hda_codec         104802  2 snd_hda_codec_realtek,snd_hda_intel
snd_pcm                96714  3 snd_hda_intel,snd_usb_audio,snd_hda_codec
snd_hwdep              13668  2 snd_usb_audio,snd_hda_codec
snd_usbmidi_lib        25371  1 snd_usb_audio
snd_seq_midi           13324  0 
snd_rawmidi            30547  2 snd_usbmidi_lib,snd_seq_midi
snd_seq_midi_event     14899  1 snd_seq_midi
snd_seq                61896  3 snd_seq_midi,snd_seq_midi_event
snd_timer              29991  3 snd_hrtimer,snd_pcm,snd_seq
snd_seq_device         14540  3 snd_seq_midi,snd_rawmidi,snd_seq
snd                    68266  16 snd_hda_codec_realtek,snd_hda_intel,snd_usb_audio,snd_hda_codec,snd_pcm,snd_hwdep,snd_usbmidi_lib,snd_rawmidi,snd_seq,snd_timer,snd_seq_device
soundcore              12680  1 snd
snd_page_alloc         18529  2 snd_hda_intel,snd_pcm

重构依赖关系图留给读者作为练习。

所有内核模块都在相同的地址空间中运行。要从一个模块调用另一个模块,需要导出一些符号:函数或全局变量。虽然在这种情况下使用全局变量可能是一种糟糕的风格。请注意,导出的符号将与所有其他内核符号位于相同的名称空间中。为了避免名称冲突,您应该为它们使用一些定义良好的前缀,通常是模块名称和下划线。因此,如果您的一个模块被称为
foo
,并且您想要导出函数
bar()
,您可以在
foo
模块中使用如下代码:

void foo_bar(const char *prm) {
  printk (KERN_INFO "foo_bar(%s) was called\n", prm);
}
EXPORT_SYMBOL(foo_bar);
在另一个模块中,比如buz,只需调用以下函数:

foo_bar("qux");
请注意,在内核模块中,您不能使用libc(或其他库)功能,许多常见的事情,如内存管理、文件I/O、网络等,可能会非常复杂。还要注意,内核模块调试不是一项简单的任务,许多错误将导致内核死机,而不是无害的分段错误


因此,在将一些工作库移动到内核空间之前,请三思。可能最好将库拆分为相同的“模块”,但将其(或其大部分)保留在用户空间中。

一般建议是避免将内容从用户空间移动到内核空间。如果您的用户空间解决方案有效,为什么要将其移动到内核空间??