一、Linux設備分類
Linux系統為了管理方便,將設備分成三種基本類型:
字符設備塊設備網絡設備字符設備:
字符(char)設備是個能夠像字節流(類似文件)一樣被訪問的設備,由字符設備驅動程序來實現這種特性。字符設備驅動程序通常至少要實現open、close、read和write的系統調用。
字符終端(/dev/console)和串口(/dev/ttyS0以及類似設備)就是兩個字符設備,它們能很好的說明“流”這種抽象概念。
字符設備可以通過文件節點來訪問,比如/dev/tty1和/dev/lp0等。這些設備文件和普通文件之間的唯一差別在于對普通文件的訪問可以前后移動訪問位置,而大多數字符設備是一個只能順序訪問的數據通道。然而,也存在具有數據區特性的字符設備,訪問它們時可前后移動訪問位置。例如framebuffer就是這樣的一個設備,app可以用mmap或lseek訪問抓取的整個圖像。
在/dev下執行ls -l ,可以看到很多創建好的設備節點:
字符設備文件(類型為c),設備文件是沒有文件大小的,取而代之的是兩個號碼:主設備號5 +次設備號1 。
塊設備:
和字符設備類似,塊設備也是通過/dev目錄下的文件系統節點來訪問。塊設備(例如磁盤)上能夠容納filesystem。在大多數的Unix系統中,進行I/O操作時塊設備每次只能傳輸一個或多個完整的塊,而每塊包含512字節(或2的更高次冪字節的數據)。
Linux可以讓app像字符設備一樣地讀寫塊設備,允許一次傳遞任意多字節的數據。因此,塊設備和字符設備的區別僅僅在于內核內部管理數據的方式,也就是內核及驅動程序之間的軟件接口,而這些不同對用戶來講是透明的。在內核中,和字符驅動程序相比,塊驅動程序具有完全不同的接口。
塊設備文件(類型為b):
網絡設備:
任何網絡事物都需要經過一個網絡接口形成,網絡接口是一個能夠和其他主機交換數據的設備。接口通常是一個硬件設備,但也可能是個純軟件設備,比如回環(loopback)接口。
網絡接口由內核中的網絡子系統驅動,負責發送和接收數據包。許多網絡連接(尤其是使用TCP協議的連接)是面向流的,但網絡設備卻圍繞數據包的傳送和接收而設計。網絡驅動程序不需要知道各個連接的相關信息,它只要處理數據包即可。
由于不是面向流的設備,因此將網絡接口映射到filesystem中的節點(比如/dev/tty1)比較困難。
Unix訪問網絡接口的方法仍然是給它們分配一個唯一的名字(比如eth0),但這個名字在filesystem中不存在對應的節點。內核和網絡設備驅動程序間的通信,完全不同于內核和字符以及塊驅動程序之間的通信,內核調用一套和數據包相關的函數socket,也叫套接字。
查看網絡設備使用命令ifconfig:
二、字符設備架構是如何實現的?
在Linux的世界里面一切皆文件,所有的硬件設備操作到應用層都會被抽象成文件的操作。我們知道如果應用層要訪問硬件設備,它必定要調用到硬件對應的驅動程序。Linux內核中有那么多驅動程序,應用層怎么才能精確的調用到底層的驅動程序呢?
在這里我們字符設備為例,來看一下應用程序是如何和底層驅動程序關聯起來的。必須知道的基礎知識:
1.在Linux文件系統中,每個文件都用一個struct inode結構體來描述,這個結構體里面記錄了這個文件的所有信息,例如:文件類型,訪問權限等。
2.在Linux操作系統中,每個驅動程序在應用層的/dev目錄下都會有一個設備文件和它對應,并且該文件會有對應的主設備號和次設備號。
3.在Linux操作系統中,每個驅動程序都要分配一個主設備號,字符設備的設備號保存在struct cdev結構體中。
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;//接口函數集合
struct list_head list;//內核鏈表
dev_t dev; //設備號
unsigned int count;//次設備號個數
};
4.在Linux操作系統中,每打開一次文件,Linux操作系統在VFS層都會分配一個struct file結構體來描述打開的這個文件。該結構體用于維護文件打開權限、文件指針偏移值、私有內存地址等信息。
注意:
常常我們認為struct inode描述的是文件的靜態信息,即這些信息很少會改變。而struct file描述的是動態信息,即在對文件的操作的時候,struct file里面的信息經常會發生變化。典型的是struct file結構體里面的f_pos(記錄當前文件的位移量),每次讀寫一個普通文件時f_ops的值都會發生改變。
這幾個結構體關系如下圖所示:
通過上圖我們可以知道,如果想訪問底層設備,就必須打開對應的設備文件。也就是在這個打開的過程中,Linux內核將應用層和對應的驅動程序關聯起來。
1.當open函數打開設備文件時,可以根據設備文件對應的struct inode結構體描述的信息,可以知道接下來要操作的設備類型(字符設備還是塊設備)。還會分配一個struct file結構體。
2.根據struct inode結構體里面記錄的設備號,可以找到對應的驅動程序。這里以字符設備為例。在Linux操作系統中每個字符設備有一個struct cdev結構體。此結構體描述了字符設備所有的信息,其中最重要一項的就是字符設備的操作函數接口。
3.找到struct cdev結構體后,Linux內核就會將struct cdev結構體所在的內存空間首地記錄在struct inode結構體的i_cdev成員中。將struct cdev結構體的中記錄的函數操作接口地址記錄在struct file結構體的f_op成員中。
4.任務完成,VFS層會給應用層返回一個文件描述符(fd)。這個fd是和struct file結構體對應的。接下來上層的應用程序就可以通過fd來找到strut file,然后在由struct file找到操作字符設備的函數接口了。
三、字符驅動相關函數分析*
* cdev_init() - initialize a cdev structure
* @cdev: the structure to initialize
* @fops: the file_operations for this device
*
* Initializes @cdev, remembering @fops, making it ready to add to the
* system with cdev_add().
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
功能:
初始化cdev結構體
參數:
@cdev cdev結構體地址
@fops 操作字符設備的函數接口地址
返回值:
無
*
* register_chrdev_region() - register a range of device numbers
* @from: the first in the desired range of device numbers; must include
* the major number.
* @count: the number of consecutive device numbers required
* @name: the name of the device or driver.
*
* Return value is zero on success, a negative error code on failure.
int register_chrdev_region(dev_t from, unsigned count, const char *name)
功能:
注冊一個范圍()的設備號
參數:
@from 設備號
@count 注冊的設備個數
@name 設備的名字
返回值:
成功返回0,失敗返回錯誤碼(負數)
*
* cdev_add() - add a char device to the system
* @p: the cdev structure for the device
* @dev: the first device number for which this device is responsible
* @count: the number of consecutive minor numbers corresponding to this
* device
*
* cdev_add() adds the device represented by @p to the system, making it
* live immediately. A negative error code is returned on failure.
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
功能:
添加一個字符設備到操作系統
參數:
@p cdev結構體地址
@dev 設備號
@count 次設備號個數
返回值:
成功返回0,失敗返回錯誤碼(負數)
*
* cdev_del() - remove a cdev from the system
* @p: the cdev structure to be removed
*
* cdev_del() removes @p from the system, possibly freeing the structure
* itself.
void cdev_del(struct cdev *p)
功能:
從系統中刪除一個字符設備
參數:
@p cdev結構體地址
返回值:
無
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
功能:
注冊或者分配設備號,并注冊fops到cdev結構體,
如果major>0,功能為注冊該主設備號,
如果major=0,功能為動態分配主設備號。
參數:
@major : 主設備號
@name : 設備名稱,執行 cat /proc/devices顯示的名稱
@fops : 文件系統的接口指針
返回值
如果major>0 成功返回0,失敗返回負的錯誤碼
如果major=0 成功返回主設備號,失敗返回負的錯誤碼
該函數實現了對cdev的初始化和注冊的封裝,所以調用該函數之后就不需要自己操作cdev了。
相對的注銷函數為unregister_chrdev
static inline void unregister_chrdev(unsigned int major, const char *name)
審核編輯:符乾江
-
Linux
+關注
關注
87文章
11479瀏覽量
213073 -
應用層
+關注
關注
0文章
46瀏覽量
11677 -
Struct
+關注
關注
0文章
31瀏覽量
11053
發布評論請先 登錄
嵌入式學習-飛凌嵌入式ElfBoard ELF 1板卡-Linux設備驅動的分類
飛凌嵌入式ElfBoard ELF 1板卡-Linux設備驅動的分類
基于OpenSBI的linux nommu實現

評論