一、模块编程问题
模块是在需要时载入和执行的代码,这同样意味着它可以在不需要时由操作系统卸载,不需要重新启动系统。
1.Linux中模块的工作方式
装载和缷载命令,lsmod(查看),insmod(装载),rmmod(缷载),modprobe(综合)。例:insmod xxx.ko;
模块中必须的两个基本函数:在Linux 2.4 内核中是函数init_module和cleanup_module;在Linux 2.6 的内核中是宏module_init(your_init_func) 和module_exit(your_exit_func).初始化函数的作用一般是分配资源、注册设备方法等,退出函数的作用一般是释放所申请的资源等。

2.模块的编程
两个基本模块是必须的;
Makefile的基本内容: obj-m += xxx.o;
编译命令: make -C /lib/modules/`uname -r`/build $SUBDIRS=PWD modules.
调试方法:
printk(<优先级> string),例如:printk(KERN_INFO "This is a test.");
查看printk输出的内容:1)配置系统日志syslog并查看相应的输出. 2)用swatch实时监视输出到系统日志中的内容。3)在console模式下(非X window的控制台),经配置,可以直接看到输出。
替换printk的方法,不细述。
参考资料:http://www.dirac.org/linux/writing/lkmpg/2.6/chinese/lkmpg_chs/#AEN871
单步调试工具:
-
gdb /usr/src/linux/vmlinux /proc/kcore
-
kdb
没用过。可参考http://www.xml.com/ldd/chapter/book/ch04.html#t5(Linux 设备驱动程序 第二版(英文),第三版已有在线版)。
二、网卡驱动程序
1.e1000网卡驱动程序模块执行顺序(不完全):
1.e1000_init_module
2.e1000_probe
3.e1000_sw_init
4.e1000_reset
5.e1000_open
6.e1000_setup_tx_resources
7.e1000_settup_rx_resoureces
8.e1000_up
9.e1000_set_multi
10.e1000_irq_disable
11.e1000_configure
12.e1000_setup_rctl
13.e1000_configure_rx
14.e1000_set_multi * 3
15.e1000_watchdog * 2
……
(收包的过程)
e1000_watchdog.
e1000_intr.
e1000_alooc_rx_buffers.
e1000_intr.
e1000_alooc_rx_buffers.
……
e1000_watchdog.
e1000_intr.
e1000_alooc_rx_buffers.
e1000_intr.
e1000_alooc_rx_buffers.
……
-
网卡收包的过程
大致的过程如下(这部分我还没搞得很清楚):
当网卡上有数据到来时,就会把数据放在网卡的I/O缓存中,并产生一个中断。网卡驱动程序截获这个中断,通过DMA模式将网卡缓存中的数据取到驱动程序所申请的内核空间中(复制),DMA通道是事先设定好的。E1000网卡驱动程序中是在e1000_alloc_rx_buffers这个函数中设定DMA相关参数的。DMA传输完成后通知网卡驱动程序,网卡驱动程序产生软中断通知上层协议软件处理。
参考资料:
http://www.xml.com/ldd/chapter/book/index.html
《Linux设备驱动程序第二版》或《Linux设备驱动程序第三版》(目前只有PDF版)
三、进程与模块间通信
以下参考:http://www.dirac.org/linux/writing/lkmpg/2.6/chinese/lkmpg_chs/
进程和模块间通信的方法有至少有两种:一是通过/dev文件系统来实现,另一种方法是通过/proc文件系统来实现。这两种方法都是通过缓冲区复制来实现的。
1.通过/dev文件系统的方法
首先创建一个用于操作/dev文件的file_operations结构体,指定结构体中要求的设备文件操作方法,文件驱动模块在注册设备文件的时候,同时注册该设备文件的操作方法,并将系统自动产生的主设备输出(printk)。然后使用mknod /dev/hello c <Major> 0命令创建一个节点,这样应用程序得到设备驱动程序的输出了。
2.通过/proc文件系统的方法
使用/proc文件系统的方法与此类似,不同的是/proc文件系统实际上是存在于内存中的,用于处理/proc文件系统的结构体是proc_dir_entry,而且模块无法得知该文件是被打开的或关闭的,所以不能使用宏 try_module_get和try_module_put来增加或减少模块使用计数。
在Example 6-1实现了过/proc文件系统进行模块和进程之间的内在共享和双向通信。这里所采用的方法是宏put_user和 get_user。
四、关于零拷贝技术
Linux 的使用的地址类型:

(来自《Linux设备驱动程序第二版》 第13章)
vmalloc返回的是kernel virtual address,kmalloc返回的是kernel logical address,用户进程使用malloc来分配内存。
零拷贝技术的目的是减少数据的复制,提高效率。实现的手段就是内存共享。内存共享有三个级别:应用程序(进程)之间;应用程序与内核;内核与设备缓存。
我们目前主要关心应用程序与内核之间的内存共享,如果可能的话还可以把从应用程序空间中映射到核心的内存再次映射到设备缓存,这样也许就可以实现真正的零拷贝。
文档http://www.scs.ch/~frey/linux/memorymap.html中详细解释了各种内存的映射关系,并给出一个示例驱动程序。该示例详细演示了进程与内核怎样共享内存空间,如何将用户空间的地址映射到物理地址,虽然该示例是针对Linux 2.2 到linux 2.4核心的,但是对于在Linux 2.6中编程仍然很有参考价值。
它的实现机制是这样的:
1)内核初始化时注册一个字符驱动设备,并分配一定的内存空间。同时注册的还有设备的相关操作函数,和内存映射实现函数。
2)在模块正确装载后,由grep mmapdrv /proc/devices得到主设备号<Major>,由mknod node c <Major> 0 创建设备节点。
3)(进程)用open函数打开设备文件,调用mmap函数进行地址空间映射。
4)映射成功后,用户进程可以直接使用mmap返回的地址空间。
五、进一步的工作
1、如何在网卡驱动程序和用户进程间进行内存映射
所需要的准备知识:网卡驱动程序的运行机制(收包部分);Linux 2.6中如何进行地址空间的映射;
2、如何进一步将设备I/O缓存映射到内核空间或用户空间中。