




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、Linux 驅(qū)動學(xué)習(xí)總結(jié)匯報驅(qū)動學(xué)習(xí)總結(jié)匯報 2016年年11月月12日日內(nèi)核模塊內(nèi)核模塊BootloderBootloder并發(fā)控制并發(fā)控制中斷處理中斷處理設(shè)備驅(qū)動的結(jié)構(gòu)設(shè)備驅(qū)動的結(jié)構(gòu)Linux內(nèi)核重要子系統(tǒng)內(nèi)核重要子系統(tǒng)1.1. 系統(tǒng)調(diào)用接口系統(tǒng)調(diào)用接口2.2. 進程管理進程管理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ū)動驅(qū)動最簡單的嵌入式系統(tǒng)最簡單的嵌入式系統(tǒng)MTK的的Bootloader在嵌入式操作系統(tǒng)中,在嵌入式操作系統(tǒng)中,BootLoader是在操作系統(tǒng)內(nèi)是在操作系統(tǒng)內(nèi)核運行之前運行。可以初始化硬件設(shè)備、建立內(nèi)存空間核
2、運行之前運行??梢猿跏蓟布O(shè)備、建立內(nèi)存空間映射圖,從而將系統(tǒng)的軟硬件環(huán)境帶到一個合適狀態(tài),映射圖,從而將系統(tǒng)的軟硬件環(huán)境帶到一個合適狀態(tài),以便為最終調(diào)用操作系統(tǒng)內(nèi)核準備好正確的環(huán)境。以便為最終調(diào)用操作系統(tǒng)內(nèi)核準備好正確的環(huán)境。MTK的的bootloader有兩部分組成:有兩部分組成:(1)第第1部分部分bootloader,也就是也就是MTK內(nèi)部內(nèi)部(in-house)的的pre-loader,這部分依賴平臺。這部分依賴平臺。(2)第第2部分部分bootloader,也就是也就是LittleKernel,這部分依賴這部分依賴操作系統(tǒng),負責引導(dǎo)操作系統(tǒng),負責引導(dǎo)linux操作系統(tǒng)和操作系統(tǒng)和
3、Android框架??蚣?。源碼位置:源碼位置:vendormediatekproprietarybootablebootloaderMTK的的Bootloader正常啟動的主要工作如下:正常啟動的主要工作如下:(1)設(shè)備上電后,設(shè)備上電后,BootROM開始運行。開始運行。(2)BootROM初始化軟件堆棧初始化軟件堆棧(softwarestack)、通信端口和可引導(dǎo)存儲通信端口和可引導(dǎo)存儲設(shè)備設(shè)備(比如比如NAND/EMMC)。(3)BootROM從存儲器中加載從存儲器中加載pre-loader到內(nèi)部到內(nèi)部SRAM(ISRAM)中,因中,因為這時候還沒有初始化外部的為這時候還沒有初始化外部的
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從存儲器中加載引導(dǎo)鏡像從存儲器中加載引導(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)默認情況下,默認情況下,UART4初始化波特率為初始化波特率為9216000bps和用于調(diào)試和用于調(diào)試信息的輸出,信息的輸出,UART1初始化為初始化為1152
6、00bps和作為和作為UARTMETA端口。端口。但也可以使用但也可以使用UART1作為調(diào)試或是作為調(diào)試或是UARTMETA端口。端口。(3)計時器計時器(timer)模塊模塊這是個基本的模塊,用來計算硬件模塊所需要的延時或是超時時間。這是個基本的模塊,用來計算硬件模塊所需要的延時或是超時時間。(4)內(nèi)存模塊內(nèi)存模塊1)Pre-loader由由bootROM加載和在芯片組內(nèi)部的加載和在芯片組內(nèi)部的SRAM中執(zhí)行,因中執(zhí)行,因為外部的為外部的DRAM還沒有初始化。還沒有初始化。2)為了準備軟件整個可執(zhí)行環(huán)境,為了準備軟件整個可執(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)當通過當通過power按鍵開機后,按鍵開機后,pre-loader拉高拉高RTC的的PWBB來保持設(shè)來保持設(shè)備一直有電備一直有電(keepthedevice
8、alive)和繼續(xù)引導(dǎo)和繼續(xù)引導(dǎo)LK。2)RTC鬧鐘鬧鐘(alarm)有可能是設(shè)備開機的啟動源,對于這種情況,設(shè)有可能是設(shè)備開機的啟動源,對于這種情況,設(shè)備部需要按備部需要按power按鍵就可自動啟動。按鍵就可自動啟動。(8)USB模塊模塊當當USB線插入時,它初始化來和外部工具通信,比如用于升級線插入時,它初始化來和外部工具通信,比如用于升級系統(tǒng)的下載工具或是系統(tǒng)的下載工具或是META模式觸發(fā)器的模式觸發(fā)器的META工具。工具。(9)NAND模塊模塊(10)MSDC模塊模塊Pre-loader可以從可以從NANDflash或是或是EMMC中加載中加載LK,這兩者只,這兩者只能選擇其中一種來啟
9、動能選擇其中一種來啟動。LK中涉及的硬件部分中涉及的硬件部分LK是第是第2個個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)造一個干凈的環(huán)境。比如計時器模塊,配置硬件寄存器,這樣可創(chuàng)造一個干凈的環(huán)境。比如計時器模塊,在在LK中,計時器重新復(fù)位清零硬件計數(shù)來對計時進行復(fù)位。所有中,計時器重新復(fù)位清零硬件計數(shù)
10、來對計時進行復(fù)位。所有在在LK中需要初始化的列在下面:中需要初始化的列在下面:(1)計時器模塊計時器模塊通過復(fù)位硬件寄存器來復(fù)位計時。通過復(fù)位硬件寄存器來復(fù)位計時。(2)串口模塊串口模塊LK采用串口模塊來配置它的輸入采用串口模塊來配置它的輸入/輸出系統(tǒng),在這個模塊初始化輸出系統(tǒng),在這個模塊初始化后,我們可以使用后,我們可以使用LK提供的提供的“printf()”等函數(shù)來使用串口功能。等函數(shù)來使用串口功能。(3)I2C模塊模塊(4)PWM模塊模塊(5)PMIC模塊模塊(6)RTC模塊模塊和計時器模塊一樣,在和計時器模塊一樣,在U-Boot中,中,I2C/PMIC/RTC重新復(fù)位寄存器來重新復(fù)位寄
11、存器來復(fù)位這些模塊。復(fù)位這些模塊。(7)LED模塊模塊通過這通過這poweroffcharging個模塊,設(shè)備能夠通知用戶當前的充電狀態(tài)。個模塊,設(shè)備能夠通知用戶當前的充電狀態(tài)。(8)充電模塊充電模塊這個模塊負責關(guān)機充電這個模塊負責關(guān)機充電(poweroffcharging)、低電壓充電低電壓充電(lowercharginginthesystem)。(9)LCD模塊模塊使用這個模塊,設(shè)備能夠顯示使用這個模塊,設(shè)備能夠顯示logo或是任何通知的消息。或是任何通知的消息。(10)NAND模塊模塊因為因為U-Boot也需要從也需要從flash讀取鏡像讀取鏡像(比如內(nèi)核或是比如內(nèi)核或是ramdisk)
12、,所以有必要在所以有必要在U-Boot中初始化中初始化NAND相關(guān)的功能。相關(guān)的功能。(11)MSDC模塊模塊支持支持MSDC啟動啟動一些重要的數(shù)據(jù)結(jié)構(gòu)一些重要的數(shù)據(jù)結(jié)構(gòu)1.1. 大部分驅(qū)動程序涉及三個重要的內(nèi)核數(shù)據(jù)結(jié)構(gòu):大部分驅(qū)動程序涉及三個重要的內(nèi)核數(shù)據(jù)結(jié)構(gòu): 文件操作文件操作file_operationsfile_operations結(jié)構(gòu)體結(jié)構(gòu)體 文件對象文件對象filefile結(jié)構(gòu)體結(jié)構(gòu)體 索引節(jié)點索引節(jié)點inodeinode結(jié)構(gòu)體結(jié)構(gòu)體Linux設(shè)備驅(qū)動設(shè)備驅(qū)動LinuxLinux下設(shè)備的屬性下設(shè)備的屬性設(shè)備的類型:字符設(shè)備、塊設(shè)備、網(wǎng)絡(luò)設(shè)備設(shè)備的類型:字符設(shè)備、塊設(shè)備、網(wǎng)絡(luò)設(shè)備主設(shè)
13、備號:標識設(shè)備對應(yīng)的驅(qū)動程序。一般主設(shè)備號:標識設(shè)備對應(yīng)的驅(qū)動程序。一般“一個主設(shè)一個主設(shè)備號對應(yīng)一個驅(qū)動程序備號對應(yīng)一個驅(qū)動程序”次設(shè)備號:每個驅(qū)動程序負責管理它所驅(qū)動的幾個硬件次設(shè)備號:每個驅(qū)動程序負責管理它所驅(qū)動的幾個硬件實例,這些硬件實例則由次設(shè)備號來表示。同一驅(qū)動下實例,這些硬件實例則由次設(shè)備號來表示。同一驅(qū)動下的實例編號,用于確定設(shè)備文件所指的設(shè)備。的實例編號,用于確定設(shè)備文件所指的設(shè)備??赏ㄟ^可通過ls l “l(fā)s l “設(shè)備文件名設(shè)備文件名”命令查看設(shè)備的主次設(shè)備命令查看設(shè)備的主次設(shè)備號,以及設(shè)備的類型。號,以及設(shè)備的類型。18分配和釋放字符設(shè)備號分配和釋放字符設(shè)備號1.1.
14、編寫驅(qū)動程序要做的第一件事,為字符設(shè)備獲取一個設(shè)備編寫驅(qū)動程序要做的第一件事,為字符設(shè)備獲取一個設(shè)備號。號。2.2. 事先知道所需要的設(shè)備編號(主設(shè)備號)的情況:事先知道所需要的設(shè)備編號(主設(shè)備號)的情況: 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è)備編號值。是要分配的起始設(shè)備編號值。 firstfirst的
15、次設(shè)備號的次設(shè)備號通常設(shè)置為通常設(shè)置為0 0。 Count Count 所請求的連續(xù)設(shè)備編號的個數(shù)。所請求的連續(xù)設(shè)備編號的個數(shù)。 NameName設(shè)備名稱,指和該編號范圍建立關(guān)系的設(shè)備。設(shè)備名稱,指和該編號范圍建立關(guān)系的設(shè)備。 分配成功返回分配成功返回0 0。19分配和釋放字符設(shè)備號分配和釋放字符設(shè)備號1.1. 動態(tài)分配設(shè)備編號(主要是主設(shè)備號)動態(tài)分配設(shè)備編號(主要是主設(shè)備號) 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 是一個僅用于輸出的參數(shù)是一個僅用于輸出的參數(shù), , 它在函數(shù)成功完成時保它在函數(shù)成功完成時保存已分配范圍的第一個編號。存已分配范圍的第一個編號。 baseminor baseminor 應(yīng)當是請求的第一個要用的次設(shè)備號,它常應(yīng)當是請求的第一個要用的次設(shè)備號,它常常是常是 0. 0. count count 和和 name name 參數(shù)跟參數(shù)跟request_chrdev_region request_chrdev_region 的一樣的
17、一樣. .20分配和釋放字符設(shè)備號分配和釋放字符設(shè)備號1.1. 不再使用時,釋放這些設(shè)備編號。使用以下函數(shù):不再使用時,釋放這些設(shè)備編號。使用以下函數(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è)備的注冊字符設(shè)備的注冊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ū)動的第二步就是注冊該設(shè)備。驅(qū)動的第二步就是注冊該設(shè)備。 包含包含頭文件。頭文件。 獲取一個獨立的獲取一個獨立的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è)備的注冊字符設(shè)備的注冊1.1. 編寫設(shè)備驅(qū)動的第二步就是注冊該設(shè)備。編寫設(shè)備驅(qū)動的第二步就是注冊該設(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 是這個設(shè)備對應(yīng)的第一個設(shè)備編號是這個設(shè)備對應(yīng)的第一個設(shè)備編號, , count count 是應(yīng)當關(guān)聯(lián)到設(shè)備的設(shè)備號的數(shù)目是應(yīng)當關(guān)聯(lián)到設(shè)備的設(shè)備號的數(shù)目. . 卸載字符設(shè)備時,調(diào)用相反的動作函數(shù):卸載字符設(shè)備
21、時,調(diào)用相反的動作函數(shù): void cdev_del(struct cdev void cdev_del(struct cdev * *dev);dev);23LinuxLinux設(shè)備驅(qū)動的并發(fā)控制設(shè)備驅(qū)動的并發(fā)控制24設(shè)備驅(qū)動的并發(fā)控制設(shè)備驅(qū)動的并發(fā)控制 1.1. 在驅(qū)動程序中,當多個線程同時訪問相同的資源時,可在驅(qū)動程序中,當多個線程同時訪問相同的資源時,可能會引發(fā)能會引發(fā)“競態(tài)競態(tài)”,必須對共享資源進行并發(fā)控制。,必須對共享資源進行并發(fā)控制。2.2. 并發(fā)和競態(tài)廣泛存在。并發(fā)和競態(tài)廣泛存在。3.3. 并發(fā)控制的目的:并發(fā)控制的目的:使得線程訪問共享資源的操作是原子操作。使得線程訪問共享資
22、源的操作是原子操作。1.1. 原子操作:原子操作:在執(zhí)行過程中不會被別的代碼路徑所中斷的操作。在執(zhí)行過程中不會被別的代碼路徑所中斷的操作。 1.1. 驅(qū)動程序中的全局變量是一種典型的共享資源。驅(qū)動程序中的全局變量是一種典型的共享資源。251.1.考慮一個非常簡單的共享資源的例子:一個全局整型變考慮一個非常簡單的共享資源的例子:一個全局整型變量和一個簡單的臨界區(qū),其中的操作僅僅是將整型變量量和一個簡單的臨界區(qū),其中的操作僅僅是將整型變量的值增加的值增加1 1: i+ i+ 1.1.該操作可以轉(zhuǎn)化成下面三條機器指令序列:該操作可以轉(zhuǎn)化成下面三條機器指令序列: 得到當前變量得到當前變量i i的值并拷
23、貝到一個寄存器中的值并拷貝到一個寄存器中 將寄存器中的值加將寄存器中的值加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ù)對內(nèi)核中的所有任務(wù)可見,所以內(nèi)核空間中的共享數(shù)據(jù)對內(nèi)核中的所有任務(wù)可見,所以當在內(nèi)核中訪問數(shù)據(jù)時,就必須考慮是否會有其他內(nèi)核當在內(nèi)核中訪問數(shù)據(jù)時,就必須考慮是否會有其他內(nèi)核任務(wù)并發(fā)訪問的可能、是否會產(chǎn)生競爭條件、是否需要任務(wù)并發(fā)訪問的可能、是否會產(chǎn)生競爭條件、是否需要對數(shù)據(jù)同步。對數(shù)據(jù)同步。27確定保護對象確定保
24、護對象 找出哪些數(shù)據(jù)需要保護是關(guān)鍵所在找出哪些數(shù)據(jù)需要保護是關(guān)鍵所在 內(nèi)核任務(wù)的局部數(shù)據(jù)僅僅被它本身訪問,顯然不需要內(nèi)核任務(wù)的局部數(shù)據(jù)僅僅被它本身訪問,顯然不需要保護。保護。 如果數(shù)據(jù)只會被特定的進程訪問,也不需加鎖如果數(shù)據(jù)只會被特定的進程訪問,也不需加鎖 大多數(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ā)控制的機制并發(fā)控制的機制 中斷屏蔽,原子數(shù)操作,自旋鎖和信號量都是解決中斷屏蔽,原子數(shù)操作,自旋鎖和信號量都是解決并發(fā)問題的機制。并發(fā)問題的機制。 中斷屏蔽很少被單獨使用,原子操作只能針對整數(shù)中斷屏蔽很少被單獨使用,原子操作只能針對整數(shù)來進行。因此自旋鎖和信號量應(yīng)用最為廣泛。來進行。因此自旋鎖和信號量應(yīng)用最為廣泛。 291.1. 鎖機制可以避免競爭狀態(tài)正如門鎖和門一樣,門后的房間鎖機制可以避免競爭狀態(tài)正如門鎖和門一樣,門后的房間可想象成一個臨界區(qū)。可想象成一個臨界區(qū)。2.2. 在一段時間內(nèi),房間里只能有一個內(nèi)核任務(wù)存在,當一個在一段時間內(nèi),房間里只能有一個內(nèi)核任務(wù)存在,當一個任
26、務(wù)進入房間后,它會鎖住身后的房門;當它結(jié)束對共享任務(wù)進入房間后,它會鎖住身后的房門;當它結(jié)束對共享數(shù)據(jù)的操作后,就會走出房間,打開門鎖。如果另一個任數(shù)據(jù)的操作后,就會走出房間,打開門鎖。如果另一個任務(wù)在房門上鎖時來了務(wù)在房門上鎖時來了, ,那么它就必須等待房間內(nèi)的任務(wù)出那么它就必須等待房間內(nèi)的任務(wù)出來并打開門鎖后,才能進入房間。來并打開門鎖后,才能進入房間。 加鎖機制 301.1. 任何要訪問臨界資源的代碼首先都需要占住相應(yīng)的鎖,這任何要訪問臨界資源的代碼首先都需要占住相應(yīng)的鎖,這樣該鎖就能阻止來自其它內(nèi)核任務(wù)的并發(fā)訪問:樣該鎖就能阻止來自其它內(nèi)核任務(wù)的并發(fā)訪問: 任務(wù)任務(wù) 1 1 試圖鎖定隊
27、列 成功:獲得鎖 訪問隊列 為隊列解除鎖 任務(wù)任務(wù)2 2 試圖鎖定隊列失?。旱却?等待 等待 成功:獲得鎖 訪問隊列 為隊列解除鎖加鎖機制 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)用于中斷處理等部分。而對于單處理在內(nèi)核中大量應(yīng)用于中斷處理等部分。而對于單處理器來說,防止中斷處理中的并發(fā)可簡單采用關(guān)閉中斷器來說,防止中斷處理中的并發(fā)可簡單采用關(guān)閉中斷的方式,不需要自旋鎖。的方式,不需要自旋鎖。 2.2. 自旋鎖最多只能被一個內(nèi)核任務(wù)持有,若一個內(nèi)核任自旋鎖最多只能被一個內(nèi)核任務(wù)持有,若
33、一個內(nèi)核任務(wù)試圖請求一個已被持有的自旋鎖,那么這個任務(wù)就務(wù)試圖請求一個已被持有的自旋鎖,那么這個任務(wù)就會一直進行忙循環(huán),也就是旋轉(zhuǎn),等待鎖重新可用。會一直進行忙循環(huán),也就是旋轉(zhuǎn),等待鎖重新可用。 3.3. 自旋鎖可以在任何時刻防止多于一個的內(nèi)核任務(wù)同時自旋鎖可以在任何時刻防止多于一個的內(nèi)核任務(wù)同時進入臨界區(qū),因此這種鎖可有效地避免多處理器上并進入臨界區(qū),因此這種鎖可有效地避免多處理器上并發(fā)運行的內(nèi)核任務(wù)競爭共享資源。發(fā)運行的內(nèi)核任務(wù)競爭共享資源。 35自旋鎖自旋鎖1.1. 自旋鎖的初衷就是:自旋鎖的初衷就是:在短期間內(nèi)進行輕量級的鎖定。一個被爭用的自在短期間內(nèi)進行輕量級的鎖定。一個被爭用的自旋
34、鎖使得請求它的線程在等待鎖重新可用的期間進行旋鎖使得請求它的線程在等待鎖重新可用的期間進行自旋(特別浪費處理器時間),所以自旋鎖不應(yīng)該被自旋(特別浪費處理器時間),所以自旋鎖不應(yīng)該被持有時間過長。如果需要長時間鎖定的話持有時間過長。如果需要長時間鎖定的話, , 最好使用最好使用信號量。信號量。 36自旋鎖自旋鎖1.1. 自旋鎖防止在不同自旋鎖防止在不同CPUCPU上的執(zhí)行單元對共享資源的同上的執(zhí)行單元對共享資源的同時訪問,以及不同進程上下文互相搶占導(dǎo)致的對共享時訪問,以及不同進程上下文互相搶占導(dǎo)致的對共享資源的非同步訪問。資源的非同步訪問。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. 進入一個臨界區(qū)前進入一個臨界區(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. 釋放一個鎖:釋放一個鎖: 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信號量信號量1.1. LinuxLinux中的信號量是一種睡眠鎖。中的信號量是一種睡眠鎖。 如果有一個任務(wù)試圖獲得一個已被持有的信號量時如果有一個任務(wù)試圖獲得一個已被持有的信號量時,信號量會將其推入等待隊列,然后讓其睡眠。,信號量會將其推入等待隊列,然后讓其睡眠。 當持有信號量的進程將信號量釋放后,在等待隊列當持有信號量的進程將信號量釋放后,在等待隊列中的一個任務(wù)將被喚醒,從而便可以獲得這個信號中的一個任務(wù)將被喚醒,從而便可以獲得這個信號量。量。 信號量的睡眠特性,使得信號量適用于鎖會被長時信號量的睡眠特性,使得信號量適用于
39、鎖會被長時間持有的情況;間持有的情況;2.2. 信號量的操作信號量的操作 信號量支持兩個原子操作信號量支持兩個原子操作P()P()和和V()V(),前者做測試操,前者做測試操作,后者叫做增加操作。作,后者叫做增加操作。 LinuxLinux中分別叫做中分別叫做down()down()和和up()up()。 41信號量信號量42信號量信號量43Linux信號量的實現(xiàn)信號量的實現(xiàn)1.1. 內(nèi)核代碼必須包含內(nèi)核代碼必須包含 ,才能使用信,才能使用信號量。號量。2.2. 相關(guān)的類型是相關(guān)的類型是 struct semaphorestruct semaphore信號量的定義structsemaphore
40、atomic_tcount;intsleepers;wait_queue_head_twait; 44Linux信號量的實現(xiàn)信號量的實現(xiàn)1.1. 信號量的聲明和初始化信號量的聲明和初始化 直接創(chuàng)建一個信號量直接創(chuàng)建一個信號量 struct semaphore struct semaphore * * sem; sem; 接著使用接著使用 sema_init sema_init 來初始化這個信號量:來初始化這個信號量:void sema_init(struct semaphore void sema_init(struct semaphore * *sem, int sem, int val)v
41、al);1.1. 互斥模式的信號量聲明,內(nèi)核提供宏定義互斥模式的信號量聲明,內(nèi)核提供宏定義. . DECLARE_MUTEX(name);DECLARE_MUTEX(name);信號量初始化為信號量初始化為 1 1 DECLARE_MUTEX_LOCKED(name);DECLARE_MUTEX_LOCKED(name);信號量初始化為信號量初始化為0 045自旋鎖自旋鎖忙等待,無調(diào)度開銷;忙等待,無調(diào)度開銷;進程搶占被禁止;進程搶占被禁止;鎖定期間不能休眠;鎖定期間不能休眠;信號量信號量拿不到就切換進程,有調(diào)度開銷;拿不到就切換進程,有調(diào)度開銷;鎖定期間可以休眠;鎖定期間可以休眠;46Lin
42、ux的中斷處理47為什么會有中斷為什么會有中斷1.1. 中斷最初是為克服對中斷最初是為克服對I/OI/O接口控制采用程序查詢所帶來的接口控制采用程序查詢所帶來的處理器低效率而產(chǎn)生的。處理器低效率而產(chǎn)生的。 處理器速度一般比外設(shè)快很多處理器速度一般比外設(shè)快很多 用輪詢的方式來查詢設(shè)備的狀態(tài),用輪詢的方式來查詢設(shè)備的狀態(tài),CPUCPU效率不高,效率不高,CPUCPU和和外設(shè)不能并行工作。外設(shè)不能并行工作。 中斷機制讓中斷機制讓CPUCPU啟動設(shè)備后,就去處理其他任務(wù),只有啟動設(shè)備后,就去處理其他任務(wù),只有當外設(shè)真正完成數(shù)據(jù)傳輸?shù)臏蕚?,請求當外設(shè)真正完成數(shù)據(jù)傳輸?shù)臏蕚?,請求CPUCPU服務(wù)的時候服務(wù)
43、的時候,CPUCPU才轉(zhuǎn)過來處理外設(shè)的請求。才轉(zhuǎn)過來處理外設(shè)的請求。48中斷和異常中斷和異常1.1. 外部中斷:外部中斷:外部設(shè)備所發(fā)出的外部設(shè)備所發(fā)出的I/OI/O請求。請求。1.1. 隨著計算機系統(tǒng)結(jié)構(gòu)的不斷改進以及應(yīng)用技術(shù)的日益提高隨著計算機系統(tǒng)結(jié)構(gòu)的不斷改進以及應(yīng)用技術(shù)的日益提高,中斷的適用范圍也隨之擴大,出現(xiàn)了所謂的內(nèi)部中斷(,中斷的適用范圍也隨之擴大,出現(xiàn)了所謂的內(nèi)部中斷(或叫異常)?;蚪挟惓#?。2.2. 異常:異常:為解決機器運行時所出現(xiàn)的某些隨機事件及編程方便而出為解決機器運行時所出現(xiàn)的某些隨機事件及編程方便而出現(xiàn)的?,F(xiàn)的。49I/O中斷處理中斷處理1.1. 為了保證系統(tǒng)對外部
44、的響應(yīng),一個中斷處理程序必須被盡為了保證系統(tǒng)對外部的響應(yīng),一個中斷處理程序必須被盡快的完成。因此,把所有的操作都放在中斷處理程序中并快的完成。因此,把所有的操作都放在中斷處理程序中并不合適不合適2.2. LinuxLinux中把緊隨中斷要執(zhí)行的操作分為三類中把緊隨中斷要執(zhí)行的操作分為三類 緊急的緊急的(critical)(critical)一般關(guān)中斷運行。諸如對一般關(guān)中斷運行。諸如對PICPIC應(yīng)答中斷,對應(yīng)答中斷,對PICPIC或是硬件或是硬件控制器重新編程,或者修改由設(shè)備和處理器同時訪問的控制器重新編程,或者修改由設(shè)備和處理器同時訪問的數(shù)據(jù)數(shù)據(jù) 非緊急的非緊急的(noncritical)(
45、noncritical)如修改那些只有處理器才會訪問的數(shù)據(jù)結(jié)構(gòu)如修改那些只有處理器才會訪問的數(shù)據(jù)結(jié)構(gòu)( (例如按下例如按下一個鍵后讀掃描碼一個鍵后讀掃描碼) ),這些也要很快完成,因此由中斷,這些也要很快完成,因此由中斷處理程序立即執(zhí)行,不過一般在開中斷的情況下處理程序立即執(zhí)行,不過一般在開中斷的情況下50I/O中斷處理中斷處理1.1. LinuxLinux中把緊隨中斷要執(zhí)行的操作分為三類中把緊隨中斷要執(zhí)行的操作分為三類 非緊急可延遲的非緊急可延遲的(noncritical deferrable)(noncritical deferrable) 這些操作可以被延遲較長的時間間隔而不影響內(nèi)核操這
46、些操作可以被延遲較長的時間間隔而不影響內(nèi)核操作,有興趣的進程將會等待數(shù)據(jù)。內(nèi)核用下半部分這作,有興趣的進程將會等待數(shù)據(jù)。內(nèi)核用下半部分這樣一個機制來在一個更為合適的時機用獨立的函數(shù)來樣一個機制來在一個更為合適的時機用獨立的函數(shù)來執(zhí)行這些操作。執(zhí)行這些操作。 如把緩沖區(qū)內(nèi)容拷貝到某個進程的地址空間如把緩沖區(qū)內(nèi)容拷貝到某個進程的地址空間( (例如把例如把鍵盤緩沖區(qū)內(nèi)容發(fā)送到終端處理程序進程鍵盤緩沖區(qū)內(nèi)容發(fā)送到終端處理程序進程) )。51注冊中斷服務(wù)例程注冊中斷服務(wù)例程 1.1. 中斷號是一個寶貴且常常有限的資源。內(nèi)核維護一個中斷中斷號是一個寶貴且常常有限的資源。內(nèi)核維護一個中斷號的注冊表。號的注冊
47、表。2.2. 要使用中斷,就要進行中斷號的申請,也就是要使用中斷,就要進行中斷號的申請,也就是IRQ(Interrupt ReQuirement)IRQ(Interrupt ReQuirement)。3.3. 只有當設(shè)備需要中斷的時候才申請占用一個只有當設(shè)備需要中斷的時候才申請占用一個IRQIRQ,或者是,或者是在申請在申請IRQIRQ時采用共享中斷的方式,讓更多的設(shè)備使用中時采用共享中斷的方式,讓更多的設(shè)備使用中斷。斷。52注冊中斷服務(wù)例程注冊中斷服務(wù)例程 1.1. 在在 實現(xiàn)中斷注冊接口實現(xiàn)中斷注冊接口: :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 指示申請成功,為負值時表指示申請成功,為負值時表示錯誤碼。函數(shù)返回示錯誤碼。函數(shù)返回 -EBUSY -EBUSY 表示已經(jīng)有另一個驅(qū)動占用表示已經(jīng)有另一個驅(qū)動占用了所要申請的中斷線。了所要申請的中斷線。53注冊中斷服務(wù)例程注冊中斷服務(wù)例程 1.1. request_irqrequest_irq的參數(shù)說明:的參數(shù)說明: unsigned int irq, unsigned int irq, 要申請的中斷號。要申請的中斷號
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注冊中斷服務(wù)例程注冊中斷服務(wù)例程 1.1. request_irqrequest_irq的參數(shù)說明:的參數(shù)說
51、明: unsigned long flags,unsigned long flags,與中斷管理相關(guān)的位掩碼選項。與中斷管理相關(guān)的位掩碼選項。 FlagsFlags的每個位有不同含義的每個位有不同含義 SA_INTERRUPT SA_INTERRUPT 當該位被設(shè)置時,當該位被設(shè)置時, 表示這是一個表示這是一個“快快速速”中斷??焖僦袛嗵幚砝踢\行時,屏蔽中斷。中斷。快速中斷處理例程運行時,屏蔽中斷。 SA_SHIRQ SA_SHIRQ 這個位表示中斷可以在設(shè)備間共享。這個位表示中斷可以在設(shè)備間共享。 void void * *dev_iddev_id這個指針用于共享的中斷號。做為驅(qū)動程序的私
52、有數(shù)據(jù)這個指針用于共享的中斷號。做為驅(qū)動程序的私有數(shù)據(jù)區(qū)(可用來識別那個設(shè)備產(chǎn)生的中斷)。不使用共享中區(qū)(可用來識別那個設(shè)備產(chǎn)生的中斷)。不使用共享中斷線方式時,可設(shè)置為斷線方式時,可設(shè)置為NULLNULL。55實現(xiàn)中斷處理例程實現(xiàn)中斷處理例程1.1. 中斷處理例程特別之處:中斷處理例程特別之處: 在中斷時間內(nèi)運行,不能向用戶空間發(fā)送或者接收數(shù)據(jù)在中斷時間內(nèi)運行,不能向用戶空間發(fā)送或者接收數(shù)據(jù)。 不能做任何導(dǎo)致休眠的操作。不能做任何導(dǎo)致休眠的操作。 不能調(diào)用不能調(diào)用scheduleschedule函數(shù)。函數(shù)。 無論快速還是慢速中斷處理例程,都應(yīng)該設(shè)計成執(zhí)行時無論快速還是慢速中斷處理例程,都應(yīng)該
53、設(shè)計成執(zhí)行時間盡可能短。間盡可能短。56實現(xiàn)中斷處理例程實現(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 中斷號中斷號 Dev_id Dev_id 驅(qū)動程序可用的數(shù)據(jù)區(qū),通常可傳遞指向描述驅(qū)動程序可用的數(shù)據(jù)區(qū),通常可傳遞指向描述設(shè)備的數(shù)據(jù)結(jié)構(gòu)指針。設(shè)備的數(shù)據(jù)結(jié)構(gòu)指針。 struc
54、t pt_regs struct pt_regs * *regsregs,保存了處理器進入中斷代碼之,保存了處理器進入中斷代碼之前的前的cpucpu寄存器的值。一般驅(qū)動可不要寄存器的值。一般驅(qū)動可不要。57實現(xiàn)中斷處理例程實現(xiàn)中斷處理例程1.1. 啟動和禁用中斷啟動和禁用中斷 驅(qū)動禁止特定中斷線的中斷:驅(qū)動禁止特定中斷線的中斷: #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 在當前處理器上禁止中斷遞交在當前處理器上禁止中斷遞交, , 在在保存當前中斷狀態(tài)到保存當前中斷狀態(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實現(xiàn)中斷處理例程實現(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 存儲于存儲于 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. 中斷處理的一個主要問題是如何在處理中進行長時間的任中斷處理的一個主要問題是如何在處理中進行長時間的任務(wù)。響應(yīng)一次設(shè)備中斷需要完成一定數(shù)量的工作,但是中務(wù)。響應(yīng)一次設(shè)備中斷需要完成一定數(shù)量的工作,但是中斷處理需要很快完成并且不使中斷阻塞太長。斷處理需要很快完成并且不使中斷阻塞太長。2.2. LinuxLinux把中斷處理例程分兩部分:把中斷處理例程分兩部分: 頂部分:實際響應(yīng)中斷的例程。頂部分:實際響應(yīng)中斷的例程。 底部分:被頂部分調(diào)用,通過開中斷的方式進行。兩種底部分:被頂部分調(diào)用,通過開中斷的方式進行。兩種機制實現(xiàn):機制實現(xiàn): Tasklet
58、Tasklet 工作隊列工作隊列work queuework queue60頂半部和底半部頂半部和底半部1.1. 頂半部頂半部 頂半部的功能是頂半部的功能是“登記中斷登記中斷”,當一個中斷發(fā)生時,它,當一個中斷發(fā)生時,它進行相應(yīng)地硬件讀寫后就把中斷例程的下半部掛到該設(shè)進行相應(yīng)地硬件讀寫后就把中斷例程的下半部掛到該設(shè)備的底半部執(zhí)行隊列中去。備的底半部執(zhí)行隊列中去。 頂半部執(zhí)行的速度就會很快,可以服務(wù)更多的中斷請求頂半部執(zhí)行的速度就會很快,可以服務(wù)更多的中斷請求。2.2. 底半部底半部 僅有僅有“登記中斷登記中斷”是遠遠不夠的,因為中斷的事件可能是遠遠不夠的,因為中斷的事件可能很復(fù)雜。很復(fù)雜。Li
59、nuxLinux引入了一個底半部,來完成中斷事件的引入了一個底半部,來完成中斷事件的絕大多數(shù)使命。絕大多數(shù)使命。 底半部和頂半部最大的不同是底半部是可中斷的,而頂?shù)装氩亢晚敯氩孔畲蟮牟煌堑装氩渴强芍袛嗟?,而頂半部是不可中斷的,底半部幾乎做了中斷處理程序所有半部是不可中斷的,底半部幾乎做了中斷處理程序所有的事情,而且可以被新的中斷打斷!的事情,而且可以被新的中斷打斷?底半部則相對來說并不是非常緊急的,通常還是比較耗底半部則相對來說并不是非常緊急的,通常還是比較耗時的,因此由系統(tǒng)自行安排運行時機,不在中斷服務(wù)上時的,因此由系統(tǒng)自行安排運行時機,不在中斷服務(wù)上下文中執(zhí)行。下文中執(zhí)行。 611.
60、軟中斷和軟中斷和 tasklet 的關(guān)系如下圖:的關(guān)系如下圖: 小任務(wù)機制小任務(wù)機制tasklet62小任務(wù)機制小任務(wù)機制tasklet1.1. ksoftirqdksoftirqd是一個后臺運行的內(nèi)核線程,它會周期的是一個后臺運行的內(nèi)核線程,它會周期的遍歷軟中斷的向量列表,如果發(fā)現(xiàn)哪個軟中斷向量被遍歷軟中斷的向量列表,如果發(fā)現(xiàn)哪個軟中斷向量被掛起了(掛起了( pend pend ),就執(zhí)行對應(yīng)的處理函數(shù)。),就執(zhí)行對應(yīng)的處理函數(shù)。2.2. tasklet tasklet 所對應(yīng)的處理函數(shù)就是所對應(yīng)的處理函數(shù)就是tasklet_actiontasklet_action,這,這個處理函數(shù)在系統(tǒng)啟
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 胃癌患者春節(jié)護理常規(guī)
- 自然教育大樹小班課程體系構(gòu)建
- 糖尿病足壞疽個案護理
- 醫(yī)美咨詢師接診技巧培訓(xùn)
- 學(xué)習(xí)方式訓(xùn)練培訓(xùn)
- 施工測量培訓(xùn)課件
- 餐飲店加盟權(quán)轉(zhuǎn)讓及接手合同范本
- 邴蕾離婚協(xié)議書全面考量子女教育與財產(chǎn)分配方案
- 桉樹種植基地土地流轉(zhuǎn)與種植合同
- 股票市場動態(tài)分析及投資策略咨詢協(xié)議
- 2025年中國大米加工行業(yè)發(fā)展?jié)摿Ψ治黾巴顿Y方向研究報告
- 2023-2024學(xué)年四川省廣安市高二下學(xué)期期末教學(xué)質(zhì)量檢測數(shù)學(xué)試題 (解析版)
- 夾具考試題及答案
- (高清版)DB31∕T 1530-2024 心理咨詢機構(gòu)服務(wù)規(guī)范
- 青海省消防救援總隊招聘消防文員筆試真題2024
- 便秘科普宣傳課件
- 浙江潔普斯清潔設(shè)備有限公司年產(chǎn)11萬臺清洗機技改項目環(huán)評報告
- 高校資產(chǎn)管理十五五規(guī)劃方案
- 會計電算化基礎(chǔ)知識2025年考試試卷及答案
- 2024年威寧自治縣在職在編教師考調(diào)真題
- 小學(xué)生反洗錢課件
評論
0/150
提交評論