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,對於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內核推薦采用幻數來生成命令

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM