




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、Linux 設(shè)備驅(qū)動(dòng)設(shè)計(jì) 梁紅波kelvinema-,1,實(shí)操應(yīng)用,設(shè)備驅(qū)動(dòng)概述,設(shè)備由兩部分組成,一個(gè)是被稱為控制器的電器部分,另一個(gè)是機(jī)械部分。 一組寄存器組被賦予到各個(gè)控制器。I/O端口包含4組寄存器,即狀態(tài)寄存器,控制寄存器,數(shù)據(jù)輸入寄存器,數(shù)據(jù)輸出寄存器。 狀態(tài)寄存器擁有可以被CPU讀取的(狀態(tài))位,用來 指示當(dāng)前命令是否執(zhí)行完畢,或者字節(jié)是否可以被讀出或?qū)懭?,以及任何錯(cuò)誤提示。 控制寄存器則用于啟動(dòng)一條命令(指令)或者改變?cè)O(shè)備的(工作)模式。 數(shù)據(jù)輸入寄存器用于獲取輸入的數(shù)據(jù)。 數(shù)據(jù)輸出寄存器則向CPU發(fā)送結(jié)果。,2,實(shí)操應(yīng)用,設(shè)備驅(qū)動(dòng)概述,操作系統(tǒng)是通過
2、各種驅(qū)動(dòng)程序來駕馭硬件設(shè)備,它為用戶屏蔽了各種各樣的設(shè)備。 設(shè)備驅(qū)動(dòng)程序是操作系統(tǒng)內(nèi)核和機(jī)器硬件之間的接口,系統(tǒng)調(diào)用是操作系統(tǒng)內(nèi)核和應(yīng)用程序之間的接口。 在應(yīng)用程序看來,硬件設(shè)備只是一個(gè)設(shè)備文件, 應(yīng)用程序可以象操作普通文件一樣對(duì)硬件設(shè)備進(jìn)行操作.,3,實(shí)操應(yīng)用,設(shè)備驅(qū)動(dòng)概述,驅(qū)動(dòng)完成以下的功能: 對(duì)設(shè)備初始化和釋放. 把數(shù)據(jù)從內(nèi)核傳送到硬件和從硬件讀取數(shù)據(jù). 讀取應(yīng)用程序傳送給設(shè)備文件的數(shù)據(jù)和回送應(yīng)用程序請(qǐng)求的數(shù)據(jù). 檢測和處理設(shè)備出現(xiàn)的錯(cuò)誤.,4,實(shí)操應(yīng)用,設(shè)備驅(qū)動(dòng)概述,無操作系統(tǒng)的設(shè)備驅(qū)動(dòng) 有操作系統(tǒng)的設(shè)備驅(qū)動(dòng),Embedded OS,Hardware,不帶操作系統(tǒng)軟件結(jié)構(gòu) 帶操作系統(tǒng)軟
3、件結(jié)構(gòu),Driver,5,實(shí)操應(yīng)用,Linux設(shè)備驅(qū)動(dòng),6,實(shí)操應(yīng)用,Linux設(shè)備驅(qū)動(dòng),用戶級(jí)的程序使用內(nèi)核提供的標(biāo)準(zhǔn)系統(tǒng)調(diào)用來與內(nèi)核通訊,這些系統(tǒng)調(diào)用有:open(), read(), write(), ioctl(), close() 等等。 Linux的內(nèi)核是映射到每一個(gè)進(jìn)程的高1G空間。每一個(gè)用戶進(jìn)程運(yùn)行時(shí)都好像有一份內(nèi)核的拷貝,每當(dāng)用戶進(jìn)程使用系統(tǒng)調(diào)用時(shí),都自動(dòng)地將運(yùn)行模式從用戶級(jí)轉(zhuǎn)為內(nèi)核級(jí),此時(shí)進(jìn)程在內(nèi)核的地址空間中運(yùn)行。,7,實(shí)操應(yīng)用,Linux設(shè)備驅(qū)動(dòng),Linux內(nèi)核使用“設(shè)備無關(guān)”的I/O子系統(tǒng)來為所有的設(shè)備服務(wù)。 每個(gè)設(shè)備都提供標(biāo)準(zhǔn)接口給內(nèi)核,盡可能地隱藏了自己的特性。
4、用戶程序使用一些基本的系統(tǒng)調(diào)用從設(shè)備讀取數(shù)據(jù)并且將它們存入緩沖的例子。我們可以看到,每當(dāng)一個(gè)系統(tǒng)調(diào)用被使用時(shí),內(nèi)核就轉(zhuǎn)到相應(yīng)的設(shè)備驅(qū)動(dòng)例程來操縱硬件。,8,實(shí)操應(yīng)用,Linux設(shè)備驅(qū)動(dòng),Linux操作系統(tǒng)把設(shè)備納入文件系統(tǒng)的范疇來管理。 每個(gè)設(shè)備在Linux系統(tǒng)上看起來都像一個(gè)文件,它們存放在/dev目錄中,稱為設(shè)備節(jié)點(diǎn)。 對(duì)文件操作的系統(tǒng)調(diào)用大都適用于設(shè)備文件。,9,實(shí)操應(yīng)用,Linux設(shè)備驅(qū)動(dòng),Linux下設(shè)備的屬性 設(shè)備的類型:字符設(shè)備、塊設(shè)備、網(wǎng)絡(luò)設(shè)備 主設(shè)備號(hào):標(biāo)識(shí)設(shè)備對(duì)應(yīng)的驅(qū)動(dòng)程序。一般“一個(gè)主設(shè)備號(hào)對(duì)應(yīng)一個(gè)驅(qū)動(dòng)程序” 次設(shè)備號(hào):每個(gè)驅(qū)動(dòng)程序負(fù)責(zé)管理它所驅(qū)動(dòng)的幾個(gè)硬件實(shí)例,這些硬件
5、實(shí)例則由次設(shè)備號(hào)來表示。同一驅(qū)動(dòng)下的實(shí)例編號(hào),用于確定設(shè)備文件所指的設(shè)備。 可通過ls l “設(shè)備文件名”命令查看設(shè)備的主次設(shè)備號(hào),以及設(shè)備的類型。,10,實(shí)操應(yīng)用,Linux設(shè)備驅(qū)動(dòng),Linux設(shè)備驅(qū)動(dòng)程序是一組由內(nèi)核中的相關(guān)子例程和數(shù)據(jù)組成的I/O設(shè)備軟件接口。 每當(dāng)用戶程序要訪問某個(gè)設(shè)備時(shí),它就通過系統(tǒng)調(diào)用,讓內(nèi)核代替它調(diào)用相應(yīng)的驅(qū)動(dòng)例程。這就使得控制從用戶進(jìn)程轉(zhuǎn)移到了驅(qū)動(dòng)例程,當(dāng)驅(qū)動(dòng)例程完成后,控制又被返回至用戶進(jìn)程。,11,實(shí)操應(yīng)用,一些重要的數(shù)據(jù)結(jié)構(gòu),大部分驅(qū)動(dòng)程序涉及三個(gè)重要的內(nèi)核數(shù)據(jù)結(jié)構(gòu): 文件操作file_operations結(jié)構(gòu)體 文件對(duì)象file結(jié)構(gòu)體 索引節(jié)點(diǎn)inode
6、結(jié)構(gòu)體,12,實(shí)操應(yīng)用,一些重要的數(shù)據(jù)結(jié)構(gòu),文件操作結(jié)構(gòu)體file_operations 結(jié)構(gòu)體file_operations在頭文件 linux/fs.h中定義,用來存儲(chǔ)驅(qū)動(dòng)內(nèi)核模塊提供的對(duì)設(shè)備進(jìn)行各種操作的函數(shù)的指針。 結(jié)構(gòu)體的每個(gè)域都對(duì)應(yīng)著驅(qū)動(dòng)模塊用來處理某個(gè)被請(qǐng)求的事務(wù)的函數(shù)的地址。 struct file_operations struct module *owner; ssize_t(*read) (struct file *, char _user *, size_t, loff_t *); ssize_t(*write) (struct file *, const char _
7、user *, size_t, loff_t *); 。 ,13,實(shí)操應(yīng)用,一些重要的數(shù)據(jù)結(jié)構(gòu),file_operations重要的成員 Struct module *owner ,指向擁有該結(jié)構(gòu)體的模塊的指針。內(nèi)核使用該指針維護(hù)模塊使用計(jì)數(shù)。 方法llseek用來修改文件的當(dāng)前讀寫位置,把新位置作為返回值返回。loff_t是在LINUX中定義的長偏移量 方法read用來從設(shè)備中讀取數(shù)據(jù)。非負(fù)返回值表示成功讀取的直接數(shù)。 方法write向設(shè)備發(fā)送數(shù)據(jù)。 方法ioctl提供一種執(zhí)行設(shè)備特定命令的方法。,14,實(shí)操應(yīng)用,一些重要的數(shù)據(jù)結(jié)構(gòu),file_operations重要的成員 unsigned
8、 int (*poll) (struct file *, struct poll_table_struct *); 系統(tǒng)調(diào)用select和poll的后端實(shí)現(xiàn),用這兩個(gè)系統(tǒng)調(diào)用來查詢 設(shè)備是否可讀寫,或是否處于某種狀態(tài)。如果poll為空,則驅(qū)動(dòng)設(shè)備會(huì)被認(rèn)為即可讀又可寫,返回值是一個(gè)狀態(tài)掩碼。 int (*mmap) (struct file *, struct vm_area_struct *);將設(shè)備內(nèi)存映射到進(jìn)程地址空間,15,實(shí)操應(yīng)用,一些重要的數(shù)據(jù)結(jié)構(gòu),file_operations重要的成員 驅(qū)動(dòng)內(nèi)核模塊是不需要實(shí)現(xiàn)每個(gè)函數(shù)的。相對(duì)應(yīng)的file_operations的項(xiàng)就為 NULL。
9、 Gcc的語法擴(kuò)展,使得可以定義該結(jié)構(gòu)體: struct file_operations fops = read: device_read, write: device_write, open: device_open, release: device_release ; 沒有顯示聲明的結(jié)構(gòu)體成員都被gcc初始化為NULL。,16,實(shí)操應(yīng)用,一些重要的數(shù)據(jù)結(jié)構(gòu),file_operations重要的成員 標(biāo)準(zhǔn)C的標(biāo)記化結(jié)構(gòu)體的初始化方法: struct file_operations fops = .read = device_read, .write = device_write, .open
10、 = device_open, .release = device_release ; 推薦使用該方法,提高移植性,方法允許對(duì)結(jié)構(gòu)體成員進(jìn)行重新排列。沒有顯示聲明的結(jié)構(gòu)體成員同樣都被gcc初始化為NULL。 指向結(jié)構(gòu)體file_operations的指針通常命名為fops。,17,實(shí)操應(yīng)用,一些重要的數(shù)據(jù)結(jié)構(gòu),文件對(duì)象file結(jié)構(gòu)體 文件對(duì)象file代表著一個(gè)打開的文件。進(jìn)程通過文件描述符fd與已打開文件的file結(jié)構(gòu)相聯(lián)系。進(jìn)程通過它對(duì)文件的線性邏輯空間進(jìn)行操作。例如:file-f_op-read(); Struct file 在中定義。 指向結(jié)構(gòu)體struct file的指針通常命名為fi
11、lp,或者file。建議使用文件指針filp。,18,實(shí)操應(yīng)用,一些重要的數(shù)據(jù)結(jié)構(gòu),文件對(duì)象file結(jié)構(gòu)體的成員 Struct file_operations *f_op; 與文件相關(guān)的操作結(jié)構(gòu)體指針。與文件相關(guān)的操作是在打開文件的時(shí)候確定下來的,也就是確定該指針的值。可在需要的時(shí)候,改變指針?biāo)赶虻奈募僮鹘Y(jié)構(gòu)體。用C語言實(shí)現(xiàn)面向?qū)ο缶幊痰姆椒ㄖ剌d。 其他成員可先忽略,后面具體實(shí)例分析。因?yàn)樵O(shè)備驅(qū)動(dòng)模塊并不自己直接填充結(jié)構(gòu)體 file,只是使用file中的數(shù)據(jù)。,19,實(shí)操應(yīng)用,一些重要的數(shù)據(jù)結(jié)構(gòu),索引節(jié)點(diǎn)inode結(jié)構(gòu) 文件打開,在內(nèi)存建立副本后,由唯一的索引節(jié)點(diǎn)inode描述。 與fil
12、e結(jié)構(gòu)不同。 file結(jié)構(gòu)是進(jìn)程使用的結(jié)構(gòu),進(jìn)程每打開一個(gè)文件,就建立一個(gè)file結(jié)構(gòu)。不同的進(jìn)程打開同一個(gè)文件,建立不同的file結(jié)構(gòu)。 Inode結(jié)構(gòu)是內(nèi)核使用的結(jié)構(gòu),文件在內(nèi)存建立副本,就建立一個(gè)inode結(jié)構(gòu)來描述。一個(gè)文件在內(nèi)存里面只有一個(gè)inode結(jié)構(gòu)對(duì)應(yīng)。,20,實(shí)操應(yīng)用,一些重要的數(shù)據(jù)結(jié)構(gòu),索引節(jié)點(diǎn)inode結(jié)構(gòu) Inode結(jié)構(gòu)包含大量描述文件信息的成員變量。 但是對(duì)于描述設(shè)備文件的inode,跟設(shè)備驅(qū)動(dòng)有關(guān)的成員只有兩個(gè)。 Dev_t i_rdev; 包含真正的設(shè)備編號(hào)。 Struct cdev *i_cdev; 指向cdev結(jié)構(gòu)體的指針。cdev是表示字符設(shè)備的內(nèi)核數(shù)據(jù)結(jié)構(gòu)
13、。 從inode中獲得主設(shè)備號(hào)和次設(shè)備號(hào)的宏: Unsigned int iminor(struct inode *inode); Unsigned int imajor(struct inode *inode);,21,實(shí)操應(yīng)用,Linux設(shè)備驅(qū)動(dòng),主設(shè)備號(hào)和次設(shè)備號(hào)的內(nèi)部表達(dá): Dev_t類型用于保存設(shè)備號(hào),稱為設(shè)備編號(hào)。/linux/types.h文件中定義。 目前設(shè)備編號(hào)dev_t是一個(gè)32位的整數(shù),其中12位表示主設(shè)備號(hào),20位表示次設(shè)備號(hào)。 通過設(shè)備編號(hào)獲取主次設(shè)備號(hào): MAJOR(dev_t dev); MINOR(dev_t dev); 通過主次設(shè)備號(hào)合成設(shè)備編號(hào): MKDEV
14、(int major, int minor); Dev_t格式以后可能會(huì)發(fā)生變化,但只要使用這些宏,就可保證設(shè)備驅(qū)動(dòng)程序的正確性。,22,實(shí)操應(yīng)用,分配和釋放字符設(shè)備號(hào),編寫驅(qū)動(dòng)程序要做的第一件事,為字符設(shè)備獲取一個(gè)設(shè)備號(hào)。 事先知道所需要的設(shè)備編號(hào)(主設(shè)備號(hào))的情況: int register_chrdev_region(dev_t first, unsigned count, const char *name) first是要分配的起始設(shè)備編號(hào)值。 first的次設(shè)備號(hào)通常設(shè)置為0。 Count 所請(qǐng)求的連續(xù)設(shè)備編號(hào)的個(gè)數(shù)。 Name設(shè)備名稱,指和該編號(hào)范圍建立關(guān)系的設(shè)備。 分配成功返回0
15、。,23,實(shí)操應(yīng)用,分配和釋放字符設(shè)備號(hào),動(dòng)態(tài)分配設(shè)備編號(hào)(主要是主設(shè)備號(hào)) int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name) dev 是一個(gè)僅用于輸出的參數(shù), 它在函數(shù)成功完成時(shí)保存已分配范圍的第一個(gè)編號(hào)。 baseminor 應(yīng)當(dāng)是請(qǐng)求的第一個(gè)要用的次設(shè)備號(hào),它常常是 0. count 和 name 參數(shù)跟request_chrdev_region 的一樣.,24,實(shí)操應(yīng)用,分配和釋放字符設(shè)備號(hào),不再使用時(shí),釋放這些設(shè)備編號(hào)。使用以下函數(shù): void unregi
16、ster_chrdev_region(dev_t from, unsigned count) 在模塊的卸載函數(shù)中調(diào)用該函數(shù)。,25,實(shí)操應(yīng)用,分配和釋放字符設(shè)備號(hào),新驅(qū)動(dòng)程序,建議使用動(dòng)態(tài)分配機(jī)制獲取主設(shè)備號(hào),也就是使用alloc_chrdev_region()。 動(dòng)態(tài)分配導(dǎo)致無法預(yù)先創(chuàng)建設(shè)備節(jié)點(diǎn)。 可在分配設(shè)備號(hào)后,從/proc/devices文件中獲取。 為了加載后自動(dòng)創(chuàng)建設(shè)備文件,可以通過編寫內(nèi)核模塊加載腳本實(shí)現(xiàn)。,26,實(shí)操應(yīng)用,字符設(shè)備的注冊(cè),內(nèi)核內(nèi)部使用struct cdev結(jié)構(gòu)表示字符設(shè)備。編寫設(shè)備驅(qū)動(dòng)的第二步就是注冊(cè)該設(shè)備。 包含頭文件。 獲取一個(gè)獨(dú)立的cdev結(jié)構(gòu): stru
17、ct cdev *my_cdev = cdev_alloc(); 調(diào)用cdev_init初始化cdev結(jié)構(gòu)體 void cdev_init(struct cdev *cdev, struct file_operations *fops); 初始化該設(shè)備的所有者字段: dev-cdev.owner = THIS_MODULE; 初始化該設(shè)備的可用操作集: dev-cdev.ops = ,27,實(shí)操應(yīng)用,字符設(shè)備的注冊(cè),編寫設(shè)備驅(qū)動(dòng)的第二步就是注冊(cè)該設(shè)備。 cdev 結(jié)構(gòu)已建立和初始化, 最后通過cdev_add函數(shù)把它告訴內(nèi)核: int cdev_add(struct cdev *dev, de
18、v_t num, unsigned int count); dev 是要添加的設(shè)備的 cdev 結(jié)構(gòu), num 是這個(gè)設(shè)備對(duì)應(yīng)的第一個(gè)設(shè)備編號(hào), count 是應(yīng)當(dāng)關(guān)聯(lián)到設(shè)備的設(shè)備號(hào)的數(shù)目. 卸載字符設(shè)備時(shí),調(diào)用相反的動(dòng)作函數(shù): void cdev_del(struct cdev *dev);,28,實(shí)操應(yīng)用,設(shè)備的注冊(cè),早期方法: 內(nèi)核中仍有許多字符驅(qū)動(dòng)不使用剛剛描述過的cdev 接口。沒有更新到 2.6 內(nèi)核接口的老代碼。 注冊(cè)一個(gè)字符設(shè)備的早期方法: int register_chrdev(unsigned int major, const char *name, struct file
19、_operations *fops); major 是給定的主設(shè)備號(hào)。為0代表什么? name 是驅(qū)動(dòng)的名字(將出現(xiàn)在 /proc/devices), fops 是設(shè)備驅(qū)動(dòng)的file_operations 結(jié)構(gòu)。 register_chrdev 將給設(shè)備分配 0 - 255 的次設(shè)備號(hào), 并且為每一個(gè)建立一個(gè)缺省的 cdev 結(jié)構(gòu)。 從系統(tǒng)中卸載字符設(shè)備的函數(shù): int unregister_chrdev(unsigned int major, const char *name);,29,實(shí)操應(yīng)用,Open方法,編寫字符設(shè)備驅(qū)動(dòng)的第三步:定義設(shè)備驅(qū)動(dòng)與文件系統(tǒng)的接口,file_operatio
20、n結(jié)構(gòu)體的函數(shù)定義。 open 方法 int (*open)(struct inode *inode, struct file *filp); 驅(qū)動(dòng)程序提供open 方法,讓用戶進(jìn)程使用設(shè)備之前,進(jìn)行一些初始化的工作。 檢查設(shè)備特定的錯(cuò)誤。 如果第一次打開設(shè)備, 則初始化設(shè)備。 如果需要, 更新 f_op 指針,更換操作方法集。 分配并填充要放進(jìn) filp-private_data 的任何數(shù)據(jù)結(jié)構(gòu)。,30,實(shí)操應(yīng)用,Open方法,對(duì)于設(shè)備文件,inode 參數(shù)只有兩個(gè)參數(shù)對(duì)設(shè)備驅(qū)動(dòng)有用的。 Dev_t i_rdev; 包含真正的設(shè)備編號(hào)。 Struct cdev *i_cdev; 指向cdev
21、結(jié)構(gòu)體的指針。 i_cdev里面包含我們之前建立的 cdev 結(jié)構(gòu)。但是有時(shí)候,我們需要的是包含 cdev 結(jié)構(gòu)的描述設(shè)備的結(jié)構(gòu)。 使用通過成員地址獲取結(jié)構(gòu)體地址的宏container_of,在 中定義: container_of(pointer, container_type, container_field); 這個(gè)宏使用一個(gè)指向 container_field 類型的成員的指針, 它在一個(gè) container_type 類型的結(jié)構(gòu)中,宏通過分析他們關(guān)系,返回指向包含該成員的結(jié)構(gòu)體指針.,31,實(shí)操應(yīng)用,Open方法,在 myscull_open, 這個(gè)宏用來找到適當(dāng)?shù)脑O(shè)備結(jié)構(gòu): dev
22、= container_of(inode-i_cdev, struct scull_dev, cdev); 找到 myscull_dev 結(jié)構(gòu)后, scull 在filp-private_data 中存儲(chǔ)其指針, 為以后存取使用. filp-private_data = dev;,32,實(shí)操應(yīng)用,release 方法,release 方法做open相反的工作 釋放 open 分配給filp-private_data的內(nèi)存空間。 在最后一次的關(guān)閉操作時(shí),關(guān)閉設(shè)備。 不是每個(gè) close 系統(tǒng)調(diào)用引起調(diào)用 release 方法。,33,實(shí)操應(yīng)用,Read和Write方法,Read的任務(wù), 就是從設(shè)
23、備拷貝數(shù)據(jù)到用戶空間。 Write的任務(wù),則從用戶空間拷貝數(shù)據(jù)到設(shè)備。 ssize_t read(struct file *filp, char _user *buff, size_t count, loff_t *offp); ssize_t write(struct file *filp, const char _user *buff, size_t count, loff_t *offp); filp 是文件對(duì)象指針, count 是請(qǐng)求的傳輸數(shù)據(jù)大小. buff 參數(shù)對(duì)write來說是指向持有被寫入數(shù)據(jù)的緩存, 對(duì)read則是放入新數(shù)據(jù)的空緩存. offp 是指向一個(gè)“l(fā)ong off
24、set type”的指針, 它指出用戶正在存取的文件位置. 返回值是“signed size type”類型;,34,實(shí)操應(yīng)用,Read和Write方法,read 和 write 方法的 buff 參數(shù)是用戶空間指針,不能被內(nèi)核代碼直接解引用。_user字符串只是形式上的說明,表明是用戶空間地址。 驅(qū)動(dòng)必須能夠存取用戶空間緩存以完成它的工作。內(nèi)核如何解決這個(gè)問題? 為安全起見,內(nèi)核提供專用的函數(shù)來完成對(duì)用戶空間的存取。這些專用函數(shù)在中聲明。 unsigned long copy_to_user(void _user *to,const void *from,unsigned long coun
25、t); unsigned long copy_from_user(void *to,const void _user *from,unsigned long count); 大多數(shù)讀寫函數(shù)都會(huì)調(diào)用這兩個(gè)函數(shù),用于跟應(yīng)用程序空間交流信息。,35,實(shí)操應(yīng)用,Read和Write方法,典型的Read函數(shù)對(duì)參數(shù)的使用。,36,實(shí)操應(yīng)用,llseek函數(shù),llseek函數(shù)用于對(duì)設(shè)備文件訪問定位。 驅(qū)動(dòng)接口loff_t (*llseek) (struct file *, loff_t, int); 庫函數(shù)off_t lseek(int filedes, off_t offset, int whence);
26、參數(shù) offset 的含義取決于參數(shù) whence: 如果 whence 是 SEEK_SET,文件偏移量將被設(shè)置為 offset。 如果 whence 是 SEEK_CUR,文件偏移量將被設(shè)置為 cfo 加上 offset,offset 可以為正也可以為負(fù)。 如果 whence 是 SEEK_END,文件偏移量將被設(shè)置為文件長度加上 offset, offset 可以為正也可以為負(fù)。 SEEK_SET、SEEK_CUR 和 SEEK_END 是 System V 引入的,是 0、1 和 2。,37,實(shí)操應(yīng)用,ioctl,進(jìn)行超出簡單的數(shù)據(jù)傳輸之外的操作,進(jìn)行各種硬件控制操作. ioctl 方
27、法和用戶空間版本不同的原型: int (*ioctl) (struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg) 不管可選的參數(shù)arg是否由用戶給定為一個(gè)整數(shù)或一個(gè)指針,它都以一個(gè)unsigned long的形式傳遞。 返回值 POSIX 標(biāo)準(zhǔn)規(guī)定:如果使用了不合適的 ioctl 命令號(hào),應(yīng)當(dāng)返回-ENOTTY 。這個(gè)錯(cuò)誤碼被 C 庫解釋為“不合適的設(shè)備 ioctl。 -EINVAL也是相當(dāng)普遍的。,38,實(shí)操應(yīng)用,結(jié)構(gòu)化設(shè)備驅(qū)動(dòng)程序,設(shè)備結(jié)構(gòu)體 把與某設(shè)備相關(guān)的所有內(nèi)容定義為一個(gè)設(shè)備結(jié)構(gòu)體 其中包
28、括設(shè)備驅(qū)動(dòng)涉及的硬件資源、全局軟件資源、控制(自旋鎖、互斥鎖、等待隊(duì)列、定時(shí)器等) 在涉及設(shè)備的操作時(shí),就僅僅操作這個(gè)結(jié)構(gòu)體,39,實(shí)操應(yīng)用,Linux設(shè)備驅(qū)動(dòng)的并發(fā)控制,40,實(shí)操應(yīng)用,設(shè)備驅(qū)動(dòng)的并發(fā)控制,在驅(qū)動(dòng)程序中,當(dāng)多個(gè)線程同時(shí)訪問相同的資源時(shí),可能會(huì)引發(fā)“競態(tài)”,必須對(duì)共享資源進(jìn)行并發(fā)控制。 并發(fā)和競態(tài)廣泛存在。 并發(fā)控制的目的: 使得線程訪問共享資源的操作是原子操作。 原子操作: 在執(zhí)行過程中不會(huì)被別的代碼路徑所中斷的操作。 驅(qū)動(dòng)程序中的全局變量是一種典型的共享資源。,41,實(shí)操應(yīng)用,考慮一個(gè)非常簡單的共享資源的例子:一個(gè)全局整型變量和一個(gè)簡單的臨界區(qū),其中的操作僅僅是將整型變量的
29、值增加1: i+ 該操作可以轉(zhuǎn)化成下面三條機(jī)器指令序列: 得到當(dāng)前變量i的值并拷貝到一個(gè)寄存器中 將寄存器中的值加1 把i的新值寫回到內(nèi)存中,原子操作,42,實(shí)操應(yīng)用,原子操作,內(nèi)核任務(wù)1 內(nèi)核任務(wù)2 獲得i(1) - 增加 i(1-2) - 寫回 i(2) - 獲得 i(2) 增加 i(2-3) 寫回 i(3),內(nèi)核任務(wù)1 內(nèi)核任務(wù)2 獲得 i(1) - 增加 i(1-2) - - 獲得 i(1) - 增加 i(1-2) - 寫回 i(2) 寫回 i(2) -,可能的實(shí)際執(zhí)行結(jié)果:,期望的結(jié)果,43,實(shí)操應(yīng)用,Linux內(nèi)核的并發(fā)控制,在內(nèi)核空間的內(nèi)核任務(wù)需要考慮同步 內(nèi)核空間中的共享數(shù)據(jù)對(duì)
30、內(nèi)核中的所有任務(wù)可見,所以當(dāng)在內(nèi)核中訪問數(shù)據(jù)時(shí),就必須考慮是否會(huì)有其他內(nèi)核任務(wù)并發(fā)訪問的可能、是否會(huì)產(chǎn)生競爭條件、是否需要對(duì)數(shù)據(jù)同步。,44,實(shí)操應(yīng)用,確定保護(hù)對(duì)象 找出哪些數(shù)據(jù)需要保護(hù)是關(guān)鍵所在 內(nèi)核任務(wù)的局部數(shù)據(jù)僅僅被它本身訪問,顯然不需要保護(hù)。 如果數(shù)據(jù)只會(huì)被特定的進(jìn)程訪問,也不需加鎖 大多數(shù)內(nèi)核數(shù)據(jù)結(jié)構(gòu)都需要加鎖:若有其它內(nèi)核任務(wù)可以訪問這些數(shù)據(jù),那么就給這些數(shù)據(jù)加上某種形式的鎖;若任何其它東西能看到它,那么就要鎖住它。,Linux內(nèi)核的并發(fā)控制,45,實(shí)操應(yīng)用,Linux內(nèi)核的并發(fā)控制,并發(fā)控制的機(jī)制 中斷屏蔽,原子數(shù)操作,自旋鎖和信號(hào)量都是解決并發(fā)問題的機(jī)制。 中斷屏蔽很少被單獨(dú)使
31、用,原子操作只能針對(duì)整數(shù)來進(jìn)行。因此自旋鎖和信號(hào)量應(yīng)用最為廣泛。,46,實(shí)操應(yīng)用,中斷屏蔽,單CPU系統(tǒng)中,避免竟態(tài)的一種簡單方式 保證正在執(zhí)行的內(nèi)核執(zhí)行路徑不被中斷處理程序所搶占,防止竟態(tài)條件的發(fā)生。 Local_irq_disable()/關(guān)中斷 Critical section /臨界區(qū) Local_irq_enable() /開中斷 中斷對(duì)內(nèi)核非常重要,長時(shí)間屏蔽中斷非常危險(xiǎn)! 只適合短時(shí)間的關(guān)閉 對(duì)SMP多CPU引發(fā)的竟態(tài)無效,47,實(shí)操應(yīng)用,鎖機(jī)制可以避免競爭狀態(tài)正如門鎖和門一樣,門后的房間可想象成一個(gè)臨界區(qū)。 在一段時(shí)間內(nèi),房間里只能有一個(gè)內(nèi)核任務(wù)存在,當(dāng)一個(gè)任務(wù)進(jìn)入房間后,它會(huì)
32、鎖住身后的房門;當(dāng)它結(jié)束對(duì)共享數(shù)據(jù)的操作后,就會(huì)走出房間,打開門鎖。如果另一個(gè)任務(wù)在房門上鎖時(shí)來了,那么它就必須等待房間內(nèi)的任務(wù)出來并打開門鎖后,才能進(jìn)入房間。,加鎖機(jī)制,48,實(shí)操應(yīng)用,任何要訪問臨界資源的代碼首先都需要占住相應(yīng)的鎖,這樣該鎖就能阻止來自其它內(nèi)核任務(wù)的并發(fā)訪問:,加鎖機(jī)制,49,實(shí)操應(yīng)用,原子數(shù)操作,整型原子數(shù)操作 原子變量初始化 atomic_t test = ATOMIC_INIT(i); 設(shè)置原子變量的值 void atomic_set(atomic_t *v, int i) 獲得原子變量的值 atomic_read(v) 原子變量加 void atomic_add(i
33、nt i, atomic_t *v) 原子變量減 void atomic_sub(int i, atomic_t *v),50,實(shí)操應(yīng)用,原子數(shù)操作,整型原子數(shù)操作 原子變量的自增操作 void atomic_inc(atomic_t *v) 原子變量的自減操作 void atomic_dec(atomic_t *v) 操作并測試 (測試其是否為0,0為true,否為false) atomic_inc_and_test(atomic_t *v) atomic_dec_and_test(atomic_t *v) int atomic_sub_and_test(int i, atomic_t *v
34、) 操作并返回 (返回新值) int atomic_add_return(int i, atomic_t *v) int atomic_sub_return(int i, atomic_t *v),51,實(shí)操應(yīng)用,原子數(shù)操作,原子位操作 設(shè)置位 void set_bit(int nr, volatile unsigned long * addr) 清除位 void clear_bit(int nr, volatile unsigned long * addr) 改變位 change_bit(nr,p) 測試位 test_bit(int nr, const volatile unsigned l
35、ong * p) 測試并操作位 test_and_set_bit(nr,p),52,實(shí)操應(yīng)用,自旋鎖,自旋鎖是專為防止多處理器并發(fā)而引入的一種鎖,它在內(nèi)核中大量應(yīng)用于中斷處理等部分。而對(duì)于單處理器來說,防止中斷處理中的并發(fā)可簡單采用關(guān)閉中斷的方式,不需要自旋鎖。 自旋鎖最多只能被一個(gè)內(nèi)核任務(wù)持有,若一個(gè)內(nèi)核任務(wù)試圖請(qǐng)求一個(gè)已被持有的自旋鎖,那么這個(gè)任務(wù)就會(huì)一直進(jìn)行忙循環(huán),也就是旋轉(zhuǎn),等待鎖重新可用。 自旋鎖可以在任何時(shí)刻防止多于一個(gè)的內(nèi)核任務(wù)同時(shí)進(jìn)入臨界區(qū),因此這種鎖可有效地避免多處理器上并發(fā)運(yùn)行的內(nèi)核任務(wù)競爭共享資源。,53,實(shí)操應(yīng)用,自旋鎖,自旋鎖的初衷就是: 在短期間內(nèi)進(jìn)行輕量級(jí)的鎖定。
36、一個(gè)被爭用的自旋鎖使得請(qǐng)求它的線程在等待鎖重新可用的期間進(jìn)行自旋(特別浪費(fèi)處理器時(shí)間),所以自旋鎖不應(yīng)該被持有時(shí)間過長。如果需要長時(shí)間鎖定的話, 最好使用信號(hào)量。,54,實(shí)操應(yīng)用,自旋鎖,自旋鎖防止在不同CPU上的執(zhí)行單元對(duì)共享資源的同時(shí)訪問,以及不同進(jìn)程上下文互相搶占導(dǎo)致的對(duì)共享資源的非同步訪問。 在單CPU且不可搶占的內(nèi)核下,自旋鎖的所有操作都是空操作。 自旋鎖不允許任務(wù)睡眠。,55,實(shí)操應(yīng)用,自旋鎖,自旋鎖的基本形式如下: spin_lock(,56,實(shí)操應(yīng)用,自旋鎖,自旋鎖原語要求包含文件是 . 鎖的類型是 spinlock_t. 鎖的兩種初始化方法: spinlock_t my_lo
37、ck = SPIN_LOCK_UNLOCKED; void spin_lock_init(spinlock_t *lock); 進(jìn)入一個(gè)臨界區(qū)前, 必須獲得需要的 lock。 void spin_lock(spinlock_t *lock); 自旋鎖等待是不可中斷的。一旦你調(diào)用spin_lock, 將自旋直到鎖變?yōu)榭捎谩?釋放一個(gè)鎖: void spin_unlock(spinlock_t *lock);,57,實(shí)操應(yīng)用,自旋鎖,關(guān)中斷的自旋鎖 Spin_lock_irq( ) Spin_unlock_irq( ) Spin_lock_irqsave ( ) Spin_unlock_irqre
38、store ( ),58,實(shí)操應(yīng)用,信號(hào)量,Linux中的信號(hào)量是一種睡眠鎖。 如果有一個(gè)任務(wù)試圖獲得一個(gè)已被持有的信號(hào)量時(shí),信號(hào)量會(huì)將其推入等待隊(duì)列,然后讓其睡眠。 當(dāng)持有信號(hào)量的進(jìn)程將信號(hào)量釋放后,在等待隊(duì)列中的一個(gè)任務(wù)將被喚醒,從而便可以獲得這個(gè)信號(hào)量。 信號(hào)量的睡眠特性,使得信號(hào)量適用于鎖會(huì)被長時(shí)間持有的情況; 信號(hào)量的操作 信號(hào)量支持兩個(gè)原子操作P()和V(),前者做測試操作,后者叫做增加操作。 Linux中分別叫做down()和up()。,59,實(shí)操應(yīng)用,信號(hào)量,down()和up()。 down()操作通過對(duì)信號(hào)量計(jì)數(shù)減1來請(qǐng)求獲得一個(gè)信號(hào)量。 如果結(jié)果是0或大于0,信號(hào)量鎖被獲
39、得,任務(wù)就可以進(jìn)入臨界區(qū)了。 如果結(jié)果是負(fù)數(shù),任務(wù)會(huì)被放入等待隊(duì)列,處理器執(zhí)行其它任務(wù)。 相反,當(dāng)臨界區(qū)中的操作完成后,up()操作用來釋放信號(hào)量,增加信號(hào)量的計(jì)數(shù)值。 如果在該信號(hào)量上的等待隊(duì)列不為空,處于隊(duì)列中等待的任務(wù)在被喚醒的同時(shí)會(huì)獲得該信號(hào)量。,60,實(shí)操應(yīng)用,信號(hào)量,61,實(shí)操應(yīng)用,信號(hào)量,62,實(shí)操應(yīng)用,Linux信號(hào)量的實(shí)現(xiàn),內(nèi)核代碼必須包含 ,才能使用信號(hào)量。 相關(guān)的類型是 struct semaphore,信號(hào)量的定義,struct semaphore atomic_t count; int sleepers; wait_queue_head_t wait; ,63,實(shí)操應(yīng)
40、用,Linux信號(hào)量的實(shí)現(xiàn),信號(hào)量的聲明和初始化 直接創(chuàng)建一個(gè)信號(hào)量 struct semaphore * sem; 接著使用 sema_init 來初始化這個(gè)信號(hào)量: void sema_init(struct semaphore *sem, int val); 互斥模式的信號(hào)量聲明,內(nèi)核提供宏定義. DECLARE_MUTEX(name); 信號(hào)量初始化為 1 DECLARE_MUTEX_LOCKED(name); 信號(hào)量初始化為0,64,實(shí)操應(yīng)用,Linux信號(hào)量的實(shí)現(xiàn),動(dòng)態(tài)分配的互斥信號(hào)量聲明 void init_MUTEX(struct semaphore *sem); 信號(hào)量初始化
41、為 1 void init_MUTEX_LOCKED(struct semaphore *sem); 信號(hào)量初始化為0,65,實(shí)操應(yīng)用,Linux信號(hào)量的實(shí)現(xiàn),信號(hào)量的P操作 void down(struct semaphore *sem); down減小信號(hào)量的值,并根據(jù)信號(hào)量的值決定是否等待。不可中斷的等待。 int down_interruptible(struct semaphore *sem); 操作是可中斷的。 int down_trylock(struct semaphore *sem); 信號(hào)量在調(diào)用時(shí)不可用, down_trylock 立刻返回一個(gè)非零值.,66,實(shí)操應(yīng)用,L
42、inux信號(hào)量的實(shí)現(xiàn),信號(hào)量的V操作 void up(struct semaphore *sem); 通過down操作進(jìn)入臨界區(qū)的進(jìn)程,再退出的時(shí)候都需要調(diào)用一個(gè)up操作,釋放信號(hào)量。,67,實(shí)操應(yīng)用,Linux信號(hào)量的實(shí)現(xiàn),信號(hào)量基本使用形式為: static DECLARE_MUTEX(mr_sem);/聲明互斥信號(hào)量 if(down_interruptible( 操作配套使用,68,實(shí)操應(yīng)用,Linux 設(shè)備驅(qū)動(dòng)調(diào)試,69,實(shí)操應(yīng)用,內(nèi)核調(diào)試選項(xiàng),內(nèi)核開發(fā)者在內(nèi)核自身中構(gòu)建了多個(gè)調(diào)試特性。這些特性會(huì)產(chǎn)生額外的輸出并降低性能,Linux發(fā)行版的內(nèi)核為了提高性能,去除這些調(diào)試特性。 用來開發(fā)
43、的內(nèi)核應(yīng)當(dāng)激活的調(diào)試配置選項(xiàng),是在“kernel hacking” 菜單中。,70,實(shí)操應(yīng)用,通過打印調(diào)試,Printk printk 通過附加不同的消息優(yōu)先級(jí)在消息上,對(duì)消息的嚴(yán)重程度進(jìn)行分類。在 定義了8個(gè)loglevel。 DEFAULT_MESSAGE_LOGLEVEL為默認(rèn)級(jí)別(printk.c ) 當(dāng)消息優(yōu)先級(jí)小于console_loglevel,信息才能顯示出來。而console_loglevel的初值為DEFAULT_CONSOLE_LOGLEVEL。 通過對(duì)/proc/sys/kernel/printk的訪問來改變console_loglevel的值。該文件包含四個(gè)數(shù)字:當(dāng)前
44、的loglevel、默認(rèn)loglevel、最小允許的loglevel、引導(dǎo)時(shí)的默認(rèn)loglevel。 echo 1 /proc/sys/kernel/printk echo 8 /proc/sys/kernel/printk,71,實(shí)操應(yīng)用,通過打印調(diào)試,打開和關(guān)閉消息 通過封裝printk函數(shù),快速打開調(diào)試信息或者關(guān)閉調(diào)試信息。 # define PDEBUG(fmt, args.) printk( KERN_DEBUG “myscull: fmt, # args) 通過在Makefile里面定義調(diào)試開關(guān)變量去決定調(diào)試信息是否打開。,72,實(shí)操應(yīng)用,通過查詢調(diào)試,獲取相關(guān)信息的最好方法:在需
45、要的時(shí)候才去查詢系統(tǒng)信息,而不是持續(xù)不斷地產(chǎn)生數(shù)據(jù)。 /proc文件系統(tǒng)是一種特殊的、由軟件創(chuàng)建的文件系統(tǒng),內(nèi)核使用他向外界導(dǎo)出信息。 /proc下面的每個(gè)文件都綁定于一個(gè)內(nèi)核函數(shù),用戶讀取其中的文件時(shí),該函數(shù)動(dòng)態(tài)的生成文件的內(nèi)容。 例如/proc/devices,73,實(shí)操應(yīng)用,通過查詢調(diào)試,包含 在驅(qū)動(dòng)中定義跟proc文件綁定的內(nèi)核函數(shù)read_proc,在函數(shù)里面定義要輸出的信息。 在初始化函數(shù)中調(diào)用creat_proc_read_entry函數(shù)將/proc入口文件和read_proc函數(shù)聯(lián)系起來。 卸載模塊時(shí)調(diào)用remove_proc_entry撤銷proc入口。,74,實(shí)操應(yīng)用,通過
46、查詢調(diào)試,read_proc函數(shù) int (*read_proc)(char *page, char *start, off_t offset, int count, int *eof, void *data); page 是輸出數(shù)據(jù)的緩存內(nèi)存頁。進(jìn)程讀取/proc文件時(shí),內(nèi)核會(huì)分配一個(gè)內(nèi)存頁,read_proc將數(shù)據(jù)通過這個(gè)內(nèi)存頁返回到用戶空間。 start 是這個(gè)函數(shù)用來說有關(guān)的數(shù)據(jù)寫在頁中哪里 eof,當(dāng)沒有數(shù)據(jù)可返回時(shí),驅(qū)動(dòng)設(shè)置這個(gè)參數(shù)。 Data是提供給驅(qū)動(dòng)的專用數(shù)據(jù)指針。,75,實(shí)操應(yīng)用,通過查詢調(diào)試,int sprintf (char *buf, const char *fmt,
47、 .) 將數(shù)據(jù)打包成字符流的形式。 內(nèi)核很多象printk函數(shù)一樣,通過庫函數(shù)的形式提供給內(nèi)核開發(fā)者的函數(shù),以滿足內(nèi)核開發(fā)中的一些簡單的需要。 void *memset (void *s, char c, size_t count); void *memcpy (void *dest, const void *src, size_t count);,76,實(shí)操應(yīng)用,通過查詢調(diào)試,creat_proc_read_entry函數(shù) struct proc_dir_entry *create_proc_read_entry(const char *name,mode_t mode, struct pr
48、oc_dir_entry *base, read_proc_t *read_proc, void *data); name 是要?jiǎng)?chuàng)建的proc文件名, mod 是文件的訪問掩碼(缺省0 ), base 指出要?jiǎng)?chuàng)建的文件的目錄;如果 base 是 NULL, 文件在 /proc 根下創(chuàng)建 。 read_proc 是實(shí)現(xiàn)文件內(nèi)容的 read_proc 函數(shù), data 被內(nèi)核忽略(傳遞給 read_proc).,77,實(shí)操應(yīng)用,查看Oops信息,大多數(shù)bug通常是因?yàn)閺U棄了一個(gè)NULL指針或者使用了錯(cuò)誤的指針值。這類bug導(dǎo)致的結(jié)果通常是一條oops消息。 一條oops消息能夠顯示發(fā)生故障時(shí)CPU
49、的狀態(tài),以及CPU寄存器的內(nèi)容和其他看似難以理解的信息。,78,實(shí)操應(yīng)用,查看Oops信息,例如訪問一個(gè)NULL指針。因?yàn)镹ULL不是一個(gè)可訪問的指針值,所以會(huì)引發(fā)一個(gè)錯(cuò)誤,內(nèi)核會(huì)簡單地將其轉(zhuǎn)換為oops消息并顯示。然后其調(diào)用進(jìn)程會(huì)被殺死。 Unable to handle kernel NULL pointer dereference at virtual address 00000000 printing eip: d083a064 Oops: 0002 #1 SMP CPU: 0 EIP: 0060: Not tainted EFLAGS: 00010246 (2.6.6) EIP is
50、 at oops_example _write+0 x4/0 x10 oops_example eax: 00000000 ebx: 00000000 ecx: 00000000 edx: 00000000 ,79,實(shí)操應(yīng)用,通過監(jiān)視調(diào)試,strace命令可以顯示由用戶空間程序所發(fā)出的所有系統(tǒng)調(diào)用。 還以符號(hào)形式顯示調(diào)用的參數(shù)和返回值。當(dāng)一個(gè)系統(tǒng)調(diào)用失敗, 錯(cuò)誤的符號(hào)值(例如, ENOMEM)和對(duì)應(yīng)的字串(Out of memory) 都顯示. strace 有很多命令行選項(xiàng); -t 來顯示每個(gè)調(diào)用執(zhí)行的時(shí)間, -T 來顯示調(diào)用中花費(fèi)的時(shí)間, -e 來限制被跟蹤調(diào)用的類型, -o 來重定向輸出
51、到一個(gè)文件. 缺省地, strace 打印調(diào)用信息到 stderr.,80,實(shí)操應(yīng)用,Linux 的內(nèi)存分配,81,實(shí)操應(yīng)用,kmalloc函數(shù),void *kmalloc(size_t size,int flags); 所分配到的內(nèi)存在物理內(nèi)存中連續(xù)且保持原有的數(shù)據(jù)(不清零)。 size是要分配的塊的大小。Linux 創(chuàng)建一系列內(nèi)存對(duì)象slab,每個(gè)slab內(nèi)的內(nèi)存塊大小是固定。處理分配請(qǐng)求時(shí),就直接在包含有足夠大內(nèi)存塊的slab中分配一個(gè)整塊給請(qǐng)求者。 內(nèi)核只能分配一些預(yù)定義的、固定大小的字節(jié)數(shù)組。kmalloc 能夠處理的最小內(nèi)存塊是 32 或 64 字節(jié)(體系結(jié)構(gòu)依賴),而內(nèi)存塊大小的
52、上限隨著體系和內(nèi)核配置而變化。 不應(yīng)分配大于 128 KB的內(nèi)存。若需多于幾個(gè) KB的內(nèi)存塊,最好使用其他方法。,82,實(shí)操應(yīng)用,kmalloc函數(shù),void *kmalloc(size_t size,int flags); Flags 分配標(biāo)志,表示如何分配空間 。所有標(biāo)志都定義在 GFP_KERNEL 內(nèi)存分配是代表運(yùn)行在內(nèi)核空間的進(jìn)程執(zhí)行的,并且在空閑內(nèi)存較少時(shí)把當(dāng)前進(jìn)程轉(zhuǎn)入休眠以等待一個(gè)頁面。 GFP_ATOMIC 內(nèi)核通常會(huì)為原子性的分配預(yù)留一些空閑頁。當(dāng)當(dāng)前進(jìn)程不能被置為睡眠時(shí),應(yīng)使用 GFP_ATOMIC,這樣kmalloc 甚至能夠使用最后一個(gè)空閑頁。如果連這最后一個(gè)空閑頁也不
53、存在,則分配返回失敗。常用來從中斷處理和進(jìn)程上下文之外的其他代碼中分配內(nèi)存,從不睡眠。 GFP_DMA 要求分配可用于DMA的內(nèi)存。,83,實(shí)操應(yīng)用,后備高速緩存,內(nèi)核為驅(qū)動(dòng)程序常常需要反復(fù)分配許多相同大小內(nèi)存塊的情況,增加了一些特殊的內(nèi)存池,稱為后備高速緩存(lookaside cache)。 設(shè)備驅(qū)動(dòng)程序通常不會(huì)涉及后備高速緩存,但是也有例外:在 Linux 2.6 中 USB 和 SCSI 驅(qū)動(dòng)。,84,實(shí)操應(yīng)用,后備高速緩存,創(chuàng)建一個(gè)新的后備高速緩存 kmem_cache_create name: name和這個(gè)后備高速緩存相關(guān)聯(lián);通常設(shè)置為被緩存的結(jié)構(gòu)類型的名字,不能包含空格。 參數(shù)
54、size:每個(gè)內(nèi)存區(qū)域的大小。 offset:頁內(nèi)第一個(gè)對(duì)象的偏移量;用來確保被分配對(duì)象的特殊對(duì)齊,0 表示缺省值。 flags:控制分配方式的位掩碼: SLAB_NO_REAP 保護(hù)緩存在系統(tǒng)查找內(nèi)存時(shí)不被削減,不推薦。 SLAB_HWCACHE_ALIGN 所有數(shù)據(jù)對(duì)象跟高速緩存行對(duì)齊,平臺(tái)依賴,可能浪費(fèi)內(nèi)存。 SLAB_CACHE_DMA 每個(gè)數(shù)據(jù)對(duì)象在 DMA 內(nèi)存區(qū)段分配.,85,實(shí)操應(yīng)用,后備高速緩存,創(chuàng)建一個(gè)新的后備高速緩存 kmem_cache_create 參數(shù)constructor 和 destructor 是可選函數(shù),用來初始化新分配的對(duì)象和在內(nèi)存被作為整體釋放給系統(tǒng)之前
55、“清理”對(duì)象。,86,實(shí)操應(yīng)用,后備高速緩存,kmem_cache_alloc 從已創(chuàng)建的后備高速緩存中分配對(duì)象: void *kmem_cache_alloc(kmem_cache_t *cache, int flags); flags 和kmalloc 的flags相同 使用 kmem_cache_free釋放一個(gè)對(duì)象: void kmem_cache_free(kmem_cache_t *cache, const void *obj); 當(dāng)驅(qū)動(dòng)用完這個(gè)后備高速緩存(通常在當(dāng)模塊被卸載時(shí)),釋放緩存: int kmem_cache_destroy(kmem_cache_t *cache);
56、,87,實(shí)操應(yīng)用,get_free_page相關(guān)函數(shù),如果一個(gè)模塊需要分配大塊的內(nèi)存,最好使用面向頁的分配技術(shù)。 _get_free_page(unsigned int flags); 返回一個(gè)指向新頁的指針, 未清零該頁 get_zeroed_page(unsigned int flags); 類似于_get_free_page,但用零填充該頁 _get_free_pages(unsigned int flags, unsigned int order); 分配是若干(物理連續(xù)的)頁面并返回指向該內(nèi)存區(qū)域的第一個(gè)字節(jié)的指針,該內(nèi)存區(qū)域未清零 flags 與 kmalloc 的用法相同 ord
57、er 是請(qǐng)求或釋放的頁數(shù)以 2 為底的對(duì)數(shù)。若其值過大(沒有這么大的連續(xù)區(qū)可用), 則分配失敗,88,實(shí)操應(yīng)用,get_free_page相關(guān)函數(shù),當(dāng)程序不需要頁面時(shí),用下列函數(shù)之一來釋放 void free_page(unsigned long addr);void free_pages(unsigned long addr, unsigned long order);,89,實(shí)操應(yīng)用,Linux 的中斷處理,90,實(shí)操應(yīng)用,為什么會(huì)有中斷,中斷最初是為克服對(duì)I/O接口控制采用程序查詢所帶來的處理器低效率而產(chǎn)生的。 處理器速度一般比外設(shè)快很多 用輪詢的方式來查詢?cè)O(shè)備的狀態(tài),CPU效率不高,C
58、PU和外設(shè)不能并行工作。 中斷機(jī)制讓CPU啟動(dòng)設(shè)備后,就去處理其他任務(wù),只有當(dāng)外設(shè)真正完成數(shù)據(jù)傳輸?shù)臏?zhǔn)備,請(qǐng)求CPU服務(wù)的時(shí)候,CPU才轉(zhuǎn)過來處理外設(shè)的請(qǐng)求。,91,實(shí)操應(yīng)用,中斷和異常,外部中斷: 外部設(shè)備所發(fā)出的I/O請(qǐng)求。 隨著計(jì)算機(jī)系統(tǒng)結(jié)構(gòu)的不斷改進(jìn)以及應(yīng)用技術(shù)的日益提高,中斷的適用范圍也隨之?dāng)U大,出現(xiàn)了所謂的內(nèi)部中斷(或叫異常)。 異常: 為解決機(jī)器運(yùn)行時(shí)所出現(xiàn)的某些隨機(jī)事件及編程方便而出現(xiàn)的。,92,實(shí)操應(yīng)用,I/O中斷處理,為了保證系統(tǒng)對(duì)外部的響應(yīng),一個(gè)中斷處理程序必須被盡快的完成。因此,把所有的操作都放在中斷處理程序中并不合適 Linux中把緊隨中斷要執(zhí)行的操作分為三類 緊急的
59、(critical)一般關(guān)中斷運(yùn)行。諸如對(duì)PIC應(yīng)答中斷,對(duì)PIC或是硬件控制器重新編程,或者修改由設(shè)備和處理器同時(shí)訪問的數(shù)據(jù) 非緊急的(noncritical)如修改那些只有處理器才會(huì)訪問的數(shù)據(jù)結(jié)構(gòu)(例如按下一個(gè)鍵后讀掃描碼),這些也要很快完成,因此由中斷處理程序立即執(zhí)行,不過一般在開中斷的情況下,93,實(shí)操應(yīng)用,I/O中斷處理,Linux中把緊隨中斷要執(zhí)行的操作分為三類 非緊急可延遲的(noncritical deferrable) 這些操作可以被延遲較長的時(shí)間間隔而不影響內(nèi)核操作,有興趣的進(jìn)程將會(huì)等待數(shù)據(jù)。內(nèi)核用下半部分這樣一個(gè)機(jī)制來在一個(gè)更為合適的時(shí)機(jī)用獨(dú)立的函數(shù)來執(zhí)行這些操作。 如把緩沖區(qū)內(nèi)容拷貝到某個(gè)進(jìn)程的地址空間(例如把鍵盤緩沖區(qū)內(nèi)容發(fā)送到終端處理程序進(jìn)程)。,94,實(shí)操應(yīng)用,S3c2410的中斷,中斷發(fā)生時(shí),需要知道中斷的來源。 芯片的引線有限,很難提供很多條中斷請(qǐng)求引線。使用中斷控制器管理中斷請(qǐng)求線。 S3c2410將中斷控制器集成在CPU芯片中,“復(fù)用”GPIO端口引腳,具有作為中斷請(qǐng)求線的功能。 SRCPND寄存器32位中的每一位對(duì)應(yīng)著一個(gè)中斷源,每一位被設(shè)置為1,則相
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- arcgis軟件的認(rèn)識(shí)與使用實(shí)驗(yàn)報(bào)告
- 橋梁設(shè)計(jì)施工方案
- 高軌星載北斗GNSS接收機(jī)規(guī)范 編制說明
- 2025年哈爾濱電力職業(yè)技術(shù)學(xué)院單招職業(yè)傾向性測試題庫參考答案
- 2025年信陽藝術(shù)職業(yè)學(xué)院單招職業(yè)技能測試題庫新版
- 2025年廣安職業(yè)技術(shù)學(xué)院單招職業(yè)傾向性測試題庫附答案
- 2025年畢節(jié)職業(yè)技術(shù)學(xué)院單招職業(yè)傾向性測試題庫新版
- 2023一年級(jí)數(shù)學(xué)上冊(cè) 2 位置教學(xué)實(shí)錄 新人教版
- 提高辦公效率的智能化管理策略
- 9生活離不開他們(教學(xué)設(shè)計(jì))-2023-2024學(xué)年道德與法治四年級(jí)下冊(cè)統(tǒng)編版
- 墨子的《非攻》課件
- 民事起訴狀(證券虛假陳述責(zé)任糾紛)示范文本
- 不動(dòng)產(chǎn)登記中心服務(wù)行為規(guī)范辦法(試行)
- 《ISO 55013-2024 資產(chǎn)管理-數(shù)據(jù)資產(chǎn)管理指南》專業(yè)解讀和應(yīng)用指導(dǎo)材料(雷澤佳編制-2024C0)【第1部分:1-130】
- 軟件資格考試嵌入式系統(tǒng)設(shè)計(jì)師(基礎(chǔ)知識(shí)、應(yīng)用技術(shù))合卷(中級(jí))試卷與參考答案(2024年)
- 2024年下半年杭州黃湖鎮(zhèn)招考編外工作人員易考易錯(cuò)模擬試題(共500題)試卷后附參考答案
- 浙江省第五屆初中生科學(xué)競賽初賽試題卷
- 雷鋒精神在2024:新時(shí)代下的學(xué)習(xí)
- 竣工驗(yàn)收流程培訓(xùn)課件
- 2024年上海中考化學(xué)終極押題密卷三含答案
- DB14∕T 1334-2017 波形鋼腹板預(yù)應(yīng)力混凝土組合結(jié)構(gòu)橋梁懸臂施工與驗(yàn)收規(guī)范
評(píng)論
0/150
提交評(píng)論