亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

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

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Linux系統驅動開發的基礎知識點有哪些

發布時間:2022-01-26 16:22:27 來源:億速云 閱讀:233 作者:iii 欄目:開發技術

這篇文章主要介紹了Linux系統驅動開發的基礎知識點有哪些的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Linux系統驅動開發的基礎知識點有哪些文章都會有所收獲,下面我們一起來看看吧。

Linux系統驅動開發的基礎知識點有哪些

基礎性總結

1, linux驅動一般分為3大類:

* 字符設備 * 塊設備 * 網絡設備

2, 開發環境構建:

* 交叉工具鏈構建 * NFS和tftp服務器安裝

3, 驅動開發中設計到的硬件:

* 數字電路知識 * ARM硬件知識 * 熟練使用萬用表和示波器 * 看懂芯片手冊和原理圖

4, linux內核源代碼目錄結構:

Linux系統驅動開發的基礎知識點有哪些 

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

* block/: 部分塊設備驅動程序; 

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

* documentation/: 文檔目錄,沒有內核代碼,只是一套有用的文檔; 

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

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

* include/: include子目錄包括編譯核心所需要的大部分頭文件。與平臺無關的頭文件在 include/linux子目錄下,與 intel cpu相關的頭文件在include/asm-i386子目錄下,而include/scsi目錄則是有關scsi設備的頭文件目錄; 

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

* ipc/: 這個目錄包含核心的進程間通訊的代碼; 

* kernel/: 主要的核心代碼,此目錄下的文件實現了大多數linux系統的內核函數,其中最重要的文件當屬sched.c;同樣,和體系結構相關的代碼在arch/i386/kernel下; 

* lib/: 放置核心的庫代碼; 

* mm/:這個目錄包括所有獨立于 cpu 體系結構的內存管理代碼,如頁式存儲管理內存的分配和釋放等;而和體系結構相關的內存管理代碼則位于arch/i386/mm/下; 

* net/: 核心與網絡相關的代碼; 

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

* security: 主要是一個SELinux的模塊; 

* sound: 常用音頻設備的驅動程序等; 

* usr: 實現了用于打包和壓縮的cpio。

5, 內核的五個子系統:

* 進程調試(SCHED) * 內存管理(MM) * 虛擬文件系統(VFS) * 網絡接口(NET) * 進程間通信(IPC)

6, linux內核的編譯:

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

7, 在linux內核中增加程序

(直接編譯進內核)要完成以下3項工作: * 將編寫的源代碼拷入linux內核源代碼相應目錄 * 在目錄的Kconifg文件中增加關于新源代碼對應項目的編譯配置選項 * 在目錄的Makefile文件中增加對新源代碼的編譯條目

8, linux下C編程的特點:

內核下的Documentation/CodingStyle描述了linux內核對編碼風格的要求。具體要求不一一列舉,以下是要注意的: * 代碼中空格的應用 * 當前函數名: GNU C預定義了兩個標志符保存當前函數的名字,__FUNCTION__保存函數在源碼中的名字,__PRETTY_FUNCTION__保存帶語言特色的名字。 由于C99已經支持__func__宏,在linux編程中應該不要使用__FUNCTION__,應該使用__func__。 *內建函數:不屬于庫函數的其他內建函數的命名通常以__builtin開始。

9,內核模塊

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

 /*
  * 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

注意:標有init的函數在鏈接的時候都放在.init.text段,在.initcall.init中還保存了一份函數指針,初始化的時候內核會通過這些函數指針調用init函數,在初始化完成后釋放init區段。 模塊編譯常用模版:

 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

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

 insmod,lsmod,rmmod
 1

系統調用:

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

flag表示文件打開標志,如:O_RDONLY mode表示文件訪問權限,如:S_IRUSR(用戶可讀),S_IRWXG(組可以讀、寫、執行)

10,linux文件系統與設備驅動的關系

Linux系統驅動開發的基礎知識點有哪些

應用程序和VFS之間的接口是系統調用,而VFS與磁盤文件系統以及普通設備之間的接口是file_operation結構體成員函數。 兩個重要的函數: (1)struct file結構體定義在/linux/include/linux/fs.h(Linux 2.6.11內核)中定義。文件結構體代表一個打開的文件,系統中每個打開的文件在內核空間都有一個關聯的struct file。它由內核在打開文件時創建,并傳遞給在文件上進行操作的任何函數。在文件的所有實例都關閉后,內核釋放這個數據結構。在內核創建和驅動源碼中,struct file的指針通常被命名為file或filp。 在驅動開發中,文件讀/寫模式mode、標志f_flags都是設備驅動關心的內容,而私有數據指針private_data在驅動中被廣泛使用,大多被指向設備驅動自定義的用于描述設備的結構體。驅動程序中常用如下類似的代碼來檢測用戶打開文件的讀寫方式:

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

下面的代碼可用于判斷以阻塞還是非阻塞方式打開設備文件:

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

(2)struct inode結構體定義在linux/fs.h中

11,devfs、sysfs、udev三者的關系:

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

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

12,linux設備模型:

Linux系統驅動開發的基礎知識點有哪些

在linux內核中,分別使用bus_type,device_driver,device來描述總線、驅動和設備,這3個結構體定義于include/linux/device.h頭文件中。驅動和設備正是通過bus_type中的match()函數來配對的。

13, 重要結構體解析

(1)cdev結構體 在linux2.6內核中,使用cdev結構體描述一個字符設備,定義如下:

 struct cdev{
     struct kobject kobj;//內嵌的kobject對象
     struct module *owner;//所屬模塊
     struct file_operations *ops;//文件操作結構體
     struct list_head list;
     dev_t dev;//設備號,長度為32位,其中高12為主設備號,低20位為此設備號
     unsigned int count;
 };12345678

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

14, 字符設備驅動程序設計基礎

主設備號和次設備號(二者一起為設備號): 一個字符設備或塊設備都有一個主設備號和一個次設備號。主設備號用來標識與設備文件相連的驅動程序,用來反映設備類型。次設備號被驅動程序用來辨別操作的是哪個設備,用來區分同類型的設備。 linux內核中,設備號用dev_t來描述,2.6.28中定義如下:

 typedef u_long dev_t;1

在32位機中是4個字節,高12位表示主設備號,低12位表示次設備號。

可以使用下列宏從dev_t中獲得主次設備號:也可以使用下列宏通過主次設備號生成dev_t:

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

分配設備號(兩種方法): (1)靜態申請:

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

(2)動態分配:

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

注銷設備號:

 void unregister_chrdev_region(dev_t from,unsigned count);
 1

創建設備文件: 利用cat/proc/devices查看申請到的設備名,設備號。 (1)使用mknod手工創建:mknod filename type major minor (2)自動創建; 利用udev(mdev)來實現設備文件的自動創建,首先應保證支持udev(mdev),由busybox配置。在驅動初始化代碼里調用class_create為該設備創建一個class,再為每個設備調用device_create創建對應的設備。

15, 字符設備驅動程序設計

設備注冊: 字符設備的注冊分為三個步驟: (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

設備操作的實現: file_operations函數集的實現。

 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

特別注意:驅動程序應用程序的數據交換: 驅動程序和應用程序的數據交換是非常重要的。file_operations中的read()和write()函數,就是用來在驅動程序和應用程序間交換數據的。通過數據交換,驅動程序和應用程序可以彼此了解對方的情況。但是驅動程序和應用程序屬于不同的地址空間。驅動程序不能直接訪問應用程序的地址空間;同樣應用程序也不能直接訪問驅動程序的地址空間,否則會破壞彼此空間中的數據,從而造成系統崩潰,或者數據損壞。安全的方法是使用內核提供的專用函數,完成數據在應用程序空間和驅動程序空間的交換。這些函數對用戶程序傳過來的指針進行了嚴格的檢查和必要的轉換,從而保證用戶程序與驅動程序交換數據的安全性。這些函數有:

 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

設備注銷:

 void cdev_del(struct cdev *p);
 1

16,ioctl函數說明

ioctl是設備驅動程序中對設備的I/O通道進行管理的函數。所謂對I/O通道進行管理,就是對設備的一些特性進行控制,例如串口的傳輸波特率、馬達的轉速等等。它的調用個數如下:

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

其中fd就是用戶程序打開設備時使用open函數返回的文件標示符,cmd就是用戶程序對設備的控制命令,后面的省略號是一些補充參數,有或沒有是和cmd的意義相關的。 ioctl函數是文件結構中的一個屬性分量,就是說如果你的驅動程序提供了對ioctl的支持,用戶就可以在用戶程序中使用ioctl函數控制設備的I/O通道。

命令的組織是有一些講究的,因為我們一定要做到命令和設備是一一對應的,這樣才不會將正確的命令發給錯誤的設備,或者是把錯誤的命令發給正確的設備,或者是把錯誤的命令發給錯誤的設備。 所以在Linux核心中是這樣定義一個命令碼的:

設備類型序列號方向數據尺寸
8bit8bit2bit13~14bit




這樣一來,一個命令就變成了一個整數形式的命令碼。但是命令碼非常的不直觀,所以LinuxKernel中提供了一些宏,這些宏可根據便于理解的字符串生成命令碼,或者是從命令碼得到一些用戶可以理解的字符串以標明這個命令對應的設備類型、設備序列號、數據傳送方向和數據傳輸尺寸。 點擊(此處)折疊或打開

 /*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)

關于“Linux系統驅動開發的基礎知識點有哪些”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“Linux系統驅動開發的基礎知識點有哪些”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

神池县| 贡觉县| 白玉县| 长武县| 玉田县| 秦安县| 永泰县| 建昌县| 金昌市| 望奎县| 玛曲县| 大洼县| 五莲县| 香格里拉县| 宣武区| 深圳市| 东乡族自治县| 家居| 金门县| 当涂县| 原阳县| 龙陵县| 新野县| 枝江市| 磐安县| 郑州市| 阿巴嘎旗| 宜兰市| 巴马| 远安县| 屯留县| 延川县| 泸溪县| 武强县| 若羌县| 临沭县| 吉木萨尔县| 台江县| 高碑店市| 北流市| 海晏县|