一、kmalloc()、kzalloc()和vmalloc()
这三个函数都可以分配连续的虚拟内存
除此之外,这三个函数的区别有:
1. kmalloc()和kzalloc()函数分配的物理内存也是连续的,而vmalloc()分配的物理内存不一定连续
2. kmalloc()和kzalloc()函数分配的大小需要小于128K,而vmalloc()分配的大小没有限制
3. kmalloc()和kzalloc()分配内存的过程可以是原子操作(使用GFP_ATOMIC),而vmalloc()分配内存时则可能产生阻塞。因此vmalloc()不能从中断上下文调用
4. 在分配DMA内存时(使用GFP_DMA)需要保证物理内存连续,需要使用kmalloc()和kzalloc()
5. kmalloc()和kzalloc()分配内存开销小,因此比vmalloc()要快。内核较多使用的也是kmalloc()和kzalloc()
6. kmalloc()和kzalloc()的差别类似于malloc()和calloc(),kmalloc()只分配,不清0;kzalloc()会进行清0
7. kmalloc()和kzalloc()使用kfree()释放,vmalloc()使用vfree()释放
二、device_create()的调用和设备节点的创建过程
device_create()
-> device_create_vargs -> kzalloc struct device -> device_register() -> device_initialize() -> device_add()
-> kobject_uevent(&dev->kobj, KOBJ_ADD);
device的注册过程,最终只是调用kobject_uevent来发送一个action,对于uevent分析,读者可以参考:二十三、uevnet机制和U盘自动挂载
那么是谁创建/dev下的设备节点呢?
通常是使用应用程序udev来创建,在嵌入式系统中我们使用busybox的mdev
我们可以在命令行中输入mdev命令查看它的描述信息
我们在制作文件系统过程中(TINY4412移植第五节)使用到了mdev -s。它的作用是在引导期间运行扫描/sys下设备的并根据扫描到的设备信息在/dev创建设备节点
三、container_of()解读
在Linux内核编程中,会经常见到一个宏函数container_of(ptr,type,member),函数原型如下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
...
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type, member)); })
其实原理很简单,它的作用就是:已知结构体type的成员member的地址ptr,求解结构体type的起始地址
如上图,type的起始地址 = ptr - size(这里需要都转换为char *,因为它为单位字节)
而图中的size就是由((size_t) &((TYPE *)0)->MEMBER)求出来的
四、readb()和writeb()系列函数
1 #define readb_relaxed(c) ({ u8 __r = __raw_readb(c); __r; }) 2 #define readw_relaxed(c) ({ u16 __r = le16_to_cpu((__force __le16) \ 3 __raw_readw(c)); __r; }) 4 #define readl_relaxed(c) ({ u32 __r = le32_to_cpu((__force __le32) \ 5 __raw_readl(c)); __r; }) 6 7 #define writeb_relaxed(v,c) __raw_writeb(v,c) 8 #define writew_relaxed(v,c) __raw_writew((__force u16) cpu_to_le16(v),c) 9 #define writel_relaxed(v,c) __raw_writel((__force u32) cpu_to_le32(v),c) 10 11 #define readb(c) ({ u8 __v = readb_relaxed(c); __iormb(); __v; }) 12 #define readw(c) ({ u16 __v = readw_relaxed(c); __iormb(); __v; }) 13 #define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; }) 14 15 #define writeb(v,c) ({ __iowmb(); writeb_relaxed(v,c); }) 16 #define writew(v,c) ({ __iowmb(); writew_relaxed(v,c); }) 17 #define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); })
此系列函数用于从内存映射的I/O空间读写数据
readb()和readb_relaxed()从I/O空间读取8位数据(1字节)
readw()和readb_relaxew()从I/O空间读取16位数据(2字节)
readl()和readb_relaxel()从I/O空间读取32位数据(4字节)
writeb()和writeb_relaxed()从I/O空间写入8位数据(1字节)
writew()和writew_relaxed()从I/O空间写入16位数据(2字节)
writel()和writel_relaxed()从I/O空间写入32位数据(4字节)
其中没有relaxed()的系列函数都调用了__iowmb()和__iormb()函数,这两个函数是内存屏障指令,防止编译器优化执行过程。
五、ioctl()的幻数
ioctl()函数的幻数定义如下:
#define GLOBAL_MAGIC 'g' #define CMD_RESET _IO(GLOBAL_MAGIC, 1)
如果正常定义CMD_RESET 0x1可能会导致不同的设备驱动拥有相同的命令号。如果设备1、设备2都支持0x1命令,就会造成命令码的污染。因此,Linux内核推荐采用幻数来生成命令