Linux驱动函数解读

一、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

那么是谁创建/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内核推荐采用幻数来生成命令

猜你喜欢

转载自www.cnblogs.com/Lioker/p/10834377.html
今日推荐