溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Linux系統(tǒng)驅(qū)動(dòng)開(kāi)發(fā)的基礎(chǔ)知識(shí)點(diǎn)有哪些

發(fā)布時(shí)間:2022-01-26 16:22:27 來(lái)源:億速云 閱讀:221 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹了Linux系統(tǒng)驅(qū)動(dòng)開(kāi)發(fā)的基礎(chǔ)知識(shí)點(diǎn)有哪些的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡(jiǎn)單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇Linux系統(tǒng)驅(qū)動(dòng)開(kāi)發(fā)的基礎(chǔ)知識(shí)點(diǎn)有哪些文章都會(huì)有所收獲,下面我們一起來(lái)看看吧。

Linux系統(tǒng)驅(qū)動(dòng)開(kāi)發(fā)的基礎(chǔ)知識(shí)點(diǎn)有哪些

基礎(chǔ)性總結(jié)

1, linux驅(qū)動(dòng)一般分為3大類:

* 字符設(shè)備 * 塊設(shè)備 * 網(wǎng)絡(luò)設(shè)備

2, 開(kāi)發(fā)環(huán)境構(gòu)建:

* 交叉工具鏈構(gòu)建 * NFS和tftp服務(wù)器安裝

3, 驅(qū)動(dòng)開(kāi)發(fā)中設(shè)計(jì)到的硬件:

* 數(shù)字電路知識(shí) * ARM硬件知識(shí) * 熟練使用萬(wàn)用表和示波器 * 看懂芯片手冊(cè)和原理圖

4, linux內(nèi)核源代碼目錄結(jié)構(gòu):

Linux系統(tǒng)驅(qū)動(dòng)開(kāi)發(fā)的基礎(chǔ)知識(shí)點(diǎn)有哪些 

* arch/: arch子目錄包括了所有和體系結(jié)構(gòu)相關(guān)的核心代碼。它的每一個(gè)子目錄都代表一種支持的體系結(jié)構(gòu),例如i386就是關(guān)于intel cpu及與之相兼容體系結(jié)構(gòu)的子目錄。 

* block/: 部分塊設(shè)備驅(qū)動(dòng)程序; 

* crypto: 常用加密和散列算法(如AES、SHA等),還有一些壓縮和CRC校驗(yàn)算法; 

* documentation/: 文檔目錄,沒(méi)有內(nèi)核代碼,只是一套有用的文檔; 

* drivers/: 放置系統(tǒng)所有的設(shè)備驅(qū)動(dòng)程序;每種驅(qū)動(dòng)程序又各占用一個(gè)子目錄:如,/block 下為塊設(shè)備驅(qū)動(dòng)程序,比如ide(ide.c)。如果你希望查看所有可能包含文件系統(tǒng)的設(shè)備是如何初始化的,你可以看 drivers/block/genhd.c中device_setup()。 

* fs/: 所有的文件系統(tǒng)代碼和各種類型的文件操作代碼,它的每一個(gè)子目錄支持一個(gè)文件系統(tǒng), 例如fat和ext2; 

* include/: include子目錄包括編譯核心所需要的大部分頭文件。與平臺(tái)無(wú)關(guān)的頭文件在 include/linux子目錄下,與 intel cpu相關(guān)的頭文件在include/asm-i386子目錄下,而include/scsi目錄則是有關(guān)scsi設(shè)備的頭文件目錄; 

* init/: 這個(gè)目錄包含核心的初始化代碼(注:不是系統(tǒng)的引導(dǎo)代碼),包含兩個(gè)文件main.c和Version.c,這是研究核心如何工作的好的起點(diǎn)之一; 

* ipc/: 這個(gè)目錄包含核心的進(jìn)程間通訊的代碼; 

* kernel/: 主要的核心代碼,此目錄下的文件實(shí)現(xiàn)了大多數(shù)linux系統(tǒng)的內(nèi)核函數(shù),其中最重要的文件當(dāng)屬sched.c;同樣,和體系結(jié)構(gòu)相關(guān)的代碼在arch/i386/kernel下; 

* lib/: 放置核心的庫(kù)代碼; 

* mm/:這個(gè)目錄包括所有獨(dú)立于 cpu 體系結(jié)構(gòu)的內(nèi)存管理代碼,如頁(yè)式存儲(chǔ)管理內(nèi)存的分配和釋放等;而和體系結(jié)構(gòu)相關(guān)的內(nèi)存管理代碼則位于arch/i386/mm/下; 

* net/: 核心與網(wǎng)絡(luò)相關(guān)的代碼; 

* scripts/: 描述文件,腳本,用于對(duì)核心的配置; 

* security: 主要是一個(gè)SELinux的模塊; 

* sound: 常用音頻設(shè)備的驅(qū)動(dòng)程序等; 

* usr: 實(shí)現(xiàn)了用于打包和壓縮的cpio。

5, 內(nèi)核的五個(gè)子系統(tǒng):

* 進(jìn)程調(diào)試(SCHED) * 內(nèi)存管理(MM) * 虛擬文件系統(tǒng)(VFS) * 網(wǎng)絡(luò)接口(NET) * 進(jìn)程間通信(IPC)

6, linux內(nèi)核的編譯:

* 配置內(nèi)核:make menuconfig,使用后會(huì)生成一個(gè).confiig配置文件,記錄哪些部分被編譯入內(nèi)核,哪些部分被編譯成內(nèi)核模塊。 * 編譯內(nèi)核和模塊的方法:make zImage Make modules * 執(zhí)行完上述命令后,在arch/arm/boot/目錄下得到壓縮的內(nèi)核映像zImage,在內(nèi)核各對(duì)應(yīng)目錄得到選中的內(nèi)核模塊。

7, 在linux內(nèi)核中增加程序

(直接編譯進(jìn)內(nèi)核)要完成以下3項(xiàng)工作: * 將編寫(xiě)的源代碼拷入linux內(nèi)核源代碼相應(yīng)目錄 * 在目錄的Kconifg文件中增加關(guān)于新源代碼對(duì)應(yīng)項(xiàng)目的編譯配置選項(xiàng) * 在目錄的Makefile文件中增加對(duì)新源代碼的編譯條目

8, linux下C編程的特點(diǎn):

內(nèi)核下的Documentation/CodingStyle描述了linux內(nèi)核對(duì)編碼風(fēng)格的要求。具體要求不一一列舉,以下是要注意的: * 代碼中空格的應(yīng)用 * 當(dāng)前函數(shù)名: GNU C預(yù)定義了兩個(gè)標(biāo)志符保存當(dāng)前函數(shù)的名字,__FUNCTION__保存函數(shù)在源碼中的名字,__PRETTY_FUNCTION__保存帶語(yǔ)言特色的名字。 由于C99已經(jīng)支持__func__宏,在linux編程中應(yīng)該不要使用__FUNCTION__,應(yīng)該使用__func__。 *內(nèi)建函數(shù):不屬于庫(kù)函數(shù)的其他內(nèi)建函數(shù)的命名通常以__builtin開(kāi)始。

9,內(nèi)核模塊

內(nèi)核模塊主要由如下幾部分組成: (1) 模塊加載函數(shù) (2) 模塊卸載函數(shù) (3) 模塊許可證聲明(常用的有Dual BSD/GPL,GPL,等) (4) 模塊參數(shù)(可選)它指的是模塊被加載的時(shí)候可以傳遞給它的值,它本身對(duì)應(yīng)模塊內(nèi)部的全局變量。例如P88頁(yè)中講到的一個(gè)帶模塊參數(shù)的例子: insmod book.ko book_name=”GOOD BOOK” num=5000 (5) 模塊導(dǎo)出符號(hào)(可選)導(dǎo)出的符號(hào)可以被其他模塊使用,在使用之前只需聲明一下。 (6) 模塊作者等聲明信息(可選) 以下是一個(gè)典型的內(nèi)核模塊:

 /*
  * A kernel module: book
  * This example is to introduce module params
  *
  * The initial developer of the original code is Baohua Song
  * . All Rights Reserved.
  */
 
 #include #include 
 static char *book_name = “dissecting Linux Device Driver”;
 static int num = 4000;
 
 static int book_init(void)
 {
         printk(KERN_INFO “ book name:%s\n”,book_name);
         printk(KERN_INFO “ book num:%d\n”,num);
         return 0;
 }
 
 static void book_exit(void)
 {
         printk(KERN_INFO “ Book module exit\n “);
 }
 
 module_init(book_init);
 module_exit(book_exit);
 module_param(num, int, S_IRUGO);
 module_param(book_name, charp, S_IRUGO);
 MODULE_AUTHOR(“Song Baohua, author@linuxdriver.cn”);
 MODULE_LICENSE(“Dual BSD/GPL”);
 MODULE_DESCRIPTION(“A simple Module for testing module params”);
 MODULE_VERSION(“V1.0”);12345678910111213141516171819202122232425262728293031323334

注意:標(biāo)有init的函數(shù)在鏈接的時(shí)候都放在.init.text段,在.initcall.init中還保存了一份函數(shù)指針,初始化的時(shí)候內(nèi)核會(huì)通過(guò)這些函數(shù)指針調(diào)用init函數(shù),在初始化完成后釋放init區(qū)段。 模塊編譯常用模版:

 KVERS = $(shell uname -r)
 # Kernel modules
 obj-m += book.o
 # Specify flags for the module compilation.
 #EXTRA_CFLAGS=-g -O0
 build: kernel_modules
 kernel_modules:
         make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules
 
 clean:
         make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean1234567891011

注意要指明內(nèi)核版本,并且內(nèi)核版本要匹配——編譯模塊使用的內(nèi)核版本要和模塊欲加載到的那個(gè)內(nèi)核版本要一致。 模塊中經(jīng)常使用的命令:

 insmod,lsmod,rmmod
 1

系統(tǒng)調(diào)用:

 int open(const char *pathname,int flags,mode_t mode);
 1

flag表示文件打開(kāi)標(biāo)志,如:O_RDONLY mode表示文件訪問(wèn)權(quán)限,如:S_IRUSR(用戶可讀),S_IRWXG(組可以讀、寫(xiě)、執(zhí)行)

10,linux文件系統(tǒng)與設(shè)備驅(qū)動(dòng)的關(guān)系

Linux系統(tǒng)驅(qū)動(dòng)開(kāi)發(fā)的基礎(chǔ)知識(shí)點(diǎn)有哪些

應(yīng)用程序和VFS之間的接口是系統(tǒng)調(diào)用,而VFS與磁盤文件系統(tǒng)以及普通設(shè)備之間的接口是file_operation結(jié)構(gòu)體成員函數(shù)。 兩個(gè)重要的函數(shù): (1)struct file結(jié)構(gòu)體定義在/linux/include/linux/fs.h(Linux 2.6.11內(nèi)核)中定義。文件結(jié)構(gòu)體代表一個(gè)打開(kāi)的文件,系統(tǒng)中每個(gè)打開(kāi)的文件在內(nèi)核空間都有一個(gè)關(guān)聯(lián)的struct file。它由內(nèi)核在打開(kāi)文件時(shí)創(chuàng)建,并傳遞給在文件上進(jìn)行操作的任何函數(shù)。在文件的所有實(shí)例都關(guān)閉后,內(nèi)核釋放這個(gè)數(shù)據(jù)結(jié)構(gòu)。在內(nèi)核創(chuàng)建和驅(qū)動(dòng)源碼中,struct file的指針通常被命名為file或filp。 在驅(qū)動(dòng)開(kāi)發(fā)中,文件讀/寫(xiě)模式mode、標(biāo)志f_flags都是設(shè)備驅(qū)動(dòng)關(guān)心的內(nèi)容,而私有數(shù)據(jù)指針private_data在驅(qū)動(dòng)中被廣泛使用,大多被指向設(shè)備驅(qū)動(dòng)自定義的用于描述設(shè)備的結(jié)構(gòu)體。驅(qū)動(dòng)程序中常用如下類似的代碼來(lái)檢測(cè)用戶打開(kāi)文件的讀寫(xiě)方式:

 if (file->f_mode & FMODE_WRITE) //用戶要求可寫(xiě)
 {
 }
 if (file->f_mode & FMODE_READ) //用戶要求可讀
 {
 }123456

下面的代碼可用于判斷以阻塞還是非阻塞方式打開(kāi)設(shè)備文件:

 if (file->f_flags & O_NONBLOCK) //非阻塞
 pr_debug("open:non-blocking\n");
 else //阻塞
 pr_debug("open:blocking\n");1234

(2)struct inode結(jié)構(gòu)體定義在linux/fs.h中

11,devfs、sysfs、udev三者的關(guān)系:

(1)devfs linux下有專門的文件系統(tǒng)用來(lái)對(duì)設(shè)備進(jìn)行管理,devfs和sysfs就是其中兩種。在2.4內(nèi)核4一直使用的是devfs,devfs掛載于/dev目錄下,提供了一種類似于文件的方法來(lái)管理位于/dev目錄下的所有設(shè)備,我們知道/dev目錄下的每一個(gè)文件都對(duì)應(yīng)的是一個(gè)設(shè)備,至于當(dāng)前該設(shè)備存在與否先且不論,而且這些特殊文件是位于根文件系統(tǒng)上的,在制作文件系統(tǒng)的時(shí)候我們就已經(jīng)建立了這些設(shè)備文件,因此通過(guò)操作這些特殊文件,可以實(shí)現(xiàn)與內(nèi)核進(jìn)行交互。但是devfs文件系統(tǒng)有一些缺點(diǎn),例如:不確定的設(shè)備映射,有時(shí)一個(gè)設(shè)備映射的設(shè)備文件可能不同,例如我的U盤可能對(duì)應(yīng)sda有可能對(duì)應(yīng)sdb;沒(méi)有足夠的主/次設(shè)備號(hào),當(dāng)設(shè)備過(guò)多的時(shí)候,顯然這會(huì)成為一個(gè)問(wèn)題;/dev目錄下文件太多而且不能表示當(dāng)前系統(tǒng)上的實(shí)際設(shè)備;命名不夠靈活,不能任意指定等等。 (2)sysfs 正因?yàn)樯鲜鲞@些問(wèn)題的存在,在linux2.6內(nèi)核以后,引入了一個(gè)新的文件系統(tǒng)sysfs,它掛載于/sys目錄下,跟devfs一樣它也是一個(gè)虛擬文件系統(tǒng),也是用來(lái)對(duì)系統(tǒng)的設(shè)備進(jìn)行管理的,它把實(shí)際連接到系統(tǒng)上的設(shè)備和總線組織成一個(gè)分級(jí)的文件,用戶空間的程序同樣可以利用這些信息以實(shí)現(xiàn)和內(nèi)核的交互,該文件系統(tǒng)是當(dāng)前系統(tǒng)上實(shí)際設(shè)備樹(shù)的一個(gè)直觀反應(yīng),它是通過(guò)kobject子系統(tǒng)來(lái)建立這個(gè)信息的,當(dāng)一個(gè)kobject被創(chuàng)建的時(shí)候,對(duì)應(yīng)的文件和目錄也就被創(chuàng)建了,位于/sys下的相關(guān)目錄下,既然每個(gè)設(shè)備在sysfs中都有唯一對(duì)應(yīng)的目錄,那么也就可以被用戶空間讀寫(xiě)了。用戶空間的工具udev就是利用了sysfs提供的信息來(lái)實(shí)現(xiàn)所有devfs的功能的,但不同的是udev運(yùn)行在用戶空間中,而devfs卻運(yùn)行在內(nèi)核空間,而且udev不存在devfs那些先天的缺陷。 

(3)udev udev是一種工具,它能夠根據(jù)系統(tǒng)中的硬件設(shè)備的狀況動(dòng)態(tài)更新設(shè)備文件,包括設(shè)備文件的創(chuàng)建,刪除等。設(shè)備文件通常放在/dev目錄下,使用udev后,在/dev下面只包含系統(tǒng)中真實(shí)存在的設(shè)備。它于硬件平臺(tái)無(wú)關(guān)的,位于用戶空間,需要內(nèi)核sysfs和tmpfs的支持,sysfs為udev提供設(shè)備入口和uevent通道,tmpfs為udev設(shè)備文件提供存放空間。

12,linux設(shè)備模型:

Linux系統(tǒng)驅(qū)動(dòng)開(kāi)發(fā)的基礎(chǔ)知識(shí)點(diǎn)有哪些

在linux內(nèi)核中,分別使用bus_type,device_driver,device來(lái)描述總線、驅(qū)動(dòng)和設(shè)備,這3個(gè)結(jié)構(gòu)體定義于include/linux/device.h頭文件中。驅(qū)動(dòng)和設(shè)備正是通過(guò)bus_type中的match()函數(shù)來(lái)配對(duì)的。

13, 重要結(jié)構(gòu)體解析

(1)cdev結(jié)構(gòu)體 在linux2.6內(nèi)核中,使用cdev結(jié)構(gòu)體描述一個(gè)字符設(shè)備,定義如下:

 struct cdev{
     struct kobject kobj;//內(nèi)嵌的kobject對(duì)象
     struct module *owner;//所屬模塊
     struct file_operations *ops;//文件操作結(jié)構(gòu)體
     struct list_head list;
     dev_t dev;//設(shè)備號(hào),長(zhǎng)度為32位,其中高12為主設(shè)備號(hào),低20位為此設(shè)備號(hào)
     unsigned int count;
 };12345678

(2)file_operations結(jié)構(gòu)體 結(jié)構(gòu)體file_operations在頭文件linux/fs.h中定義,用來(lái)存儲(chǔ)驅(qū)動(dòng)內(nèi)核模塊提供的對(duì)設(shè)備進(jìn)行各種操作的函數(shù)的指針。這些函數(shù)實(shí)際會(huì)在應(yīng)用程序進(jìn)行l(wèi)inux的open(),write(),read(),close()等系統(tǒng)調(diào)用時(shí)最終被調(diào)用。該結(jié)構(gòu)體的每個(gè)域都對(duì)應(yīng)著驅(qū)動(dòng)內(nèi)核模塊用來(lái)處理某個(gè)被請(qǐng)求的事務(wù)的函數(shù)地址。源代碼(2.6.28.7)如下:

 struct file_operations{
     struct module*owner;
     loff_t (*llseek)(struct file*,loff_t,int);
     ssize_t (*read)(struct file*,char__user*,size_t,loff_t*);
     ssize_t (*write)(struct file*,constchar__user*,size_t,loff_t*);
     ssize_t (*aio_read)(struct kiocb*,cons tstruct iovec*,unsigned long,loff_t);
     ssize_t (*aio_write)(struct kiocb*,const struct iovec*,unsigned long,loff_t);
     int (*readdir)(struct file*,void*,filldir_t);
     unsigned int (*poll)(struct file*,struct poll_table_struct*);
     int (*ioctl)(struc inode*,struct file*,unsigned int,unsigned long);
     long (*unlocked_ioctl)(struct file*,unsigned int,unsigned long);
     long (*compat_ioctl)(struct file*,unsigned int,unsigned long);
     int (*mmap)(struct file*,struct vm_area_struct*);
     int (*open)(struct inode*,struct file*);
     int (*flush)(struct file*,fl_owner_t id);
     int (*release)(struct inode*,struct file*);
     int (*fsync)(struct file*,struct dentry*,int datasync);
     int (*aio_fsync)(struct kiocb*,int datasync);
     in (*fasync)(int,struct file*,int);
     int (*lock)(struct file*,int,struct file_lock*);
     ssize_t (*sendpage)(struct file*,struct page*,int,size_t,loff_t*,int);
     unsigned long (*get_unmapped_area)(struct file*,unsigned long,unsigned long,unsigned long,unsigned long);
     in t(*check_flags)(int);
     int (*dir_notify)(structfile*filp,unsignedlongarg);
     int (*flock)(structfile*,int,structfile_lock*);
     ssize_t (*splice_write)(struct pipe_inode_info*,struct file*,loff_t*,size_t,unsig ned int);
     ssize_t (*splice_read)(struct file*,loff_t*,struct pipe_inode_info*,size_t,unsigned int);
     int(*setlease)(struct file*,long,struct file_lock**);
 }; 1234567891011121314151617181920212223242526272829

解析:

 struct module*owner;
 /*第一個(gè)file_operations成員根本不是一個(gè)操作;它是一個(gè)指向擁有這個(gè)結(jié)構(gòu)的模塊的指針.
 這個(gè)成員用來(lái)在它的操作還在被使用時(shí)阻止模塊被卸載.幾乎所有時(shí)間中,它被簡(jiǎn)單初始化為
 THIS_MODULE,一個(gè)在中定義的宏.這個(gè)宏比較復(fù)雜,在進(jìn)行簡(jiǎn)單學(xué)習(xí)操作的時(shí)候,一般初始化為THIS_MODULE。*/
 loff_t (*llseek)(struct file*filp,loff_tp,int orig);
 /*(指針參數(shù)filp為進(jìn)行讀取信息的目標(biāo)文件結(jié)構(gòu)體指針;參數(shù)p為文件定位的目標(biāo)偏移量;參數(shù)orig為對(duì)文件定位
 的起始地址,這個(gè)值可以為文件開(kāi)頭(SEEK_SET,0,當(dāng)前位置(SEEK_CUR,1),文件末尾(SEEK_END,2))
 llseek方法用作改變文件中的當(dāng)前讀/寫(xiě)位置,并且新位置作為(正的)返回值.
 loff_t參數(shù)是一個(gè)"longoffset",并且就算在32位平臺(tái)上也至少64位寬.錯(cuò)誤由一個(gè)負(fù)返回值指示.
 如果這個(gè)函數(shù)指針是NULL,seek調(diào)用會(huì)以潛在地?zé)o法預(yù)知的方式修改file結(jié)構(gòu)中的位置計(jì)數(shù)器(在"file結(jié)構(gòu)"一節(jié)中描述).*/
 ssize_t (*read)(struct file *filp,char__user *buffer,size_t size,loff_t *p);
 /*(指針參數(shù)filp為進(jìn)行讀取信息的目標(biāo)文件,指針參數(shù)buffer為對(duì)應(yīng)放置信息的緩沖區(qū)(即用戶空間內(nèi)存地址),
 參數(shù)size為要讀取的信息長(zhǎng)度,參數(shù)p為讀的位置相對(duì)于文件開(kāi)頭的偏移,在讀取信息后,這個(gè)指針一般都會(huì)移動(dòng),移動(dòng)的值為要讀取信息的長(zhǎng)度值)
 這個(gè)函數(shù)用來(lái)從設(shè)備中獲取數(shù)據(jù).在這個(gè)位置的一個(gè)空指針導(dǎo)致read系統(tǒng)調(diào)用以-EINVAL("Invalidargument")失敗.
 一個(gè)非負(fù)返回值代表了成功讀取的字節(jié)數(shù)(返回值是一個(gè)"signedsize"類型,常常是目標(biāo)平臺(tái)本地的整數(shù)類型).*/
 ssize_t (*aio_read)(struct kiocb*,char__user *buffer,size_t size,loff_t p);
 /*可以看出,這個(gè)函數(shù)的第一、三個(gè)參數(shù)和本結(jié)構(gòu)體中的read()函數(shù)的第一、三個(gè)參數(shù)是不同的,
 異步讀寫(xiě)的第三個(gè)參數(shù)直接傳遞值,而同步讀寫(xiě)的第三個(gè)參數(shù)傳遞的是指針,因?yàn)锳IO從來(lái)不需要改變文件的位置。
 異步讀寫(xiě)的第一個(gè)參數(shù)為指向kiocb結(jié)構(gòu)體的指針,而同步讀寫(xiě)的第一參數(shù)為指向file結(jié)構(gòu)體的指針,每一個(gè)I/O請(qǐng)求都對(duì)應(yīng)一個(gè)kiocb結(jié)構(gòu)體);
 初始化一個(gè)異步讀--可能在函數(shù)返回前不結(jié)束的讀操作.如果這個(gè)方法是NULL,所有的操作會(huì)由read代替進(jìn)行(同步地).
 (有關(guān)linux異步I/O,可以參考有關(guān)的資料,《linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》中給出了詳細(xì)的解答)*/
 ssize_t (*write)(struct file*filp,const char__user *buffer,size_t count,loff_t *ppos);
 /*(參數(shù)filp為目標(biāo)文件結(jié)構(gòu)體指針,buffer為要寫(xiě)入文件的信息緩沖區(qū),count為要寫(xiě)入信息的長(zhǎng)度,
 ppos為當(dāng)前的偏移位置,這個(gè)值通常是用來(lái)判斷寫(xiě)文件是否越界)
 發(fā)送數(shù)據(jù)給設(shè)備.如果NULL,-EINVAL返回給調(diào)用write系統(tǒng)調(diào)用的程序.如果非負(fù),返回值代表成功寫(xiě)的字節(jié)數(shù).
 (注:這個(gè)操作和上面的對(duì)文件進(jìn)行讀的操作均為阻塞操作)*/
 ssize_t (*aio_write)(struct kiocb*,const char__user *buffer,size_t count,loff_t *ppos);
 /*初始化設(shè)備上的一個(gè)異步寫(xiě).參數(shù)類型同aio_read()函數(shù);*/
 int (*readdir)(struct file*filp,void*,filldir_t);
 /*對(duì)于設(shè)備文件這個(gè)成員應(yīng)當(dāng)為NULL;它用來(lái)讀取目錄,并且僅對(duì)文件系統(tǒng)有用.*/
 unsigned int(*poll)(struct file*,struct poll_table_struct*);
 /*(這是一個(gè)設(shè)備驅(qū)動(dòng)中的輪詢函數(shù),第一個(gè)參數(shù)為file結(jié)構(gòu)指針,第二個(gè)為輪詢表指針)
 這個(gè)函數(shù)返回設(shè)備資源的可獲取狀態(tài),即POLLIN,POLLOUT,POLLPRI,POLLERR,POLLNVAL等宏的位“或”結(jié)果。
 每個(gè)宏都表明設(shè)備的一種狀態(tài),如:POLLIN(定義為0x0001)意味著設(shè)備可以無(wú)阻塞的讀,POLLOUT(定義為0x0004)意味著設(shè)備可以無(wú)阻塞的寫(xiě)。
 (poll方法是3個(gè)系統(tǒng)調(diào)用的后端:poll,epoll,和select,都用作查詢對(duì)一個(gè)或多個(gè)文件描述符的讀或?qū)懯欠駮?huì)阻塞.
 poll方法應(yīng)當(dāng)返回一個(gè)位掩碼指示是否非阻塞的讀或?qū)懯强赡艿?并且,可能地,提供給內(nèi)核信息用來(lái)使調(diào)用進(jìn)程睡眠直到I/O變?yōu)榭赡?
 如果一個(gè)驅(qū)動(dòng)的poll方法為NULL,設(shè)備假定為不阻塞地可讀可寫(xiě).
 (這里通常將設(shè)備看作一個(gè)文件進(jìn)行相關(guān)的操作,而輪詢操作的取值直接關(guān)系到設(shè)備的響應(yīng)情況,可以是阻塞操作結(jié)果,同時(shí)也可以是非阻塞操作結(jié)果)*/
 int (*ioctl)(struct inode*inode,struct file*filp,unsigned int cmd,unsigned long arg);
 /*(inode和filp指針是對(duì)應(yīng)應(yīng)用程序傳遞的文件描述符fd的值,和傳遞給open方法的相同參數(shù).
 cmd參數(shù)從用戶那里不改變地傳下來(lái),并且可選的參數(shù)arg參數(shù)以一個(gè)unsignedlong的形式傳遞,不管它是否由用戶給定為一個(gè)整數(shù)或一個(gè)指針.
 如果調(diào)用程序不傳遞第3個(gè)參數(shù),被驅(qū)動(dòng)操作收到的arg值是無(wú)定義的.
 因?yàn)轭愋蜋z查在這個(gè)額外參數(shù)上被關(guān)閉,編譯器不能警告你如果一個(gè)無(wú)效的參數(shù)被傳遞給ioctl,并且任何關(guān)聯(lián)的錯(cuò)誤將難以查找.)
 ioctl系統(tǒng)調(diào)用提供了發(fā)出設(shè)備特定命令的方法(例如格式化軟盤的一個(gè)磁道,這不是讀也不是寫(xiě)).另外,幾個(gè)ioctl命令被內(nèi)核識(shí)別而不必引用fops表.
 如果設(shè)備不提供ioctl方法,對(duì)于任何未事先定義的請(qǐng)求(-ENOTTY,"設(shè)備無(wú)這樣的ioctl"),系統(tǒng)調(diào)用返回一個(gè)錯(cuò)誤.*/
 int(*mmap)(struct file*,struct vm_area_struct*);
 /*mmap用來(lái)請(qǐng)求將設(shè)備內(nèi)存映射到進(jìn)程的地址空間.如果這個(gè)方法是NULL,mmap系統(tǒng)調(diào)用返回-ENODEV.
 (如果想對(duì)這個(gè)函數(shù)有個(gè)徹底的了解,那么請(qǐng)看有關(guān)“進(jìn)程地址空間”介紹的書(shū)籍)*/
 int(*open)(struct inode *inode,struct file *filp);
 /*(inode為文件節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)只有一個(gè),無(wú)論用戶打開(kāi)多少個(gè)文件,都只是對(duì)應(yīng)著一個(gè)inode結(jié)構(gòu);
 但是filp就不同,只要打開(kāi)一個(gè)文件,就對(duì)應(yīng)著一個(gè)file結(jié)構(gòu)體,file結(jié)構(gòu)體通常用來(lái)追蹤文件在運(yùn)行時(shí)的狀態(tài)信息)
 盡管這常常是對(duì)設(shè)備文件進(jìn)行的第一個(gè)操作,不要求驅(qū)動(dòng)聲明一個(gè)對(duì)應(yīng)的方法.如果這個(gè)項(xiàng)是NULL,設(shè)備打開(kāi)一直成功,但是你的驅(qū)動(dòng)不會(huì)得到通知.
 與open()函數(shù)對(duì)應(yīng)的是release()函數(shù)。*/
 int(*flush)(struct file*);
 /*flush操作在進(jìn)程關(guān)閉它的設(shè)備文件描述符的拷貝時(shí)調(diào)用;它應(yīng)當(dāng)執(zhí)行(并且等待)設(shè)備的任何未完成的操作.
 這個(gè)必須不要和用戶查詢請(qǐng)求的fsync操作混淆了.當(dāng)前,flush在很少驅(qū)動(dòng)中使用;
 SCSI磁帶驅(qū)動(dòng)使用它,例如,為確保所有寫(xiě)的數(shù)據(jù)在設(shè)備關(guān)閉前寫(xiě)到磁帶上.如果flush為NULL,內(nèi)核簡(jiǎn)單地忽略用戶應(yīng)用程序的請(qǐng)求.*/
 int(*release)(struct inode*,struct file*);
 /*release()函數(shù)當(dāng)最后一個(gè)打開(kāi)設(shè)備的用戶進(jìn)程執(zhí)行close()系統(tǒng)調(diào)用的時(shí)候,內(nèi)核將調(diào)用驅(qū)動(dòng)程序release()函數(shù):
 void release(struct inode inode,struct file *file),release函數(shù)的主要任務(wù)是清理未結(jié)束的輸入輸出操作,釋放資源,用戶自定義排他標(biāo)志的復(fù)位等。
 在文件結(jié)構(gòu)被釋放時(shí)引用這個(gè)操作.如同open,release可以為NULL.*/
 int (*synch)(struct file*,struct dentry*,intdatasync);
 //刷新待處理的數(shù)據(jù),允許進(jìn)程把所有的臟緩沖區(qū)刷新到磁盤。
 int(*aio_fsync)(struct kiocb*,int);
 /*這是fsync方法的異步版本.所謂的fsync方法是一個(gè)系統(tǒng)調(diào)用函數(shù)。系統(tǒng)調(diào)用fsync
 把文件所指定的文件的所有臟緩沖區(qū)寫(xiě)到磁盤中(如果需要,還包括存有索引節(jié)點(diǎn)的緩沖區(qū))。
 相應(yīng)的服務(wù)例程獲得文件對(duì)象的地址,并隨后調(diào)用fsync方法。通常這個(gè)方法以調(diào)用函數(shù)__writeback_single_inode()結(jié)束,
 這個(gè)函數(shù)把與被選中的索引節(jié)點(diǎn)相關(guān)的臟頁(yè)和索引節(jié)點(diǎn)本身都寫(xiě)回磁盤。*/
 int(*fasync)(int,struct file*,int);
 //這個(gè)函數(shù)是系統(tǒng)支持異步通知的設(shè)備驅(qū)動(dòng),下面是這個(gè)函數(shù)的模板:
 static int***_fasync(intfd,structfile*filp,intmode)
 {
 struct***_dev*dev=filp->private_data;
 returnfasync_helper(fd,filp,mode,&dev->async_queue);//第四個(gè)參數(shù)為fasync_struct結(jié)構(gòu)體指針的指針。
 //這個(gè)函數(shù)是用來(lái)處理FASYNC標(biāo)志的函數(shù)。(FASYNC:表示兼容BSD的fcntl同步操作)當(dāng)這個(gè)標(biāo)志改變時(shí),驅(qū)動(dòng)程序中的fasync()函數(shù)將得到執(zhí)行。
 }
 /*此操作用來(lái)通知設(shè)備它的FASYNC標(biāo)志的改變.異步通知是一個(gè)高級(jí)的主題,在第6章中描述.
 這個(gè)成員可以是NULL如果驅(qū)動(dòng)不支持異步通知.*/
 int (*lock)(struct file*,int,struct file_lock*);
 //lock方法用來(lái)實(shí)現(xiàn)文件加鎖;加鎖對(duì)常規(guī)文件是必不可少的特性,但是設(shè)備驅(qū)動(dòng)幾乎從不實(shí)現(xiàn)它.
 ssize_t (*readv)(structfile*,const struct iovec*,unsigned long,loff_t*);
 ssize_t (*writev)(struct file*,const struct iovec*,unsigned long,loff_t*);
 /*這些方法實(shí)現(xiàn)發(fā)散/匯聚讀和寫(xiě)操作.應(yīng)用程序偶爾需要做一個(gè)包含多個(gè)內(nèi)存區(qū)的單個(gè)讀或?qū)懖僮?
 這些系統(tǒng)調(diào)用允許它們這樣做而不必對(duì)數(shù)據(jù)進(jìn)行額外拷貝.如果這些函數(shù)指針為NULL,read和write方法被調(diào)用(可能多于一次).*/
 ssize_t (*sendfile)(struct file*,loff_t*,size_t,read_actor_t,void*);
 /*這個(gè)方法實(shí)現(xiàn)sendfile系統(tǒng)調(diào)用的讀,使用最少的拷貝從一個(gè)文件描述符搬移數(shù)據(jù)到另一個(gè).
 例如,它被一個(gè)需要發(fā)送文件內(nèi)容到一個(gè)網(wǎng)絡(luò)連接的web服務(wù)器使用.設(shè)備驅(qū)動(dòng)常常使sendfile為NULL.*/
 ssize_t (*sendpage)(structfile*,structpage*,int,size_t,loff_t*,int);
 /*sendpage是sendfile的另一半;它由內(nèi)核調(diào)用來(lái)發(fā)送數(shù)據(jù),一次一頁(yè),到對(duì)應(yīng)的文件.設(shè)備驅(qū)動(dòng)實(shí)際上不實(shí)現(xiàn)sendpage.*/
 unsigned long(*get_unmapped_area)(struct file*,unsigned long,unsignedlong,unsigned long,unsigned long);
 /*這個(gè)方法的目的是在進(jìn)程的地址空間找一個(gè)合適的位置來(lái)映射在底層設(shè)備上的內(nèi)存段中.
 這個(gè)任務(wù)通常由內(nèi)存管理代碼進(jìn)行;這個(gè)方法存在為了使驅(qū)動(dòng)能強(qiáng)制特殊設(shè)備可能有的任何的對(duì)齊請(qǐng)求.大部分驅(qū)動(dòng)可以置這個(gè)方法為NULL.[10]*/
 int (*check_flags)(int)
 //這個(gè)方法允許模塊檢查傳遞給fnctl(F_SETFL...)調(diào)用的標(biāo)志.
 int (*dir_notify)(struct file*,unsigned long);
 //這個(gè)方法在應(yīng)用程序使用fcntl來(lái)請(qǐng)求目錄改變通知時(shí)調(diào)用.只對(duì)文件系統(tǒng)有用;驅(qū)動(dòng)不需要實(shí)現(xiàn)dir_notify.123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596

14, 字符設(shè)備驅(qū)動(dòng)程序設(shè)計(jì)基礎(chǔ)

主設(shè)備號(hào)和次設(shè)備號(hào)(二者一起為設(shè)備號(hào)): 一個(gè)字符設(shè)備或塊設(shè)備都有一個(gè)主設(shè)備號(hào)和一個(gè)次設(shè)備號(hào)。主設(shè)備號(hào)用來(lái)標(biāo)識(shí)與設(shè)備文件相連的驅(qū)動(dòng)程序,用來(lái)反映設(shè)備類型。次設(shè)備號(hào)被驅(qū)動(dòng)程序用來(lái)辨別操作的是哪個(gè)設(shè)備,用來(lái)區(qū)分同類型的設(shè)備。 linux內(nèi)核中,設(shè)備號(hào)用dev_t來(lái)描述,2.6.28中定義如下:

 typedef u_long dev_t;1

在32位機(jī)中是4個(gè)字節(jié),高12位表示主設(shè)備號(hào),低12位表示次設(shè)備號(hào)。

可以使用下列宏從dev_t中獲得主次設(shè)備號(hào):也可以使用下列宏通過(guò)主次設(shè)備號(hào)生成dev_t:

 MAJOR(dev_tdev);
 MKDEV(intmajor,intminor);
 MINOR(dev_tdev);123

分配設(shè)備號(hào)(兩種方法): (1)靜態(tài)申請(qǐng):

 int register_chrdev_region(dev_t from,unsigned count,const char *name);
 1

(2)動(dòng)態(tài)分配:

 int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name);
 1

注銷設(shè)備號(hào):

 void unregister_chrdev_region(dev_t from,unsigned count);
 1

創(chuàng)建設(shè)備文件: 利用cat/proc/devices查看申請(qǐng)到的設(shè)備名,設(shè)備號(hào)。 (1)使用mknod手工創(chuàng)建:mknod filename type major minor (2)自動(dòng)創(chuàng)建; 利用udev(mdev)來(lái)實(shí)現(xiàn)設(shè)備文件的自動(dòng)創(chuàng)建,首先應(yīng)保證支持udev(mdev),由busybox配置。在驅(qū)動(dòng)初始化代碼里調(diào)用class_create為該設(shè)備創(chuàng)建一個(gè)class,再為每個(gè)設(shè)備調(diào)用device_create創(chuàng)建對(duì)應(yīng)的設(shè)備。

15, 字符設(shè)備驅(qū)動(dòng)程序設(shè)計(jì)

設(shè)備注冊(cè): 字符設(shè)備的注冊(cè)分為三個(gè)步驟: (1)分配

 cdev:struct cdev *cdev_alloc(void);
 1

(2)初始化

 cdev:void cdev_init(struct cdev *cdev,const struct file_operations *fops);
 1

(3)添加

 cdev:int cdev_add(struct cdev *p,dev_t dev,unsigned count)
 1

設(shè)備操作的實(shí)現(xiàn): file_operations函數(shù)集的實(shí)現(xiàn)。

 struct file_operations xxx_ops={
 .owner=THIS_MODULE,
 .llseek=xxx_llseek,
 .read=xxx_read,
 .write=xxx_write,
 .ioctl=xxx_ioctl,
 .open=xxx_open,
 .release=xxx_release,
 …
 };12345678910

特別注意:驅(qū)動(dòng)程序應(yīng)用程序的數(shù)據(jù)交換: 驅(qū)動(dòng)程序和應(yīng)用程序的數(shù)據(jù)交換是非常重要的。file_operations中的read()和write()函數(shù),就是用來(lái)在驅(qū)動(dòng)程序和應(yīng)用程序間交換數(shù)據(jù)的。通過(guò)數(shù)據(jù)交換,驅(qū)動(dòng)程序和應(yīng)用程序可以彼此了解對(duì)方的情況。但是驅(qū)動(dòng)程序和應(yīng)用程序?qū)儆诓煌牡刂房臻g。驅(qū)動(dòng)程序不能直接訪問(wèn)應(yīng)用程序的地址空間;同樣應(yīng)用程序也不能直接訪問(wèn)驅(qū)動(dòng)程序的地址空間,否則會(huì)破壞彼此空間中的數(shù)據(jù),從而造成系統(tǒng)崩潰,或者數(shù)據(jù)損壞。安全的方法是使用內(nèi)核提供的專用函數(shù),完成數(shù)據(jù)在應(yīng)用程序空間和驅(qū)動(dòng)程序空間的交換。這些函數(shù)對(duì)用戶程序傳過(guò)來(lái)的指針進(jìn)行了嚴(yán)格的檢查和必要的轉(zhuǎn)換,從而保證用戶程序與驅(qū)動(dòng)程序交換數(shù)據(jù)的安全性。這些函數(shù)有:

 unsigned long copy_to_user(void__user *to,const void *from,unsigned long n);
 unsigned long copy_from_user(void *to,constvoid __user *from,unsigned long n);
 put_user(local,user);
 get_user(local,user);1234

設(shè)備注銷:

 void cdev_del(struct cdev *p);
 1

16,ioctl函數(shù)說(shuō)明

ioctl是設(shè)備驅(qū)動(dòng)程序中對(duì)設(shè)備的I/O通道進(jìn)行管理的函數(shù)。所謂對(duì)I/O通道進(jìn)行管理,就是對(duì)設(shè)備的一些特性進(jìn)行控制,例如串口的傳輸波特率、馬達(dá)的轉(zhuǎn)速等等。它的調(diào)用個(gè)數(shù)如下:

 int ioctl(int fd,ind cmd,…);
 1

其中fd就是用戶程序打開(kāi)設(shè)備時(shí)使用open函數(shù)返回的文件標(biāo)示符,cmd就是用戶程序?qū)υO(shè)備的控制命令,后面的省略號(hào)是一些補(bǔ)充參數(shù),有或沒(méi)有是和cmd的意義相關(guān)的。 ioctl函數(shù)是文件結(jié)構(gòu)中的一個(gè)屬性分量,就是說(shuō)如果你的驅(qū)動(dòng)程序提供了對(duì)ioctl的支持,用戶就可以在用戶程序中使用ioctl函數(shù)控制設(shè)備的I/O通道。

命令的組織是有一些講究的,因?yàn)槲覀円欢ㄒ龅矫詈驮O(shè)備是一一對(duì)應(yīng)的,這樣才不會(huì)將正確的命令發(fā)給錯(cuò)誤的設(shè)備,或者是把錯(cuò)誤的命令發(fā)給正確的設(shè)備,或者是把錯(cuò)誤的命令發(fā)給錯(cuò)誤的設(shè)備。 所以在Linux核心中是這樣定義一個(gè)命令碼的:

設(shè)備類型序列號(hào)方向數(shù)據(jù)尺寸
8bit8bit2bit13~14bit




這樣一來(lái),一個(gè)命令就變成了一個(gè)整數(shù)形式的命令碼。但是命令碼非常的不直觀,所以LinuxKernel中提供了一些宏,這些宏可根據(jù)便于理解的字符串生成命令碼,或者是從命令碼得到一些用戶可以理解的字符串以標(biāo)明這個(gè)命令對(duì)應(yīng)的設(shè)備類型、設(shè)備序列號(hào)、數(shù)據(jù)傳送方向和數(shù)據(jù)傳輸尺寸。 點(diǎn)擊(此處)折疊或打開(kāi)

 /*used to create numbers*/
 #define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
 #define _IOR(type,nr,size)    _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
 #define _IOW(type,nr,size)    _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
 #define _IOWR(type,nr,size)    _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
 #defin e_IOR_BAD(type,nr,size)    _IOC(_IOC_READ,(type),(nr),sizeof(size))
 #define _IOW_BAD(type,nr,size)    _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
 #define _IOWR_BAD(type,nr,size)_IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
 
 #define _IOC(dir,type,nr,size)\
     (((dir)

關(guān)于“Linux系統(tǒng)驅(qū)動(dòng)開(kāi)發(fā)的基礎(chǔ)知識(shí)點(diǎn)有哪些”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對(duì)“Linux系統(tǒng)驅(qū)動(dòng)開(kāi)發(fā)的基礎(chǔ)知識(shí)點(diǎn)有哪些”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI