linux驅(qū)動(dòng)基礎(chǔ)知識(shí)講解_第1頁
linux驅(qū)動(dòng)基礎(chǔ)知識(shí)講解_第2頁
linux驅(qū)動(dòng)基礎(chǔ)知識(shí)講解_第3頁
linux驅(qū)動(dòng)基礎(chǔ)知識(shí)講解_第4頁
linux驅(qū)動(dòng)基礎(chǔ)知識(shí)講解_第5頁
已閱讀5頁,還剩67頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、Linux 驅(qū)動(dòng)學(xué)習(xí)總結(jié)匯報(bào)驅(qū)動(dòng)學(xué)習(xí)總結(jié)匯報(bào) 2016年年11月月12日日內(nèi)核模塊內(nèi)核模塊BootloderBootloder并發(fā)控制并發(fā)控制中斷處理中斷處理設(shè)備驅(qū)動(dòng)的結(jié)構(gòu)設(shè)備驅(qū)動(dòng)的結(jié)構(gòu)Linux內(nèi)核重要子系統(tǒng)內(nèi)核重要子系統(tǒng)1.1. 系統(tǒng)調(diào)用接口系統(tǒng)調(diào)用接口2.2. 進(jìn)程管理進(jìn)程管理3.3. 內(nèi)存管理內(nèi)存管理4.4. 虛擬文件系統(tǒng)虛擬文件系統(tǒng)5.5. 網(wǎng)絡(luò)堆棧網(wǎng)絡(luò)堆棧6.6. 設(shè)備設(shè)備驅(qū)動(dòng)驅(qū)動(dòng)最簡單的嵌入式系統(tǒng)最簡單的嵌入式系統(tǒng)MTK的的Bootloader在嵌入式操作系統(tǒng)中,在嵌入式操作系統(tǒng)中,BootLoader是在操作系統(tǒng)內(nèi)是在操作系統(tǒng)內(nèi)核運(yùn)行之前運(yùn)行??梢猿跏蓟布O(shè)備、建立內(nèi)存空間核

2、運(yùn)行之前運(yùn)行??梢猿跏蓟布O(shè)備、建立內(nèi)存空間映射圖,從而將系統(tǒng)的軟硬件環(huán)境帶到一個(gè)合適狀態(tài),映射圖,從而將系統(tǒng)的軟硬件環(huán)境帶到一個(gè)合適狀態(tài),以便為最終調(diào)用操作系統(tǒng)內(nèi)核準(zhǔn)備好正確的環(huán)境。以便為最終調(diào)用操作系統(tǒng)內(nèi)核準(zhǔn)備好正確的環(huán)境。MTK的的bootloader有兩部分組成:有兩部分組成:(1)第第1部分部分bootloader,也就是也就是MTK內(nèi)部內(nèi)部(in-house)的的pre-loader,這部分依賴平臺(tái)。這部分依賴平臺(tái)。(2)第第2部分部分bootloader,也就是也就是LittleKernel,這部分依賴這部分依賴操作系統(tǒng),負(fù)責(zé)引導(dǎo)操作系統(tǒng),負(fù)責(zé)引導(dǎo)linux操作系統(tǒng)和操作系統(tǒng)和

3、Android框架??蚣堋T创a位置:源碼位置:vendormediatekproprietarybootablebootloaderMTK的的Bootloader正常啟動(dòng)的主要工作如下:正常啟動(dòng)的主要工作如下:(1)設(shè)備上電后,設(shè)備上電后,BootROM開始運(yùn)行。開始運(yùn)行。(2)BootROM初始化軟件堆棧初始化軟件堆棧(softwarestack)、通信端口和可引導(dǎo)存儲(chǔ)通信端口和可引導(dǎo)存儲(chǔ)設(shè)備設(shè)備(比如比如NAND/EMMC)。(3)BootROM從存儲(chǔ)器中加載從存儲(chǔ)器中加載pre-loader到內(nèi)部到內(nèi)部SRAM(ISRAM)中,因中,因?yàn)檫@時(shí)候還沒有初始化外部的為這時(shí)候還沒有初始化外部的

4、DRAM。(4)BootROM跳轉(zhuǎn)到跳轉(zhuǎn)到pre-loader的入口處并執(zhí)行。的入口處并執(zhí)行。(5)Pre-loader初始化初始化DRAM和加載和加載LK到到RAM中。中。(6)Pre-loader跳轉(zhuǎn)到跳轉(zhuǎn)到LK中并執(zhí)行,然后中并執(zhí)行,然后LK做一些初始化,比如顯示的做一些初始化,比如顯示的初始化等。初始化等。(7)LK從存儲(chǔ)器中加載引導(dǎo)鏡像從存儲(chǔ)器中加載引導(dǎo)鏡像(bootimage),包括包括linux內(nèi)核和內(nèi)核和ramdisk(Android呢?呢?)(8)LK跳轉(zhuǎn)到跳轉(zhuǎn)到linux內(nèi)核并執(zhí)行。內(nèi)核并執(zhí)行。MTK的的Bootloaderpre-loaders中涉及的硬件部分中涉及的硬件

5、部分(1)PLL模塊模塊1)PLL模塊用于調(diào)整處理器和外部內(nèi)存的頻率。模塊用于調(diào)整處理器和外部內(nèi)存的頻率。2)在在PLL模塊初始化后,處理器和外部內(nèi)存的頻率可由模塊初始化后,處理器和外部內(nèi)存的頻率可由26MHZ/26MHZ增加到增加到1GHZ/192MHZ。(2)UART模塊模塊1)UART模塊用于調(diào)試或是模塊用于調(diào)試或是META(MobileEngineeringTestingArchitecture)模式下的握手。模式下的握手。2)默認(rèn)情況下,默認(rèn)情況下,UART4初始化波特率為初始化波特率為9216000bps和用于調(diào)試和用于調(diào)試信息的輸出,信息的輸出,UART1初始化為初始化為1152

6、00bps和作為和作為UARTMETA端口。端口。但也可以使用但也可以使用UART1作為調(diào)試或是作為調(diào)試或是UARTMETA端口。端口。(3)計(jì)時(shí)器計(jì)時(shí)器(timer)模塊模塊這是個(gè)基本的模塊,用來計(jì)算硬件模塊所需要的延時(shí)或是超時(shí)時(shí)間。這是個(gè)基本的模塊,用來計(jì)算硬件模塊所需要的延時(shí)或是超時(shí)時(shí)間。(4)內(nèi)存模塊內(nèi)存模塊1)Pre-loader由由bootROM加載和在芯片組內(nèi)部的加載和在芯片組內(nèi)部的SRAM中執(zhí)行,因中執(zhí)行,因?yàn)橥獠康臑橥獠康腄RAM還沒有初始化。還沒有初始化。2)為了準(zhǔn)備軟件整個(gè)可執(zhí)行環(huán)境,為了準(zhǔn)備軟件整個(gè)可執(zhí)行環(huán)境,pre-loader采用內(nèi)置的內(nèi)存設(shè)置來采用內(nèi)置的內(nèi)存設(shè)置來

7、初始化初始化DRAM(DRAMisinitializeduponpre-loaderbuilt-inmemorysettigns)。這樣,。這樣,LK就能夠被加載到就能夠被加載到DRAM中并執(zhí)行。中并執(zhí)行。(5)GPIO模塊模塊(6)PMIC模塊模塊為了提供一些基本的硬件功能,比如控制外設(shè)電源,為了提供一些基本的硬件功能,比如控制外設(shè)電源,pre-loader初始化初始化上層模塊上層模塊(uppermodules)。(7)RTC模塊模塊1)當(dāng)通過當(dāng)通過power按鍵開機(jī)后,按鍵開機(jī)后,pre-loader拉高拉高RTC的的PWBB來保持設(shè)來保持設(shè)備一直有電備一直有電(keepthedevice

8、alive)和繼續(xù)引導(dǎo)和繼續(xù)引導(dǎo)LK。2)RTC鬧鐘鬧鐘(alarm)有可能是設(shè)備開機(jī)的啟動(dòng)源,對(duì)于這種情況,設(shè)有可能是設(shè)備開機(jī)的啟動(dòng)源,對(duì)于這種情況,設(shè)備部需要按備部需要按power按鍵就可自動(dòng)啟動(dòng)。按鍵就可自動(dòng)啟動(dòng)。(8)USB模塊模塊當(dāng)當(dāng)USB線插入時(shí),它初始化來和外部工具通信,比如用于升級(jí)線插入時(shí),它初始化來和外部工具通信,比如用于升級(jí)系統(tǒng)的下載工具或是系統(tǒng)的下載工具或是META模式觸發(fā)器的模式觸發(fā)器的META工具。工具。(9)NAND模塊模塊(10)MSDC模塊模塊Pre-loader可以從可以從NANDflash或是或是EMMC中加載中加載LK,這兩者只,這兩者只能選擇其中一種來啟

9、動(dòng)能選擇其中一種來啟動(dòng)。LK中涉及的硬件部分中涉及的硬件部分LK是第是第2個(gè)個(gè)loader,它由,它由pre-loader引導(dǎo)并執(zhí)行。從根本上來說引導(dǎo)并執(zhí)行。從根本上來說(basically),pre-loader已經(jīng)初始化了相關(guān)的硬件模塊,而不需要已經(jīng)初始化了相關(guān)的硬件模塊,而不需要在在LK中重新配置這些模塊了。但一些模塊在中重新配置這些模塊了。但一些模塊在LK中被重新復(fù)位來中被重新復(fù)位來配置硬件寄存器,這樣可創(chuàng)造一個(gè)干凈的環(huán)境。比如計(jì)時(shí)器模塊,配置硬件寄存器,這樣可創(chuàng)造一個(gè)干凈的環(huán)境。比如計(jì)時(shí)器模塊,在在LK中,計(jì)時(shí)器重新復(fù)位清零硬件計(jì)數(shù)來對(duì)計(jì)時(shí)進(jìn)行復(fù)位。所有中,計(jì)時(shí)器重新復(fù)位清零硬件計(jì)數(shù)

10、來對(duì)計(jì)時(shí)進(jìn)行復(fù)位。所有在在LK中需要初始化的列在下面:中需要初始化的列在下面:(1)計(jì)時(shí)器模塊計(jì)時(shí)器模塊通過復(fù)位硬件寄存器來復(fù)位計(jì)時(shí)。通過復(fù)位硬件寄存器來復(fù)位計(jì)時(shí)。(2)串口模塊串口模塊LK采用串口模塊來配置它的輸入采用串口模塊來配置它的輸入/輸出系統(tǒng),在這個(gè)模塊初始化輸出系統(tǒng),在這個(gè)模塊初始化后,我們可以使用后,我們可以使用LK提供的提供的“printf()”等函數(shù)來使用串口功能。等函數(shù)來使用串口功能。(3)I2C模塊模塊(4)PWM模塊模塊(5)PMIC模塊模塊(6)RTC模塊模塊和計(jì)時(shí)器模塊一樣,在和計(jì)時(shí)器模塊一樣,在U-Boot中,中,I2C/PMIC/RTC重新復(fù)位寄存器來重新復(fù)位寄

11、存器來復(fù)位這些模塊。復(fù)位這些模塊。(7)LED模塊模塊通過這通過這poweroffcharging個(gè)模塊,設(shè)備能夠通知用戶當(dāng)前的充電狀態(tài)。個(gè)模塊,設(shè)備能夠通知用戶當(dāng)前的充電狀態(tài)。(8)充電模塊充電模塊這個(gè)模塊負(fù)責(zé)關(guān)機(jī)充電這個(gè)模塊負(fù)責(zé)關(guān)機(jī)充電(poweroffcharging)、低電壓充電低電壓充電(lowercharginginthesystem)。(9)LCD模塊模塊使用這個(gè)模塊,設(shè)備能夠顯示使用這個(gè)模塊,設(shè)備能夠顯示logo或是任何通知的消息。或是任何通知的消息。(10)NAND模塊模塊因?yàn)橐驗(yàn)閁-Boot也需要從也需要從flash讀取鏡像讀取鏡像(比如內(nèi)核或是比如內(nèi)核或是ramdisk)

12、,所以有必要在所以有必要在U-Boot中初始化中初始化NAND相關(guān)的功能。相關(guān)的功能。(11)MSDC模塊模塊支持支持MSDC啟動(dòng)啟動(dòng)一些重要的數(shù)據(jù)結(jié)構(gòu)一些重要的數(shù)據(jù)結(jié)構(gòu)1.1. 大部分驅(qū)動(dòng)程序涉及三個(gè)重要的內(nèi)核數(shù)據(jù)結(jié)構(gòu):大部分驅(qū)動(dòng)程序涉及三個(gè)重要的內(nèi)核數(shù)據(jù)結(jié)構(gòu): 文件操作文件操作file_operationsfile_operations結(jié)構(gòu)體結(jié)構(gòu)體 文件對(duì)象文件對(duì)象filefile結(jié)構(gòu)體結(jié)構(gòu)體 索引節(jié)點(diǎn)索引節(jié)點(diǎn)inodeinode結(jié)構(gòu)體結(jié)構(gòu)體Linux設(shè)備驅(qū)動(dòng)設(shè)備驅(qū)動(dòng)LinuxLinux下設(shè)備的屬性下設(shè)備的屬性設(shè)備的類型:字符設(shè)備、塊設(shè)備、網(wǎng)絡(luò)設(shè)備設(shè)備的類型:字符設(shè)備、塊設(shè)備、網(wǎng)絡(luò)設(shè)備主設(shè)

13、備號(hào):標(biāo)識(shí)設(shè)備對(duì)應(yīng)的驅(qū)動(dòng)程序。一般主設(shè)備號(hào):標(biāo)識(shí)設(shè)備對(duì)應(yīng)的驅(qū)動(dòng)程序。一般“一個(gè)主設(shè)一個(gè)主設(shè)備號(hào)對(duì)應(yīng)一個(gè)驅(qū)動(dòng)程序備號(hào)對(duì)應(yīng)一個(gè)驅(qū)動(dòng)程序”次設(shè)備號(hào):每個(gè)驅(qū)動(dòng)程序負(fù)責(zé)管理它所驅(qū)動(dòng)的幾個(gè)硬件次設(shè)備號(hào):每個(gè)驅(qū)動(dòng)程序負(fù)責(zé)管理它所驅(qū)動(dòng)的幾個(gè)硬件實(shí)例,這些硬件實(shí)例則由次設(shè)備號(hào)來表示。同一驅(qū)動(dòng)下實(shí)例,這些硬件實(shí)例則由次設(shè)備號(hào)來表示。同一驅(qū)動(dòng)下的實(shí)例編號(hào),用于確定設(shè)備文件所指的設(shè)備。的實(shí)例編號(hào),用于確定設(shè)備文件所指的設(shè)備??赏ㄟ^可通過ls l “l(fā)s l “設(shè)備文件名設(shè)備文件名”命令查看設(shè)備的主次設(shè)備命令查看設(shè)備的主次設(shè)備號(hào),以及設(shè)備的類型。號(hào),以及設(shè)備的類型。18分配和釋放字符設(shè)備號(hào)分配和釋放字符設(shè)備號(hào)1.1.

14、編寫驅(qū)動(dòng)程序要做的第一件事,為字符設(shè)備獲取一個(gè)設(shè)備編寫驅(qū)動(dòng)程序要做的第一件事,為字符設(shè)備獲取一個(gè)設(shè)備號(hào)。號(hào)。2.2. 事先知道所需要的設(shè)備編號(hào)(主設(shè)備號(hào))的情況:事先知道所需要的設(shè)備編號(hào)(主設(shè)備號(hào))的情況: int register_chrdev_region(dev_t first, unsigned int register_chrdev_region(dev_t first, unsigned count, const char count, const char * *name)name) firstfirst是要分配的起始設(shè)備編號(hào)值。是要分配的起始設(shè)備編號(hào)值。 firstfirst的

15、次設(shè)備號(hào)的次設(shè)備號(hào)通常設(shè)置為通常設(shè)置為0 0。 Count Count 所請(qǐng)求的連續(xù)設(shè)備編號(hào)的個(gè)數(shù)。所請(qǐng)求的連續(xù)設(shè)備編號(hào)的個(gè)數(shù)。 NameName設(shè)備名稱,指和該編號(hào)范圍建立關(guān)系的設(shè)備。設(shè)備名稱,指和該編號(hào)范圍建立關(guān)系的設(shè)備。 分配成功返回分配成功返回0 0。19分配和釋放字符設(shè)備號(hào)分配和釋放字符設(shè)備號(hào)1.1. 動(dòng)態(tài)分配設(shè)備編號(hào)(主要是主設(shè)備號(hào))動(dòng)態(tài)分配設(shè)備編號(hào)(主要是主設(shè)備號(hào)) int alloc_chrdev_region(dev_t int alloc_chrdev_region(dev_t * *dev, unsigned dev, unsigned baseminor, unsign

16、ed count,const char baseminor, unsigned count,const char * *name)name) dev dev 是一個(gè)僅用于輸出的參數(shù)是一個(gè)僅用于輸出的參數(shù), , 它在函數(shù)成功完成時(shí)保它在函數(shù)成功完成時(shí)保存已分配范圍的第一個(gè)編號(hào)。存已分配范圍的第一個(gè)編號(hào)。 baseminor baseminor 應(yīng)當(dāng)是請(qǐng)求的第一個(gè)要用的次設(shè)備號(hào),它常應(yīng)當(dāng)是請(qǐng)求的第一個(gè)要用的次設(shè)備號(hào),它常常是常是 0. 0. count count 和和 name name 參數(shù)跟參數(shù)跟request_chrdev_region request_chrdev_region 的一樣的

17、一樣. .20分配和釋放字符設(shè)備號(hào)分配和釋放字符設(shè)備號(hào)1.1. 不再使用時(shí),釋放這些設(shè)備編號(hào)。使用以下函數(shù):不再使用時(shí),釋放這些設(shè)備編號(hào)。使用以下函數(shù): void unregister_chrdev_region(dev_t from, void unregister_chrdev_region(dev_t from, unsigned count)unsigned count) 在模塊的卸載函數(shù)中調(diào)用該函數(shù)。在模塊的卸載函數(shù)中調(diào)用該函數(shù)。21字符設(shè)備的注冊(cè)字符設(shè)備的注冊(cè)1.1. 內(nèi)核內(nèi)部使用內(nèi)核內(nèi)部使用struct cdevstruct cdev結(jié)構(gòu)表示字符設(shè)備。編寫設(shè)備結(jié)構(gòu)表示字符設(shè)備。編

18、寫設(shè)備驅(qū)動(dòng)的第二步就是注冊(cè)該設(shè)備。驅(qū)動(dòng)的第二步就是注冊(cè)該設(shè)備。 包含包含頭文件。頭文件。 獲取一個(gè)獨(dú)立的獲取一個(gè)獨(dú)立的cdevcdev結(jié)構(gòu):結(jié)構(gòu):struct cdev struct cdev * *my_cdev = cdev_alloc();my_cdev = cdev_alloc(); 調(diào)用調(diào)用cdev_initcdev_init初始化初始化cdevcdev結(jié)構(gòu)體結(jié)構(gòu)體void cdev_init(struct cdev void cdev_init(struct cdev * *cdev, struct cdev, struct file_operations file_operati

19、ons * *fops);fops); 初始化該設(shè)備的所有者字段:初始化該設(shè)備的所有者字段:dev-cdev.owner = THIS_MODULE;dev-cdev.owner = THIS_MODULE; 初始化該設(shè)備的可用操作集:初始化該設(shè)備的可用操作集:dev-cdev.ops = &device_fops;dev-cdev.ops = &device_fops;22字符設(shè)備的注冊(cè)字符設(shè)備的注冊(cè)1.1. 編寫設(shè)備驅(qū)動(dòng)的第二步就是注冊(cè)該設(shè)備。編寫設(shè)備驅(qū)動(dòng)的第二步就是注冊(cè)該設(shè)備。 cdev cdev 結(jié)構(gòu)已建立和初始化結(jié)構(gòu)已建立和初始化, , 最后通過最后通過cdev_addcdev_ad

20、d函數(shù)把函數(shù)把它告訴內(nèi)核:它告訴內(nèi)核:int cdev_add(struct cdev int cdev_add(struct cdev * *dev, dev_t num, dev, dev_t num, unsigned int count);unsigned int count); dev dev 是要添加的設(shè)備的是要添加的設(shè)備的 cdev cdev 結(jié)構(gòu)結(jié)構(gòu), , num num 是這個(gè)設(shè)備對(duì)應(yīng)的第一個(gè)設(shè)備編號(hào)是這個(gè)設(shè)備對(duì)應(yīng)的第一個(gè)設(shè)備編號(hào), , count count 是應(yīng)當(dāng)關(guān)聯(lián)到設(shè)備的設(shè)備號(hào)的數(shù)目是應(yīng)當(dāng)關(guān)聯(lián)到設(shè)備的設(shè)備號(hào)的數(shù)目. . 卸載字符設(shè)備時(shí),調(diào)用相反的動(dòng)作函數(shù):卸載字符設(shè)備

21、時(shí),調(diào)用相反的動(dòng)作函數(shù): void cdev_del(struct cdev void cdev_del(struct cdev * *dev);dev);23LinuxLinux設(shè)備驅(qū)動(dòng)的并發(fā)控制設(shè)備驅(qū)動(dòng)的并發(fā)控制24設(shè)備驅(qū)動(dòng)的并發(fā)控制設(shè)備驅(qū)動(dòng)的并發(fā)控制 1.1. 在驅(qū)動(dòng)程序中,當(dāng)多個(gè)線程同時(shí)訪問相同的資源時(shí),可在驅(qū)動(dòng)程序中,當(dāng)多個(gè)線程同時(shí)訪問相同的資源時(shí),可能會(huì)引發(fā)能會(huì)引發(fā)“競態(tài)競態(tài)”,必須對(duì)共享資源進(jìn)行并發(fā)控制。,必須對(duì)共享資源進(jìn)行并發(fā)控制。2.2. 并發(fā)和競態(tài)廣泛存在。并發(fā)和競態(tài)廣泛存在。3.3. 并發(fā)控制的目的:并發(fā)控制的目的:使得線程訪問共享資源的操作是原子操作。使得線程訪問共享資

22、源的操作是原子操作。1.1. 原子操作:原子操作:在執(zhí)行過程中不會(huì)被別的代碼路徑所中斷的操作。在執(zhí)行過程中不會(huì)被別的代碼路徑所中斷的操作。 1.1. 驅(qū)動(dòng)程序中的全局變量是一種典型的共享資源。驅(qū)動(dòng)程序中的全局變量是一種典型的共享資源。251.1.考慮一個(gè)非常簡單的共享資源的例子:一個(gè)全局整型變考慮一個(gè)非常簡單的共享資源的例子:一個(gè)全局整型變量和一個(gè)簡單的臨界區(qū),其中的操作僅僅是將整型變量量和一個(gè)簡單的臨界區(qū),其中的操作僅僅是將整型變量的值增加的值增加1 1: i+ i+ 1.1.該操作可以轉(zhuǎn)化成下面三條機(jī)器指令序列:該操作可以轉(zhuǎn)化成下面三條機(jī)器指令序列: 得到當(dāng)前變量得到當(dāng)前變量i i的值并拷

23、貝到一個(gè)寄存器中的值并拷貝到一個(gè)寄存器中 將寄存器中的值加將寄存器中的值加1 1 把把i i的新值寫回到內(nèi)存中的新值寫回到內(nèi)存中 原子操作原子操作26Linux內(nèi)核的并發(fā)控制內(nèi)核的并發(fā)控制 1.1. 在內(nèi)核空間的內(nèi)核任務(wù)需要考慮同步在內(nèi)核空間的內(nèi)核任務(wù)需要考慮同步 內(nèi)核空間中的共享數(shù)據(jù)對(duì)內(nèi)核中的所有任務(wù)可見,所以內(nèi)核空間中的共享數(shù)據(jù)對(duì)內(nèi)核中的所有任務(wù)可見,所以當(dāng)在內(nèi)核中訪問數(shù)據(jù)時(shí),就必須考慮是否會(huì)有其他內(nèi)核當(dāng)在內(nèi)核中訪問數(shù)據(jù)時(shí),就必須考慮是否會(huì)有其他內(nèi)核任務(wù)并發(fā)訪問的可能、是否會(huì)產(chǎn)生競爭條件、是否需要任務(wù)并發(fā)訪問的可能、是否會(huì)產(chǎn)生競爭條件、是否需要對(duì)數(shù)據(jù)同步。對(duì)數(shù)據(jù)同步。27確定保護(hù)對(duì)象確定保

24、護(hù)對(duì)象 找出哪些數(shù)據(jù)需要保護(hù)是關(guān)鍵所在找出哪些數(shù)據(jù)需要保護(hù)是關(guān)鍵所在 內(nèi)核任務(wù)的局部數(shù)據(jù)僅僅被它本身訪問,顯然不需要內(nèi)核任務(wù)的局部數(shù)據(jù)僅僅被它本身訪問,顯然不需要保護(hù)。保護(hù)。 如果數(shù)據(jù)只會(huì)被特定的進(jìn)程訪問,也不需加鎖如果數(shù)據(jù)只會(huì)被特定的進(jìn)程訪問,也不需加鎖 大多數(shù)內(nèi)核數(shù)據(jù)結(jié)構(gòu)都需要加鎖:若有其它內(nèi)核任務(wù)大多數(shù)內(nèi)核數(shù)據(jù)結(jié)構(gòu)都需要加鎖:若有其它內(nèi)核任務(wù)可以訪問這些數(shù)據(jù),那么就給這些數(shù)據(jù)加上某種形式可以訪問這些數(shù)據(jù),那么就給這些數(shù)據(jù)加上某種形式的鎖;若任何其它東西能看到它,那么就要鎖住它。的鎖;若任何其它東西能看到它,那么就要鎖住它。 Linux內(nèi)核的并發(fā)控制28Linux內(nèi)核的并發(fā)控制內(nèi)核的并發(fā)控

25、制1.1. 并發(fā)控制的機(jī)制并發(fā)控制的機(jī)制 中斷屏蔽,原子數(shù)操作,自旋鎖和信號(hào)量都是解決中斷屏蔽,原子數(shù)操作,自旋鎖和信號(hào)量都是解決并發(fā)問題的機(jī)制。并發(fā)問題的機(jī)制。 中斷屏蔽很少被單獨(dú)使用,原子操作只能針對(duì)整數(shù)中斷屏蔽很少被單獨(dú)使用,原子操作只能針對(duì)整數(shù)來進(jìn)行。因此自旋鎖和信號(hào)量應(yīng)用最為廣泛。來進(jìn)行。因此自旋鎖和信號(hào)量應(yīng)用最為廣泛。 291.1. 鎖機(jī)制可以避免競爭狀態(tài)正如門鎖和門一樣,門后的房間鎖機(jī)制可以避免競爭狀態(tài)正如門鎖和門一樣,門后的房間可想象成一個(gè)臨界區(qū)。可想象成一個(gè)臨界區(qū)。2.2. 在一段時(shí)間內(nèi),房間里只能有一個(gè)內(nèi)核任務(wù)存在,當(dāng)一個(gè)在一段時(shí)間內(nèi),房間里只能有一個(gè)內(nèi)核任務(wù)存在,當(dāng)一個(gè)任

26、務(wù)進(jìn)入房間后,它會(huì)鎖住身后的房門;當(dāng)它結(jié)束對(duì)共享任務(wù)進(jìn)入房間后,它會(huì)鎖住身后的房門;當(dāng)它結(jié)束對(duì)共享數(shù)據(jù)的操作后,就會(huì)走出房間,打開門鎖。如果另一個(gè)任數(shù)據(jù)的操作后,就會(huì)走出房間,打開門鎖。如果另一個(gè)任務(wù)在房門上鎖時(shí)來了務(wù)在房門上鎖時(shí)來了, ,那么它就必須等待房間內(nèi)的任務(wù)出那么它就必須等待房間內(nèi)的任務(wù)出來并打開門鎖后,才能進(jìn)入房間。來并打開門鎖后,才能進(jìn)入房間。 加鎖機(jī)制 301.1. 任何要訪問臨界資源的代碼首先都需要占住相應(yīng)的鎖,這任何要訪問臨界資源的代碼首先都需要占住相應(yīng)的鎖,這樣該鎖就能阻止來自其它內(nèi)核任務(wù)的并發(fā)訪問:樣該鎖就能阻止來自其它內(nèi)核任務(wù)的并發(fā)訪問: 任務(wù)任務(wù) 1 1 試圖鎖定隊(duì)

27、列 成功:獲得鎖 訪問隊(duì)列 為隊(duì)列解除鎖 任務(wù)任務(wù)2 2 試圖鎖定隊(duì)列失?。旱却?等待 等待 成功:獲得鎖 訪問隊(duì)列 為隊(duì)列解除鎖加鎖機(jī)制 31原子數(shù)操作原子數(shù)操作1.1. 整型原子數(shù)操作整型原子數(shù)操作 原子變量初始化原子變量初始化atomic_t test = ATOMIC_INIT(i);atomic_t test = ATOMIC_INIT(i); 設(shè)置原子變量的值設(shè)置原子變量的值void atomic_set(atomic_t void atomic_set(atomic_t * *v, int i)v, int i) 獲得原子變量的值獲得原子變量的值atomic_read(v)ato

28、mic_read(v) 原子變量加原子變量加void atomic_add(int i, atomic_t void atomic_add(int i, atomic_t * *v)v) 原子變量減原子變量減void atomic_sub(int i, atomic_t void atomic_sub(int i, atomic_t * *v)v)32原子數(shù)操作原子數(shù)操作1.1. 整型原子數(shù)操作整型原子數(shù)操作 原子變量的自增操作原子變量的自增操作void atomic_inc(atomic_t void atomic_inc(atomic_t * *v)v) 原子變量的自減操作原子變量的自減操

29、作void atomic_dec(atomic_t void atomic_dec(atomic_t * *v)v) 操作并測試操作并測試 (測試其是否為(測試其是否為0 0,0 0為為truetrue,否為,否為falsefalse)atomic_inc_and_test(atomic_t atomic_inc_and_test(atomic_t * *v)v)atomic_dec_and_test(atomic_t atomic_dec_and_test(atomic_t * *v)v)int atomic_sub_and_test(int i, atomic_t int atomic_s

30、ub_and_test(int i, atomic_t * *v)v) 操作并返回操作并返回 (返回新值)(返回新值)int atomic_add_return(int i, atomic_t int atomic_add_return(int i, atomic_t * *v)v)int atomic_sub_return(int i, atomic_t int atomic_sub_return(int i, atomic_t * *v)v)33原子數(shù)操作原子數(shù)操作1.1. 原子位操作原子位操作 設(shè)置位設(shè)置位void set_bit(int nr, volatile unsigned lo

31、ng void set_bit(int nr, volatile unsigned long * * addr) addr) 清除位清除位void clear_bit(int nr, volatile unsigned void clear_bit(int nr, volatile unsigned long long * * addr) addr) 改變位改變位change_bit(nr,p)change_bit(nr,p) 測試位測試位test_bit(int nr, const volatile unsigned test_bit(int nr, const volatile unsig

32、ned long long * * p) p) 測試并操作位測試并操作位test_and_set_bit(nr,p)test_and_set_bit(nr,p)34自旋鎖自旋鎖1.1. 自旋鎖是專為防止多處理器并發(fā)而引入的一種鎖,它自旋鎖是專為防止多處理器并發(fā)而引入的一種鎖,它在內(nèi)核中大量應(yīng)用于中斷處理等部分。而對(duì)于單處理在內(nèi)核中大量應(yīng)用于中斷處理等部分。而對(duì)于單處理器來說,防止中斷處理中的并發(fā)可簡單采用關(guān)閉中斷器來說,防止中斷處理中的并發(fā)可簡單采用關(guān)閉中斷的方式,不需要自旋鎖。的方式,不需要自旋鎖。 2.2. 自旋鎖最多只能被一個(gè)內(nèi)核任務(wù)持有,若一個(gè)內(nèi)核任自旋鎖最多只能被一個(gè)內(nèi)核任務(wù)持有,若

33、一個(gè)內(nèi)核任務(wù)試圖請(qǐng)求一個(gè)已被持有的自旋鎖,那么這個(gè)任務(wù)就務(wù)試圖請(qǐng)求一個(gè)已被持有的自旋鎖,那么這個(gè)任務(wù)就會(huì)一直進(jìn)行忙循環(huán),也就是旋轉(zhuǎn),等待鎖重新可用。會(huì)一直進(jìn)行忙循環(huán),也就是旋轉(zhuǎn),等待鎖重新可用。 3.3. 自旋鎖可以在任何時(shí)刻防止多于一個(gè)的內(nèi)核任務(wù)同時(shí)自旋鎖可以在任何時(shí)刻防止多于一個(gè)的內(nèi)核任務(wù)同時(shí)進(jìn)入臨界區(qū),因此這種鎖可有效地避免多處理器上并進(jìn)入臨界區(qū),因此這種鎖可有效地避免多處理器上并發(fā)運(yùn)行的內(nèi)核任務(wù)競爭共享資源。發(fā)運(yùn)行的內(nèi)核任務(wù)競爭共享資源。 35自旋鎖自旋鎖1.1. 自旋鎖的初衷就是:自旋鎖的初衷就是:在短期間內(nèi)進(jìn)行輕量級(jí)的鎖定。一個(gè)被爭用的自在短期間內(nèi)進(jìn)行輕量級(jí)的鎖定。一個(gè)被爭用的自旋

34、鎖使得請(qǐng)求它的線程在等待鎖重新可用的期間進(jìn)行旋鎖使得請(qǐng)求它的線程在等待鎖重新可用的期間進(jìn)行自旋(特別浪費(fèi)處理器時(shí)間),所以自旋鎖不應(yīng)該被自旋(特別浪費(fèi)處理器時(shí)間),所以自旋鎖不應(yīng)該被持有時(shí)間過長。如果需要長時(shí)間鎖定的話持有時(shí)間過長。如果需要長時(shí)間鎖定的話, , 最好使用最好使用信號(hào)量。信號(hào)量。 36自旋鎖自旋鎖1.1. 自旋鎖防止在不同自旋鎖防止在不同CPUCPU上的執(zhí)行單元對(duì)共享資源的同上的執(zhí)行單元對(duì)共享資源的同時(shí)訪問,以及不同進(jìn)程上下文互相搶占導(dǎo)致的對(duì)共享時(shí)訪問,以及不同進(jìn)程上下文互相搶占導(dǎo)致的對(duì)共享資源的非同步訪問。資源的非同步訪問。2.2. 在單在單CPUCPU且不可搶占的內(nèi)核下,自旋

35、鎖的所有操作都且不可搶占的內(nèi)核下,自旋鎖的所有操作都是空操作。是空操作。 3.3. 自旋鎖不允許任務(wù)睡眠。自旋鎖不允許任務(wù)睡眠。37自旋鎖自旋鎖1.1. 自旋鎖的基本形式如下:自旋鎖的基本形式如下:spin_lock(&mr_lock);spin_lock(&mr_lock);/ /* *臨界區(qū)臨界區(qū)* */ /spin_unlock(&mr_lock)spin_unlock(&mr_lock);38自旋鎖自旋鎖1.1. 自旋鎖原語要求包含文件是自旋鎖原語要求包含文件是 . . 鎖的類型是鎖的類型是 spinlock_t.spinlock_t.2.2. 鎖的兩種初始化方法:鎖的兩種初始化方法:

36、 spinlock_t my_lock = SPIN_LOCK_UNLOCKED;spinlock_t my_lock = SPIN_LOCK_UNLOCKED; void spin_lock_init(spinlock_t void spin_lock_init(spinlock_t * *lock);lock);3.3. 進(jìn)入一個(gè)臨界區(qū)前進(jìn)入一個(gè)臨界區(qū)前, , 必須獲得需要的必須獲得需要的 locklock。 void spin_lock(spinlock_t void spin_lock(spinlock_t * *lock);lock); 自旋鎖等待是不可中斷的。一旦你調(diào)用自旋鎖等待是

37、不可中斷的。一旦你調(diào)用spin_lock, spin_lock, 將自旋直到鎖變?yōu)榭捎?。將自旋直到鎖變?yōu)榭捎谩?.4. 釋放一個(gè)鎖:釋放一個(gè)鎖: void spin_unlock(spinlock_t void spin_unlock(spinlock_t * *lock);lock);39自旋鎖自旋鎖1.1. 關(guān)中斷的自旋鎖關(guān)中斷的自旋鎖 Spin_lock_irq( )Spin_lock_irq( ) Spin_unlock_irq( )Spin_unlock_irq( ) Spin_lock_irqsave ( )Spin_lock_irqsave ( ) Spin_unlock_irq

38、restore Spin_unlock_irqrestore ()40信號(hào)量信號(hào)量1.1. LinuxLinux中的信號(hào)量是一種睡眠鎖。中的信號(hào)量是一種睡眠鎖。 如果有一個(gè)任務(wù)試圖獲得一個(gè)已被持有的信號(hào)量時(shí)如果有一個(gè)任務(wù)試圖獲得一個(gè)已被持有的信號(hào)量時(shí),信號(hào)量會(huì)將其推入等待隊(duì)列,然后讓其睡眠。,信號(hào)量會(huì)將其推入等待隊(duì)列,然后讓其睡眠。 當(dāng)持有信號(hào)量的進(jìn)程將信號(hào)量釋放后,在等待隊(duì)列當(dāng)持有信號(hào)量的進(jìn)程將信號(hào)量釋放后,在等待隊(duì)列中的一個(gè)任務(wù)將被喚醒,從而便可以獲得這個(gè)信號(hào)中的一個(gè)任務(wù)將被喚醒,從而便可以獲得這個(gè)信號(hào)量。量。 信號(hào)量的睡眠特性,使得信號(hào)量適用于鎖會(huì)被長時(shí)信號(hào)量的睡眠特性,使得信號(hào)量適用于

39、鎖會(huì)被長時(shí)間持有的情況;間持有的情況;2.2. 信號(hào)量的操作信號(hào)量的操作 信號(hào)量支持兩個(gè)原子操作信號(hào)量支持兩個(gè)原子操作P()P()和和V()V(),前者做測試操,前者做測試操作,后者叫做增加操作。作,后者叫做增加操作。 LinuxLinux中分別叫做中分別叫做down()down()和和up()up()。 41信號(hào)量信號(hào)量42信號(hào)量信號(hào)量43Linux信號(hào)量的實(shí)現(xiàn)信號(hào)量的實(shí)現(xiàn)1.1. 內(nèi)核代碼必須包含內(nèi)核代碼必須包含 ,才能使用信,才能使用信號(hào)量。號(hào)量。2.2. 相關(guān)的類型是相關(guān)的類型是 struct semaphorestruct semaphore信號(hào)量的定義structsemaphore

40、atomic_tcount;intsleepers;wait_queue_head_twait; 44Linux信號(hào)量的實(shí)現(xiàn)信號(hào)量的實(shí)現(xiàn)1.1. 信號(hào)量的聲明和初始化信號(hào)量的聲明和初始化 直接創(chuàng)建一個(gè)信號(hào)量直接創(chuàng)建一個(gè)信號(hào)量 struct semaphore struct semaphore * * sem; sem; 接著使用接著使用 sema_init sema_init 來初始化這個(gè)信號(hào)量:來初始化這個(gè)信號(hào)量:void sema_init(struct semaphore void sema_init(struct semaphore * *sem, int sem, int val)v

41、al);1.1. 互斥模式的信號(hào)量聲明,內(nèi)核提供宏定義互斥模式的信號(hào)量聲明,內(nèi)核提供宏定義. . DECLARE_MUTEX(name);DECLARE_MUTEX(name);信號(hào)量初始化為信號(hào)量初始化為 1 1 DECLARE_MUTEX_LOCKED(name);DECLARE_MUTEX_LOCKED(name);信號(hào)量初始化為信號(hào)量初始化為0 045自旋鎖自旋鎖忙等待,無調(diào)度開銷;忙等待,無調(diào)度開銷;進(jìn)程搶占被禁止;進(jìn)程搶占被禁止;鎖定期間不能休眠;鎖定期間不能休眠;信號(hào)量信號(hào)量拿不到就切換進(jìn)程,有調(diào)度開銷;拿不到就切換進(jìn)程,有調(diào)度開銷;鎖定期間可以休眠;鎖定期間可以休眠;46Lin

42、ux的中斷處理47為什么會(huì)有中斷為什么會(huì)有中斷1.1. 中斷最初是為克服對(duì)中斷最初是為克服對(duì)I/OI/O接口控制采用程序查詢所帶來的接口控制采用程序查詢所帶來的處理器低效率而產(chǎn)生的。處理器低效率而產(chǎn)生的。 處理器速度一般比外設(shè)快很多處理器速度一般比外設(shè)快很多 用輪詢的方式來查詢?cè)O(shè)備的狀態(tài),用輪詢的方式來查詢?cè)O(shè)備的狀態(tài),CPUCPU效率不高,效率不高,CPUCPU和和外設(shè)不能并行工作。外設(shè)不能并行工作。 中斷機(jī)制讓中斷機(jī)制讓CPUCPU啟動(dòng)設(shè)備后,就去處理其他任務(wù),只有啟動(dòng)設(shè)備后,就去處理其他任務(wù),只有當(dāng)外設(shè)真正完成數(shù)據(jù)傳輸?shù)臏?zhǔn)備,請(qǐng)求當(dāng)外設(shè)真正完成數(shù)據(jù)傳輸?shù)臏?zhǔn)備,請(qǐng)求CPUCPU服務(wù)的時(shí)候服務(wù)

43、的時(shí)候,CPUCPU才轉(zhuǎn)過來處理外設(shè)的請(qǐng)求。才轉(zhuǎn)過來處理外設(shè)的請(qǐng)求。48中斷和異常中斷和異常1.1. 外部中斷:外部中斷:外部設(shè)備所發(fā)出的外部設(shè)備所發(fā)出的I/OI/O請(qǐng)求。請(qǐng)求。1.1. 隨著計(jì)算機(jī)系統(tǒng)結(jié)構(gòu)的不斷改進(jìn)以及應(yīng)用技術(shù)的日益提高隨著計(jì)算機(jī)系統(tǒng)結(jié)構(gòu)的不斷改進(jìn)以及應(yīng)用技術(shù)的日益提高,中斷的適用范圍也隨之?dāng)U大,出現(xiàn)了所謂的內(nèi)部中斷(,中斷的適用范圍也隨之?dāng)U大,出現(xiàn)了所謂的內(nèi)部中斷(或叫異常)?;蚪挟惓#?.2. 異常:異常:為解決機(jī)器運(yùn)行時(shí)所出現(xiàn)的某些隨機(jī)事件及編程方便而出為解決機(jī)器運(yùn)行時(shí)所出現(xiàn)的某些隨機(jī)事件及編程方便而出現(xiàn)的?,F(xiàn)的。49I/O中斷處理中斷處理1.1. 為了保證系統(tǒng)對(duì)外部

44、的響應(yīng),一個(gè)中斷處理程序必須被盡為了保證系統(tǒng)對(duì)外部的響應(yīng),一個(gè)中斷處理程序必須被盡快的完成。因此,把所有的操作都放在中斷處理程序中并快的完成。因此,把所有的操作都放在中斷處理程序中并不合適不合適2.2. LinuxLinux中把緊隨中斷要執(zhí)行的操作分為三類中把緊隨中斷要執(zhí)行的操作分為三類 緊急的緊急的(critical)(critical)一般關(guān)中斷運(yùn)行。諸如對(duì)一般關(guān)中斷運(yùn)行。諸如對(duì)PICPIC應(yīng)答中斷,對(duì)應(yīng)答中斷,對(duì)PICPIC或是硬件或是硬件控制器重新編程,或者修改由設(shè)備和處理器同時(shí)訪問的控制器重新編程,或者修改由設(shè)備和處理器同時(shí)訪問的數(shù)據(jù)數(shù)據(jù) 非緊急的非緊急的(noncritical)(

45、noncritical)如修改那些只有處理器才會(huì)訪問的數(shù)據(jù)結(jié)構(gòu)如修改那些只有處理器才會(huì)訪問的數(shù)據(jù)結(jié)構(gòu)( (例如按下例如按下一個(gè)鍵后讀掃描碼一個(gè)鍵后讀掃描碼) ),這些也要很快完成,因此由中斷,這些也要很快完成,因此由中斷處理程序立即執(zhí)行,不過一般在開中斷的情況下處理程序立即執(zhí)行,不過一般在開中斷的情況下50I/O中斷處理中斷處理1.1. LinuxLinux中把緊隨中斷要執(zhí)行的操作分為三類中把緊隨中斷要執(zhí)行的操作分為三類 非緊急可延遲的非緊急可延遲的(noncritical deferrable)(noncritical deferrable) 這些操作可以被延遲較長的時(shí)間間隔而不影響內(nèi)核操這

46、些操作可以被延遲較長的時(shí)間間隔而不影響內(nèi)核操作,有興趣的進(jìn)程將會(huì)等待數(shù)據(jù)。內(nèi)核用下半部分這作,有興趣的進(jìn)程將會(huì)等待數(shù)據(jù)。內(nèi)核用下半部分這樣一個(gè)機(jī)制來在一個(gè)更為合適的時(shí)機(jī)用獨(dú)立的函數(shù)來樣一個(gè)機(jī)制來在一個(gè)更為合適的時(shí)機(jī)用獨(dú)立的函數(shù)來執(zhí)行這些操作。執(zhí)行這些操作。 如把緩沖區(qū)內(nèi)容拷貝到某個(gè)進(jìn)程的地址空間如把緩沖區(qū)內(nèi)容拷貝到某個(gè)進(jìn)程的地址空間( (例如把例如把鍵盤緩沖區(qū)內(nèi)容發(fā)送到終端處理程序進(jìn)程鍵盤緩沖區(qū)內(nèi)容發(fā)送到終端處理程序進(jìn)程) )。51注冊(cè)中斷服務(wù)例程注冊(cè)中斷服務(wù)例程 1.1. 中斷號(hào)是一個(gè)寶貴且常常有限的資源。內(nèi)核維護(hù)一個(gè)中斷中斷號(hào)是一個(gè)寶貴且常常有限的資源。內(nèi)核維護(hù)一個(gè)中斷號(hào)的注冊(cè)表。號(hào)的注冊(cè)

47、表。2.2. 要使用中斷,就要進(jìn)行中斷號(hào)的申請(qǐng),也就是要使用中斷,就要進(jìn)行中斷號(hào)的申請(qǐng),也就是IRQ(Interrupt ReQuirement)IRQ(Interrupt ReQuirement)。3.3. 只有當(dāng)設(shè)備需要中斷的時(shí)候才申請(qǐng)占用一個(gè)只有當(dāng)設(shè)備需要中斷的時(shí)候才申請(qǐng)占用一個(gè)IRQIRQ,或者是,或者是在申請(qǐng)?jiān)谏暾?qǐng)IRQIRQ時(shí)采用共享中斷的方式,讓更多的設(shè)備使用中時(shí)采用共享中斷的方式,讓更多的設(shè)備使用中斷。斷。52注冊(cè)中斷服務(wù)例程注冊(cè)中斷服務(wù)例程 1.1. 在在 實(shí)現(xiàn)中斷注冊(cè)接口實(shí)現(xiàn)中斷注冊(cè)接口: :int request_irq(unsigned int irq, int req

48、uest_irq(unsigned int irq, irqreturn_t (irqreturn_t (* *handler)(int, void handler)(int, void * *, struct , struct pt_regs pt_regs * *),),unsigned long flags,unsigned long flags,const char const char * *dev_name, dev_name, void void * *dev_iddev_id););void free_irq(unsigned int irq, void void free_i

49、rq(unsigned int irq, void * *dev_id);dev_id);1.1. request_irq request_irq 的返回值是的返回值是 0 0 指示申請(qǐng)成功,為負(fù)值時(shí)表指示申請(qǐng)成功,為負(fù)值時(shí)表示錯(cuò)誤碼。函數(shù)返回示錯(cuò)誤碼。函數(shù)返回 -EBUSY -EBUSY 表示已經(jīng)有另一個(gè)驅(qū)動(dòng)占用表示已經(jīng)有另一個(gè)驅(qū)動(dòng)占用了所要申請(qǐng)的中斷線。了所要申請(qǐng)的中斷線。53注冊(cè)中斷服務(wù)例程注冊(cè)中斷服務(wù)例程 1.1. request_irqrequest_irq的參數(shù)說明:的參數(shù)說明: unsigned int irq, unsigned int irq, 要申請(qǐng)的中斷號(hào)。要申請(qǐng)的中斷號(hào)

50、。 irqreturn_t (irqreturn_t (* *handler)(int, void handler)(int, void * *, struct , struct pt_regs pt_regs * *),),要安裝的中斷處理函數(shù)指針。要安裝的中斷處理函數(shù)指針。 const char const char * *dev_name, dev_name, 用在用在 /proc/interrupts /proc/interrupts 中顯示中斷的擁有者。中顯示中斷的擁有者。54注冊(cè)中斷服務(wù)例程注冊(cè)中斷服務(wù)例程 1.1. request_irqrequest_irq的參數(shù)說明:的參數(shù)說

51、明: unsigned long flags,unsigned long flags,與中斷管理相關(guān)的位掩碼選項(xiàng)。與中斷管理相關(guān)的位掩碼選項(xiàng)。 FlagsFlags的每個(gè)位有不同含義的每個(gè)位有不同含義 SA_INTERRUPT SA_INTERRUPT 當(dāng)該位被設(shè)置時(shí),當(dāng)該位被設(shè)置時(shí), 表示這是一個(gè)表示這是一個(gè)“快快速速”中斷。快速中斷處理例程運(yùn)行時(shí),屏蔽中斷。中斷??焖僦袛嗵幚砝踢\(yùn)行時(shí),屏蔽中斷。 SA_SHIRQ SA_SHIRQ 這個(gè)位表示中斷可以在設(shè)備間共享。這個(gè)位表示中斷可以在設(shè)備間共享。 void void * *dev_iddev_id這個(gè)指針用于共享的中斷號(hào)。做為驅(qū)動(dòng)程序的私

52、有數(shù)據(jù)這個(gè)指針用于共享的中斷號(hào)。做為驅(qū)動(dòng)程序的私有數(shù)據(jù)區(qū)(可用來識(shí)別那個(gè)設(shè)備產(chǎn)生的中斷)。不使用共享中區(qū)(可用來識(shí)別那個(gè)設(shè)備產(chǎn)生的中斷)。不使用共享中斷線方式時(shí),可設(shè)置為斷線方式時(shí),可設(shè)置為NULLNULL。55實(shí)現(xiàn)中斷處理例程實(shí)現(xiàn)中斷處理例程1.1. 中斷處理例程特別之處:中斷處理例程特別之處: 在中斷時(shí)間內(nèi)運(yùn)行,不能向用戶空間發(fā)送或者接收數(shù)據(jù)在中斷時(shí)間內(nèi)運(yùn)行,不能向用戶空間發(fā)送或者接收數(shù)據(jù)。 不能做任何導(dǎo)致休眠的操作。不能做任何導(dǎo)致休眠的操作。 不能調(diào)用不能調(diào)用scheduleschedule函數(shù)。函數(shù)。 無論快速還是慢速中斷處理例程,都應(yīng)該設(shè)計(jì)成執(zhí)行時(shí)無論快速還是慢速中斷處理例程,都應(yīng)該

53、設(shè)計(jì)成執(zhí)行時(shí)間盡可能短。間盡可能短。56實(shí)現(xiàn)中斷處理例程實(shí)現(xiàn)中斷處理例程1.1. 中斷處理函數(shù)的參數(shù)和返回值中斷處理函數(shù)的參數(shù)和返回值irqreturn_t (irqreturn_t (* *handler)(int irq, void handler)(int irq, void * *dev_id, dev_id, struct pt_regs struct pt_regs * *regs)regs) Irq Irq 中斷號(hào)中斷號(hào) Dev_id Dev_id 驅(qū)動(dòng)程序可用的數(shù)據(jù)區(qū),通??蓚鬟f指向描述驅(qū)動(dòng)程序可用的數(shù)據(jù)區(qū),通??蓚鬟f指向描述設(shè)備的數(shù)據(jù)結(jié)構(gòu)指針。設(shè)備的數(shù)據(jù)結(jié)構(gòu)指針。 struc

54、t pt_regs struct pt_regs * *regsregs,保存了處理器進(jìn)入中斷代碼之,保存了處理器進(jìn)入中斷代碼之前的前的cpucpu寄存器的值。一般驅(qū)動(dòng)可不要寄存器的值。一般驅(qū)動(dòng)可不要。57實(shí)現(xiàn)中斷處理例程實(shí)現(xiàn)中斷處理例程1.1. 啟動(dòng)和禁用中斷啟動(dòng)和禁用中斷 驅(qū)動(dòng)禁止特定中斷線的中斷:驅(qū)動(dòng)禁止特定中斷線的中斷: #include .#include . void disable_irq(int irq);void disable_irq(int irq); void enable_irq(int irq);void enable_irq(int irq); 禁止所有中斷禁止所

55、有中斷 void local_irq_save(unsigned long flags);void local_irq_save(unsigned long flags); local_irq_save local_irq_save 在當(dāng)前處理器上禁止中斷遞交在當(dāng)前處理器上禁止中斷遞交, , 在在保存當(dāng)前中斷狀態(tài)到保存當(dāng)前中斷狀態(tài)到 flagsflags。 void local_irq_disable(void);void local_irq_disable(void); local_irq_disable local_irq_disable 關(guān)閉本地中斷遞交而不保存狀態(tài)關(guān)閉本地中斷遞交而不保

56、存狀態(tài);58實(shí)現(xiàn)中斷處理例程實(shí)現(xiàn)中斷處理例程1.1. 打開中斷打開中斷: : void local_irq_restore(unsigned long flags);void local_irq_restore(unsigned long flags);恢復(fù)由恢復(fù)由 local_irq_save local_irq_save 存儲(chǔ)于存儲(chǔ)于 flags flags 的狀態(tài)的狀態(tài), , 而而 local_irq_enable local_irq_enable 無條件打開中斷無條件打開中斷. . void local_irq_enable(void);void local_irq_enable(vo

57、id);59頂半部和底頂半部和底半部半部1.1. 中斷處理的一個(gè)主要問題是如何在處理中進(jìn)行長時(shí)間的任中斷處理的一個(gè)主要問題是如何在處理中進(jìn)行長時(shí)間的任務(wù)。響應(yīng)一次設(shè)備中斷需要完成一定數(shù)量的工作,但是中務(wù)。響應(yīng)一次設(shè)備中斷需要完成一定數(shù)量的工作,但是中斷處理需要很快完成并且不使中斷阻塞太長。斷處理需要很快完成并且不使中斷阻塞太長。2.2. LinuxLinux把中斷處理例程分兩部分:把中斷處理例程分兩部分: 頂部分:實(shí)際響應(yīng)中斷的例程。頂部分:實(shí)際響應(yīng)中斷的例程。 底部分:被頂部分調(diào)用,通過開中斷的方式進(jìn)行。兩種底部分:被頂部分調(diào)用,通過開中斷的方式進(jìn)行。兩種機(jī)制實(shí)現(xiàn):機(jī)制實(shí)現(xiàn): Tasklet

58、Tasklet 工作隊(duì)列工作隊(duì)列work queuework queue60頂半部和底半部頂半部和底半部1.1. 頂半部頂半部 頂半部的功能是頂半部的功能是“登記中斷登記中斷”,當(dāng)一個(gè)中斷發(fā)生時(shí),它,當(dāng)一個(gè)中斷發(fā)生時(shí),它進(jìn)行相應(yīng)地硬件讀寫后就把中斷例程的下半部掛到該設(shè)進(jìn)行相應(yīng)地硬件讀寫后就把中斷例程的下半部掛到該設(shè)備的底半部執(zhí)行隊(duì)列中去。備的底半部執(zhí)行隊(duì)列中去。 頂半部執(zhí)行的速度就會(huì)很快,可以服務(wù)更多的中斷請(qǐng)求頂半部執(zhí)行的速度就會(huì)很快,可以服務(wù)更多的中斷請(qǐng)求。2.2. 底半部底半部 僅有僅有“登記中斷登記中斷”是遠(yuǎn)遠(yuǎn)不夠的,因?yàn)橹袛嗟氖录赡苁沁h(yuǎn)遠(yuǎn)不夠的,因?yàn)橹袛嗟氖录赡芎軓?fù)雜。很復(fù)雜。Li

59、nuxLinux引入了一個(gè)底半部,來完成中斷事件的引入了一個(gè)底半部,來完成中斷事件的絕大多數(shù)使命。絕大多數(shù)使命。 底半部和頂半部最大的不同是底半部是可中斷的,而頂?shù)装氩亢晚敯氩孔畲蟮牟煌堑装氩渴强芍袛嗟模敯氩渴遣豢芍袛嗟?,底半部幾乎做了中斷處理程序所有半部是不可中斷的,底半部幾乎做了中斷處理程序所有的事情,而且可以被新的中斷打斷!的事情,而且可以被新的中斷打斷?底半部則相對(duì)來說并不是非常緊急的,通常還是比較耗底半部則相對(duì)來說并不是非常緊急的,通常還是比較耗時(shí)的,因此由系統(tǒng)自行安排運(yùn)行時(shí)機(jī),不在中斷服務(wù)上時(shí)的,因此由系統(tǒng)自行安排運(yùn)行時(shí)機(jī),不在中斷服務(wù)上下文中執(zhí)行。下文中執(zhí)行。 611.

60、軟中斷和軟中斷和 tasklet 的關(guān)系如下圖:的關(guān)系如下圖: 小任務(wù)機(jī)制小任務(wù)機(jī)制tasklet62小任務(wù)機(jī)制小任務(wù)機(jī)制tasklet1.1. ksoftirqdksoftirqd是一個(gè)后臺(tái)運(yùn)行的內(nèi)核線程,它會(huì)周期的是一個(gè)后臺(tái)運(yùn)行的內(nèi)核線程,它會(huì)周期的遍歷軟中斷的向量列表,如果發(fā)現(xiàn)哪個(gè)軟中斷向量被遍歷軟中斷的向量列表,如果發(fā)現(xiàn)哪個(gè)軟中斷向量被掛起了(掛起了( pend pend ),就執(zhí)行對(duì)應(yīng)的處理函數(shù)。),就執(zhí)行對(duì)應(yīng)的處理函數(shù)。2.2. tasklet tasklet 所對(duì)應(yīng)的處理函數(shù)就是所對(duì)應(yīng)的處理函數(shù)就是tasklet_actiontasklet_action,這,這個(gè)處理函數(shù)在系統(tǒng)啟

溫馨提示

  • 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ì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論