嵌入式Linux操作系統(tǒng)第3章-3_第1頁
嵌入式Linux操作系統(tǒng)第3章-3_第2頁
嵌入式Linux操作系統(tǒng)第3章-3_第3頁
嵌入式Linux操作系統(tǒng)第3章-3_第4頁
嵌入式Linux操作系統(tǒng)第3章-3_第5頁
已閱讀5頁,還剩85頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

嵌入式Linux操作系統(tǒng)第3章基于Linux的嵌入式軟件開發(fā)

嵌入式軟件結(jié)構(gòu)1嵌入式軟件開發(fā)流程2嵌入式linux開發(fā)環(huán)境3嵌入式系統(tǒng)引導(dǎo)代碼4第3章基于Linux的嵌入式軟件開發(fā)linux內(nèi)核結(jié)構(gòu)及移植5嵌入式文件系統(tǒng)及移植6linux設(shè)備驅(qū)動概述7設(shè)備驅(qū)動程序接口8linux設(shè)備驅(qū)動開發(fā)流程9嵌入式文件系統(tǒng)及移植Linux支持多種文件系統(tǒng),包括EXT2、EXT3、vFat、NTFS、ISO9660、JFFS、RomFS和NFS等為了對各類文件系統(tǒng)進(jìn)行統(tǒng)一管理,Linux引入了虛擬文件系統(tǒng)VFS(VirtualFileSystem),為各類文件系統(tǒng)提供一個統(tǒng)一的操作界面和應(yīng)用編程接口。嵌入式文件系統(tǒng)及移植基于FLASH的文件系統(tǒng)1.JFFS22.YAFFS3.Cramfs

4.Romfs

嵌入式文件系統(tǒng)及移植JFFS2JFFS文件系統(tǒng)最早是由瑞典AxisCommunications公司基于Linux2.0的內(nèi)核為嵌入式系統(tǒng)開發(fā)的文件系統(tǒng)。JFFS2是RedHat公司基于JFFS開發(fā)的閃存文件系統(tǒng),最初是針對RedHat公司嵌入式產(chǎn)品eCos開發(fā)的嵌入式文件系統(tǒng),所以JFFS2也可以用在Linux,uCLinux中JFFS2主要用于NORFLASH,不適合NANDFLASH嵌入式文件系統(tǒng)及移植YAFFS/YAFFS2YAFFS/YAFFS2是專為嵌入式系統(tǒng)使用NAND型閃存而設(shè)計的一種日志型文件系統(tǒng)。與JFFS2相比,它減少了一些功能(例如不支持?jǐn)?shù)據(jù)壓縮),所以速度更快,掛載時間很短,對內(nèi)存的占用較小。另外,YAFFS/YAFFS2是跨平臺的文件系統(tǒng),除了Linux和eCos,還支持WinCE,pSOS和ThreadX等。嵌入式文件系統(tǒng)及移植Cramfs

Cramfs是Linux的創(chuàng)始人Linus

Torvalds參與開發(fā)的一種只讀的壓縮文件系統(tǒng)。它也基于MTD驅(qū)動程序。在Cramfs文件系統(tǒng)中,每一頁(4KB)被單獨壓縮,可以隨機(jī)頁訪問,其壓縮比高達(dá)2:1,為嵌入式系統(tǒng)節(jié)省大量的Flash存儲空間,使系統(tǒng)可通過更低容量的FLASH存儲相同的文件,從而降低系統(tǒng)成本。嵌入式文件系統(tǒng)及移植Romfs傳統(tǒng)型的Romfs文件系統(tǒng)是一種簡單的、緊湊的、只讀的文件系統(tǒng),不支持動態(tài)擦寫保存,按順序存放數(shù)據(jù),因而支持應(yīng)用程序以XIP(eXecuteInPlace,芯片內(nèi)執(zhí)行,指應(yīng)用程序可以直接在FLAH閃存內(nèi)運(yùn)行,不必再把代碼讀到系統(tǒng)RAM中)方式運(yùn)行,在系統(tǒng)運(yùn)行時節(jié)省RAM空間。uClinux系統(tǒng)通常采用Romfs文件系統(tǒng)嵌入式文件系統(tǒng)及移植基于RAM的文件系統(tǒng)1.Ramdisk

2.Ramfs/Tmpfs嵌入式文件系統(tǒng)及移植RamdiskRamdisk是將一部分固定大小的內(nèi)存當(dāng)作分區(qū)來使用。它并非一個實際的文件系統(tǒng),而是一種將實際的文件系統(tǒng)裝入內(nèi)存的機(jī)制,并且可以作為根文件系統(tǒng)。將一些經(jīng)常被訪問而又不會更改的文件(如只讀的根文件系統(tǒng))通過Ramdisk放在內(nèi)存中,可以明顯提高系統(tǒng)性能。在Linux的啟動階段,initrd提供了一套機(jī)制,可以將內(nèi)核映像和根文件系統(tǒng)一起載入內(nèi)存。嵌入式文件系統(tǒng)及移植Ramfs/Tmpfs

Ramfs/Tmpfs文件系統(tǒng)把所有的文件都放在RAM中,所以讀/寫操作發(fā)生在RAM中,可以用Ramfs/Tmpfs來存儲一些臨時性或經(jīng)常要修改的數(shù)據(jù),例如/tmp和/var目錄,這樣既避免了對Flash存儲器的讀寫損耗,也提高了數(shù)據(jù)讀寫速度。Ramfs/Tmpfs相對傳統(tǒng)的Ramdisk的不同之處主要在于:不能格式化,文件系統(tǒng)大小可隨所含文件內(nèi)容大小變化。Tmpfs的一個缺點是當(dāng)系統(tǒng)重新引導(dǎo)時會丟失所有數(shù)據(jù)嵌入式文件系統(tǒng)及移植網(wǎng)絡(luò)文件系統(tǒng)NFSNFS是由Sun開發(fā)并發(fā)展起來的一項在不同機(jī)器、不同操作系統(tǒng)之間通過網(wǎng)絡(luò)共享文件的技術(shù)。在嵌入式Linux系統(tǒng)的開發(fā)調(diào)試階段,可以利用該技術(shù)在主機(jī)上建立基于NFS的根文件系統(tǒng),掛載到嵌入式設(shè)備,可以很方便地修改根文件系統(tǒng)的內(nèi)容嵌入式文件系統(tǒng)及移植文件系統(tǒng)格式選擇的基本策略通常,當(dāng)設(shè)計根文件系統(tǒng)時,可以按如下幾點配置方案來解決文件系統(tǒng)的選擇:把任何在運(yùn)行時不需要進(jìn)行更新的文件放在cramFs文件系統(tǒng)中。因為CramFs的壓縮比高達(dá)2:1,節(jié)約存儲空間的效果是明顯的。如果應(yīng)用程序要求采用XIP方式運(yùn)行,則可以選擇采用RomFs文件系統(tǒng)。那些需要經(jīng)常讀/寫的目錄,例如/VAR、/tmp,應(yīng)該放在tmpfs文件系統(tǒng)中,以減少對Flash的擦寫次數(shù),延長flash的使用壽命。tmptfs文件系統(tǒng)中的變化在下次啟動后是不會保存的。嵌入式文件系統(tǒng)及移植對于那些需要進(jìn)行讀、寫,并且在下次啟動之后也能將更新信息保存的文件,則應(yīng)該放入日志型文件系統(tǒng)里。如果采用的是NOR型閃存,則應(yīng)選擇JFFs2文件系統(tǒng);如果是NAND閃存,則應(yīng)選擇YAFFS文件系統(tǒng)。嵌入式文件系統(tǒng)及移植混合型文件系統(tǒng)格式的設(shè)計方法綜合考慮存儲空間和系統(tǒng)可用性因素,適用于嵌入式系統(tǒng)的文件系統(tǒng)格式各有千秋,因此可以在嵌入式系統(tǒng)中采用混雜模式的文件系統(tǒng)格式嵌入式文件系統(tǒng)及移植嵌入式根文件系統(tǒng)的制作結(jié)合UP-NetARM2410-S試驗箱介紹Linux根文件系統(tǒng)的構(gòu)建過程1.文件系統(tǒng)方案2.文件系統(tǒng)構(gòu)建流程3.根文件系統(tǒng)的實現(xiàn)嵌入式文件系統(tǒng)及移植文件系統(tǒng)方案根文件系統(tǒng):根文件系統(tǒng)是系統(tǒng)啟動時掛載的第一個文件系統(tǒng),其他的文件系統(tǒng)需要在跟文件系統(tǒng)目錄中建立節(jié)點后再掛載。UP-NetARM2410-S有一個64M的NANDFLASH,根文件系統(tǒng)和用戶文件系統(tǒng)都建立在該flash的后大半部分。該flash的前小半部分用來存放bootloader

和kernel映像。根文件系統(tǒng)選用了Cramfs

文件系統(tǒng)格式。嵌入式文件系統(tǒng)及移植用戶文件系統(tǒng):由于Cramfs

為只讀文件系統(tǒng),為了得到可讀寫的文件系統(tǒng),用戶文件系統(tǒng)采用

YAFFS文件系統(tǒng)格式。用戶文件系統(tǒng)掛載于根文件系統(tǒng)下的/mnt/yaffs

目錄。臨時文件系統(tǒng):采用了Ramfs

文件系統(tǒng)。根目錄下的/var,/tmp

目錄為Ramfs

臨時文件系統(tǒng)的掛載點嵌入式文件系統(tǒng)及移植文件系統(tǒng)構(gòu)建流程在嵌人式Linux系統(tǒng)中混合使用Cramfs、YAFFS和Ramfs

三種文件系統(tǒng)的實現(xiàn)思路如下:1.配置內(nèi)核:將內(nèi)核對MTD,Cramfs,YAFFS以及Ramfs

文件系統(tǒng)的支持功能編譯進(jìn)內(nèi)核。2.劃分Flash分區(qū):對Flash物理空間進(jìn)行分區(qū),以便在不同的分區(qū)上存放不同的數(shù)據(jù),采用不同的文件系統(tǒng)格式;必要時編寫MAPS文件。今修改系統(tǒng)腳本,在系統(tǒng)啟動后利用腳本掛載文件系統(tǒng)。創(chuàng)建文件系統(tǒng)鏡像文件,利用工具生成文件系統(tǒng)鏡像文件,并通過Flash燒寫工具將鏡像文件燒寫到Flash物理空間。嵌入式文件系統(tǒng)及移植根文件系統(tǒng)的實現(xiàn)制作Cramfs

格式的根文件系統(tǒng):一個使用linux

內(nèi)核的嵌入式系統(tǒng)中的root文件系統(tǒng)必須包括支持完整linux

系統(tǒng)的全部東西,因此,它至少應(yīng)包括:基本文件系統(tǒng)結(jié)構(gòu),至少含有目錄/dev、/proc、/bin、/etc、/lib、/usr最基本的應(yīng)用程序,如sh、ls、cp、mv

等最低限度的配置文件,如inittab、fstab

等必要的設(shè)備支持:/dev/null、/dev/console、/dev/tty*、/dev/ttyS*、對應(yīng)flash分區(qū)的設(shè)備節(jié)點等基本程序運(yùn)行所需的函數(shù)庫:Glibclinux設(shè)備驅(qū)動概述linux設(shè)備驅(qū)動作用驅(qū)動程序DeviceDriver,全稱為“設(shè)備驅(qū)動程序”,是一種可以使計算機(jī)和設(shè)備通信的特殊程序,可以說相當(dāng)于硬件的接口,操作系統(tǒng)只有通過這個接口,才能控制硬件設(shè)備的工作,假如某設(shè)備的驅(qū)動程序未能正確安裝,便不能正常工作。Linux系統(tǒng)內(nèi)核通過設(shè)備驅(qū)動程序與外圍設(shè)備進(jìn)行交互,設(shè)備驅(qū)動程序是Linux內(nèi)核的一部分,它是一組數(shù)據(jù)結(jié)構(gòu)和函數(shù),這些數(shù)據(jù)結(jié)構(gòu)和函數(shù)通過定義的接口控制一個或多個設(shè)備。對應(yīng)用程序而言,設(shè)備驅(qū)動程序隱藏了設(shè)備的具體細(xì)節(jié),對各種不同設(shè)備提供一致的接口。不同于windows驅(qū)動程序,Linux設(shè)備驅(qū)動程序在與硬件設(shè)備之間建立了標(biāo)準(zhǔn)的抽象接口。通過這個接口,用戶可以像處理普通文件一樣,通過open,close,read,write等系統(tǒng)調(diào)用對設(shè)備進(jìn)行操作,如此一來也大大簡化了linux驅(qū)動程序的開發(fā)。linux設(shè)備驅(qū)動概述設(shè)備驅(qū)動程序的主要功能對設(shè)備進(jìn)行初始化。啟動或停止設(shè)備的運(yùn)行。把數(shù)據(jù)從內(nèi)核傳送到硬件和從硬件讀取數(shù)據(jù)。讀取應(yīng)用程序傳送給設(shè)備文件的數(shù)據(jù)和回送應(yīng)用程序請求的數(shù)據(jù)。檢測和處理設(shè)備出現(xiàn)的錯誤等。linux設(shè)備驅(qū)動概述設(shè)備驅(qū)動程序有如下特點:驅(qū)動程序是與設(shè)備相關(guān)的。驅(qū)動程序的代碼由內(nèi)核統(tǒng)一管理。驅(qū)動程序在具有特權(quán)級別的內(nèi)核態(tài)下運(yùn)行。設(shè)備驅(qū)動程序是輸入輸出系統(tǒng)的一部分。驅(qū)動程序是為某個進(jìn)程服務(wù)的,其執(zhí)行過程仍處在進(jìn)程運(yùn)行的過程中,即處于進(jìn)程的上下文中。若驅(qū)動程序需要等待設(shè)備的某種狀態(tài),它將阻塞當(dāng)前進(jìn)程,把進(jìn)程加入到該設(shè)備的等待隊列中。linux設(shè)備驅(qū)動概述linux設(shè)備驅(qū)動程序的基本結(jié)構(gòu)linux設(shè)備驅(qū)動概述應(yīng)用程序、庫、內(nèi)核、驅(qū)動程序的關(guān)系:應(yīng)用程序調(diào)用應(yīng)用程序函數(shù)庫完成功能、應(yīng)用程序以文件形式訪問各種資源、應(yīng)用程序函數(shù)庫、部分函數(shù)直接完成功能、部分函數(shù)通過系統(tǒng)調(diào)用。由內(nèi)核完成內(nèi)核處理系統(tǒng)調(diào)用。調(diào)用設(shè)備驅(qū)動程序,設(shè)備驅(qū)動直接與硬件通信。linux設(shè)備驅(qū)動概述linux設(shè)備驅(qū)動的分類Linux的方式看待設(shè)備可區(qū)分為三種基本設(shè)備:字符設(shè)備塊設(shè)備網(wǎng)絡(luò)設(shè)備linux設(shè)備驅(qū)動概述字符設(shè)備:一個字符(char)設(shè)備是一種可以當(dāng)作一個字節(jié)流來存取的設(shè)備(如同一個文件);一個字符驅(qū)動負(fù)責(zé)實現(xiàn)這種行為。這樣的驅(qū)動常常至少實現(xiàn)open,close,read,和write系統(tǒng)調(diào)用。文本控制臺(/dev/console)和串口(/dev/ttyS0)是字符設(shè)備的例子,因為它們很好地展現(xiàn)了流的抽象。字符設(shè)備通過文件系統(tǒng)結(jié)點來存取,例如/dev/tty1和/dev/lp0。在一個字符設(shè)備和一個普通文件之間唯一有關(guān)的不同就是,你經(jīng)常可以在普通文件中移來移去,但是大部分字符設(shè)備僅僅是數(shù)據(jù)通道,你只能順序存取。當(dāng)然,也存在看起來象數(shù)據(jù)區(qū)的字符設(shè)備,你可以在里面移來移去。例如,framegrabber經(jīng)常這樣,應(yīng)用程序可以使用mmap

或者lseek

存取整個要求的圖像。linux設(shè)備驅(qū)動概述塊設(shè)備如同字符設(shè)備,塊設(shè)備通過位于/dev目錄的文件系統(tǒng)結(jié)點來存取。一個塊設(shè)備(例如一個磁盤)應(yīng)該是可以駐有一個文件系統(tǒng)的。在大部分的Unix系統(tǒng)中,一個塊設(shè)備只能處理這樣的I/O操作,傳送一個或多個長度經(jīng)常是512字節(jié)(或一個更大的2的冪的數(shù))的整塊。Linux中則相反,允許應(yīng)用程序讀寫一個塊設(shè)備象一個字符設(shè)備一樣,它允許一次傳送任意數(shù)目的字節(jié)。結(jié)果就是,塊和字符設(shè)備的區(qū)別僅僅在內(nèi)核在內(nèi)部管理數(shù)據(jù)的方式上,并且因此在內(nèi)核/驅(qū)動的軟件接口上不同。如同一個字符設(shè)備,每個塊設(shè)備都通過一個文件系統(tǒng)結(jié)點被存取的,它們之間的區(qū)別對用戶是透明的。塊驅(qū)動和字符驅(qū)動相比,與內(nèi)核的接口完全不同。linux設(shè)備驅(qū)動概述網(wǎng)絡(luò)設(shè)備任何網(wǎng)絡(luò)事務(wù)都通過一個接口來進(jìn)行,就是說,一個能夠與其他主機(jī)交換數(shù)據(jù)的設(shè)備。通常,一個接口是一個硬件設(shè)備,但是它也可能是一個純粹的軟件設(shè)備,比如回環(huán)接口。一個網(wǎng)絡(luò)接口負(fù)責(zé)發(fā)送和接收數(shù)據(jù)報文,在內(nèi)核網(wǎng)絡(luò)子系統(tǒng)的驅(qū)動下,不必知道單個事務(wù)是如何映射到實際的被發(fā)送的報文上的。linux設(shè)備驅(qū)動概述字符設(shè)備與塊設(shè)備的主要區(qū)別是:在對字符設(shè)備發(fā)出讀/寫請求時,實際的硬件I/O一般緊接著發(fā)生。塊設(shè)備則不然,它利用一塊系統(tǒng)內(nèi)存作為緩沖區(qū),若用戶進(jìn)程對設(shè)備的請求能滿足用戶的要求,就返回請求的數(shù)據(jù);否則,就調(diào)用請求函數(shù)來進(jìn)行實際的I/O操作。塊設(shè)備主要是針對磁盤等慢速設(shè)備設(shè)計的,以免耗費(fèi)過多的CPU時間用來等待。網(wǎng)絡(luò)設(shè)備可以通過BSD套接口訪問數(shù)據(jù)。linux設(shè)備驅(qū)動概述linux設(shè)備文件和設(shè)備文件系統(tǒng)Linux是一種類Unix系統(tǒng),Unix的一個基本特點是“一切皆為文件”,它抽象了設(shè)備的處理,將所有的硬件設(shè)備都像普通文件一樣看待,也就是說硬件可以跟普通文件一樣來打開、關(guān)閉和讀寫。系統(tǒng)中設(shè)備都用一個設(shè)備特殊文件代表,叫做設(shè)備文件,設(shè)備類型、主次設(shè)備號是內(nèi)核與設(shè)備驅(qū)動程序通信時所使用的,但是對于開發(fā)應(yīng)用程序的用戶來說比較難于理解和記憶,所以Linux使用了設(shè)備文件的概念來統(tǒng)一對設(shè)備的訪問接口,在引入設(shè)備文件系統(tǒng)devfs之前Linux將設(shè)備文件放在/dev目錄下,設(shè)備的命名一般為設(shè)備文件名+數(shù)字或字母表示的子類,例如/dev/hda1、/dev/hda2等。linux設(shè)備驅(qū)動概述每個設(shè)備文件都有其文件屬性(c/b),表示是字符設(shè)備還是塊設(shè)備。另外每個文件都有2個設(shè)備號,第一個是主設(shè)備號,唯一標(biāo)識一個設(shè)備。主設(shè)備號相同的設(shè)備使用相同的驅(qū)動程序;第二個是從設(shè)備號,標(biāo)識使用同一個設(shè)備驅(qū)動程序的、不同的硬件設(shè)備。在Linux2.4內(nèi)核中引入了設(shè)備文件系統(tǒng)devfs,所有設(shè)備文件作為一個可以掛載的文件系統(tǒng),這樣就可以被文件系統(tǒng)進(jìn)行統(tǒng)一管理,從而設(shè)備文件就可以掛載到任何需要的地方。命名規(guī)則也發(fā)生了變化,一般將主設(shè)備建立一個目錄,再將具體的子設(shè)備文件建立在此目錄下。比如在UP-NETARM2410-S中的MTD設(shè)備為:/dev/mtdblock/0。設(shè)備驅(qū)動程序接口linux設(shè)備驅(qū)動的加載方式設(shè)備驅(qū)動程序是Linux內(nèi)核的重要組成部分,控制了操作系統(tǒng)和硬件設(shè)備之間的交互。Linux的設(shè)備管理是和文件系統(tǒng)緊密結(jié)合的,各種設(shè)備都以文件的形式存放在/dev目錄下,成為設(shè)備文件。應(yīng)用程序可以打開、關(guān)閉、讀寫這些設(shè)備文件,對設(shè)備的操作就像操作普通的數(shù)據(jù)文件一樣簡便設(shè)備驅(qū)動程序接口在LINUX下加載驅(qū)動程序可以采用兩種方式。靜態(tài)加載動態(tài)加載靜態(tài)加載就是把驅(qū)動程序直接編譯到內(nèi)核里,在執(zhí)行makemenuconfig命令進(jìn)行內(nèi)核配置裁剪時,在窗口中可以選擇是否編譯入內(nèi)核,還是放入/lib/modules/下相應(yīng)內(nèi)核版本目錄中,還是不選。驅(qū)動編譯進(jìn)內(nèi)核后,系統(tǒng)啟動后可以直接調(diào)用。靜態(tài)加載的缺點是調(diào)試起來比較麻煩,每次修改一個地方都要重新編譯下載內(nèi)核,效率較低。設(shè)備驅(qū)動程序接口動態(tài)加載利用了LINUX的module特性,可以在系統(tǒng)啟動后用insmod命令把驅(qū)動程序(.o文件)添加上去,在不需要的時候用rmmod命令來卸載。在臺式機(jī)上一般采用動態(tài)加載的方式。在嵌入式產(chǎn)品里可以先用動態(tài)加載的方式來調(diào)試,調(diào)試完畢后再編譯到內(nèi)核里。動態(tài)加載動態(tài)加載是將驅(qū)動模塊加載到內(nèi)核中,而不能放入/lib/modules/下。下圖是將一個設(shè)備驅(qū)動模塊動態(tài)掛接、卸載和系統(tǒng)調(diào)用的全過程。設(shè)備驅(qū)動程序接口設(shè)備驅(qū)動程序接口下面看一下有關(guān)模塊的命令,在加載驅(qū)動程序要用到:lsmod、modprob、insmod、rmmod、modinfo。lsmod命令:lsmod查看當(dāng)前加載到內(nèi)核中的所有驅(qū)動模塊,同時提供其它一些信息,比如其它模塊是否在使用另一個模塊。例如:#lsmod

(與cat/proc/modules得出的內(nèi)容是一致的)ModuleSizeUsedbyNottaintedradeon1153641agpgart566643設(shè)備驅(qū)動程序接口rmmod命令:如果后面有autoclean,則該模塊可以被rmmod-a命令自動清洗。rmmod-a命令會將目前有autoclean的模塊卸載,如果這時候某個模塊未被使用,則將該模塊標(biāo)記為autoclean。如果在行尾的[]括號內(nèi)有模塊名稱,則括號內(nèi)的模塊就依賴于該模塊。例如:

cdrom341440[sr_mod

ide-cd]其中ide-cd及sr_mod模塊就依賴于cdrom模塊。系統(tǒng)的模塊文件保存在/lib/modules/2.6.XXX/kerne目錄中,根據(jù)分類分別在fs、net等子目錄中,他們的互相依存關(guān)系則保存在/lib/modules/2.6.XXX/modules.dep文件中。需要注意,該文件不僅寫入了模塊的依存關(guān)系,同時內(nèi)核查找模塊也是在這個文件中。設(shè)備驅(qū)動程序接口insmod命令:是插入模塊的命令,但是它不會自動解決依存關(guān)系,所以一般加載內(nèi)核模塊時使用的命令為modprobe。使用modprobe命令,可以智能插入模塊,它可以根據(jù)模塊間依存關(guān)系,以及/etc/modules.conf文件中的內(nèi)容智能插入模塊。modinfo命令:用來查看模塊信息,如modinfo-dcdrom。設(shè)備驅(qū)動程序接口設(shè)備驅(qū)動程序接口Linux操作系統(tǒng)通過系統(tǒng)調(diào)用和硬件中斷完成從用戶空間到內(nèi)核空間的控制轉(zhuǎn)移。設(shè)備驅(qū)動模塊的功能就是擴(kuò)展內(nèi)核的功能,主要完成兩部分任務(wù):一個是系統(tǒng)調(diào)用,另一個是處理中斷。系統(tǒng)調(diào)用部分則是對設(shè)備的操作過程,比如open,read,write,ioctl等操作,設(shè)備驅(qū)動程序所提供的這組入口點由幾個結(jié)構(gòu)向系統(tǒng)進(jìn)行說明,分別是file_operations數(shù)據(jù)結(jié)構(gòu)、inode數(shù)據(jù)結(jié)構(gòu)和file數(shù)據(jù)結(jié)構(gòu)。內(nèi)核內(nèi)部通過file結(jié)構(gòu)識別設(shè)備,通過file_operations數(shù)據(jù)結(jié)構(gòu)提供文件系統(tǒng)的入口點函數(shù),也就是訪問設(shè)備驅(qū)動的函數(shù),結(jié)構(gòu)中的每一個成員都對應(yīng)著一個系統(tǒng)調(diào)用。設(shè)備驅(qū)動程序接口在嵌入式系統(tǒng)的開發(fā)中,我們一般僅僅實現(xiàn)其中幾個接口函數(shù):read、write、open、ioctl及release就可以完成應(yīng)用系統(tǒng)需要的功能。寫驅(qū)動程序的任務(wù)之一就是完成file_operations中的函數(shù)指針。通常所說的設(shè)備驅(qū)動程序接口是指結(jié)構(gòu)file_operations{},它定義在include/linux/fs.h

中。file_operations

數(shù)據(jù)結(jié)構(gòu)定義如下:設(shè)備驅(qū)動程序接口struct

file_operations{structmodule*owner;loff_t(*llseek)(structfile*,loff_t,int);ssize_t(*read)(structfile*,char*,size_t,loff_t*);ssize_t(*write)(structfile*,constchar*,size_t,loff_t*);int(*readdir)(structfile*,void*,filldir_t);unsignedint(*poll)(structfile*,struct

poll_table_struct*);int(*ioctl)(struct

inode*,structfile*,unsignedint,unsignedlong);int(*mmap)(structfile*,struct

vm_area_struct*);int(*open)(struct

inode*,structfile*);int(*flush)(structfile*);int(*release)(struct

inode*,structfile*);int(*fsync)(structfile*,struct

dentry*,int

datasync);int(*fasync)(int,structfile*,int);int(*lock)(structfile*,int,struct

file_lock*);…#ifdefMAGIC_ROM_PTR

int(*romptr)(structfile*,struct

vm_area_struct*);#endif/*MAGIC_ROM_PTR*/};設(shè)備驅(qū)動程序接口常用的操作包括以下幾種:*lseek,移動文件指針的位置,只用于隨機(jī)存取設(shè)備。*read,進(jìn)行讀操作,參數(shù)buf為存放讀取結(jié)果的緩沖區(qū),count為所要讀取的數(shù)據(jù)長度。返回值為負(fù)表示讀取操作發(fā)生錯誤;否則,返回實際讀取的字節(jié)數(shù)。對于字符型,要求讀取的字節(jié)數(shù)和返回的實際讀取字節(jié)數(shù)都必須是inode-i_blksize的倍數(shù)。*write,進(jìn)行寫操作,與read類似。*readdir,取得下一個目錄入口點,只有與文件系統(tǒng)相關(guān)的設(shè)備程序才使用。設(shè)備驅(qū)動程序接口*select,進(jìn)行選擇操作。如果驅(qū)動程序沒有提供select入口,select操作會認(rèn)為設(shè)備已經(jīng)準(zhǔn)備好進(jìn)行任何I/O操作。*ioctl,進(jìn)行讀、寫以外的其他操作,參數(shù)cmd為自定義的命令。*mmap,用于把設(shè)備的內(nèi)容映射到地址空間,一般只有塊設(shè)備驅(qū)動程序使用。*open,打開設(shè)備準(zhǔn)備進(jìn)行I/O操作。返回0表示打開成功,返回負(fù)數(shù)表示失敗。如果驅(qū)動程序沒有提供open入口,則只要/dev/driver文件存在就認(rèn)為打開成功。*release,即close操作。設(shè)備驅(qū)動程序接口linux設(shè)備控制方式1.查詢方式2.中斷方式3.直接訪問內(nèi)存(DMA)方式設(shè)備驅(qū)動程序接口1.查詢方式設(shè)備驅(qū)動程序通過設(shè)備的I/O端口空間,以及存儲器空間完成數(shù)據(jù)的交換。如網(wǎng)卡一般將自己的內(nèi)部寄存器映射為設(shè)備的I/O端口,而顯卡則利用大量的存儲器空間作為視頻信息存儲空間。利用這些地址空間,驅(qū)動程序可以向外設(shè)發(fā)送指定的操作指令。通常來講,由于外設(shè)的操作耗時較長,因此,當(dāng)處理器實際執(zhí)行了操作指令之后,驅(qū)動程序可采用查詢方式等待外設(shè)完成操作。設(shè)備驅(qū)動程序接口查詢方式的優(yōu)點是硬件開銷小,使用起來比較簡單。但在此方式下,CPU要不斷地查詢外設(shè)的狀態(tài),當(dāng)外設(shè)未準(zhǔn)備好時,就只能循環(huán)等待,不能執(zhí)行其他程序,這樣就浪費(fèi)了CPU的大量時間,降低了處理器的利用率。設(shè)備驅(qū)動程序接口2.中斷方式當(dāng)CPU進(jìn)行主程序操作時,外設(shè)的數(shù)據(jù)已存入端口的數(shù)據(jù)輸入寄存器,或端口的數(shù)據(jù)輸出寄存器已空,此時由外設(shè)通過接口電路向CPU發(fā)出中斷請求信號。CPU在滿足一定條件下,暫停執(zhí)行當(dāng)前正在執(zhí)行的主程序,轉(zhuǎn)入執(zhí)行相應(yīng)能夠進(jìn)行輸入/輸出操作的子程序,待輸入/輸出操作執(zhí)行完畢之后,CPU再返回并繼續(xù)執(zhí)行原來被中斷的主程序。這樣,CPU就避免了把大量時間耗費(fèi)在等待、查詢外設(shè)狀態(tài)的操作上,使其工作效率得以大大提高。設(shè)備驅(qū)動程序接口能夠向CPU發(fā)出中斷請求的設(shè)備或事件稱為中斷源。中斷源向CPU發(fā)出中斷請求,若優(yōu)先級別最高,則CPU在滿足一定的條件時,可中斷當(dāng)前程序的運(yùn)行,保護(hù)好被中斷的主程序的斷點及現(xiàn)場信息,然后根據(jù)中斷源提供的信息,找到中斷服務(wù)子程序的入口地址,轉(zhuǎn)去執(zhí)行新的程序段,這就是中斷響應(yīng)。CPU響應(yīng)中斷是有條件的,如內(nèi)部允許中斷、中斷未被屏蔽、當(dāng)前指令執(zhí)行完等。CPU響應(yīng)中斷以后,就會中止當(dāng)前的程序,轉(zhuǎn)去執(zhí)行一個中斷服務(wù)子程序,以完成為相應(yīng)設(shè)備的服務(wù)。設(shè)備驅(qū)動程序接口3.直接訪問內(nèi)存DMA方式利用中斷,系統(tǒng)和設(shè)備之間可以通過設(shè)備驅(qū)動程序傳送數(shù)據(jù),但是,當(dāng)傳送的數(shù)據(jù)量很大時,因為中斷處理上的延遲,利用中斷方式的效率會大大降低。而DMA可以解決這一問題。DMA可允許設(shè)備和系統(tǒng)內(nèi)存間在沒有處理器參與的情況下傳輸大量數(shù)據(jù)。設(shè)備驅(qū)動程序在利用DMA之前,需要選擇DMA通道并定義相關(guān)寄存器,以及數(shù)據(jù)的傳輸方向,即讀取或?qū)懭耄缓髮⒃O(shè)備設(shè)定為利用該DMA通道傳輸數(shù)據(jù)。設(shè)備完成設(shè)置之后,可立即利用該DMA通道在設(shè)備和系統(tǒng)的內(nèi)存之間傳輸數(shù)據(jù),傳輸完畢后產(chǎn)生中斷以便通知驅(qū)動程序進(jìn)行后續(xù)處理。在利用DMA進(jìn)行數(shù)據(jù)傳輸?shù)耐瑫r,處理器仍然可以繼續(xù)執(zhí)行指令。linux設(shè)備驅(qū)動開發(fā)流程設(shè)備驅(qū)動開發(fā)流程設(shè)備驅(qū)動程序和應(yīng)用程序區(qū)別如下:應(yīng)用程序一般有一個main函數(shù),從頭到尾執(zhí)行一個任務(wù);驅(qū)動程序卻不同,它沒有main函數(shù),通過使用宏module_init(初始化函數(shù)名);將初始化函數(shù)加入內(nèi)核全局初始化函數(shù)列表中,在內(nèi)核初始化時執(zhí)行驅(qū)動的初始化函數(shù),從而完成驅(qū)動的初始化和注冊,之后驅(qū)動便停止等待被應(yīng)用軟件調(diào)用。驅(qū)動程序中有一個宏moudule_exit(退出處理函數(shù)名)注冊退出處理函數(shù)。它在驅(qū)動退出時被調(diào)用。linux設(shè)備驅(qū)動開發(fā)流程設(shè)備驅(qū)動開發(fā)流程1.編寫驅(qū)動源碼2.編寫Makefile文件3.編譯驅(qū)動模塊4.加載驅(qū)動模塊5.驅(qū)動程序框架linux設(shè)備驅(qū)動開發(fā)流程1.編寫驅(qū)動源碼//hello.c#include<linux/init.h>#include<linux/module.h>#include<linux/kernel.h>staticint__inithello_init(void){

printk(KERN_ALERT"HelloWorld!\n"); return0;}staticvoid__exithello_exit(void){

printk(KERN_ALERT"GoodbyeWorld!\n");}module_init(hello_init);module_exit(hello_exit);MODULE_LICENSE("GPL");linux設(shè)備驅(qū)動開發(fā)流程2.編寫Makefile文件針對以上源碼寫一個Makefile文件用來編譯它,Makefile和hello.c文件保存在同一個目錄下。Makefile文件的內(nèi)容可以簡單編寫如下內(nèi)容:##Makefile###obj-m:=hello.o

linux設(shè)備驅(qū)動開發(fā)流程3.編譯驅(qū)動模塊make-C/usr/src/kernels/2.6.29.4-167.fc11.i686.PAE/M=$(pwd)modulesmake:Enteringdirectory`/usr/src/kernels/2.6.29.4-167.fc11.i686.PAE'CC[M]/root/drvhello/hello.oBuildingmodules,stage2.MODPOST1modulesCC/root/drvhello/hello.mod.oLD[M]/root/drvhello/hello.komake:Leavingdirectory`/usr/src/kernels/2.6.29.4-167.fc11.i686.PAE'linux設(shè)備驅(qū)動開發(fā)流程編譯過程經(jīng)歷了這樣的步驟:先進(jìn)入linux內(nèi)核所在的目錄,并編譯出hello.o文件,然后創(chuàng)建模塊,運(yùn)行MODPOST生成臨時的hello.mod.c文件,而后根據(jù)此文件編譯出hello.mod.o,之后連接hello.o和hello.mod.o文件得到模塊目標(biāo)文件hello.ko,最后離開linux內(nèi)核所在的目錄。使用ls命令查看當(dāng)前目錄可以看到如下信息。linux設(shè)備驅(qū)動開發(fā)流程[root@JLUZH

drvhello]#ls

Makefile

Module.markers

hello.ko

hello.o

Module.symvers

hello.mod.c

modules.order

hello.c

hello.mod.o[root@JLUZH

drvhello]#

##Makefile###

obj-m:=modulename.o

module-objs:=file1.ofile2.olinux設(shè)備驅(qū)動開發(fā)流程4.加載驅(qū)動模塊使用insmod來加載該驅(qū)動模塊,并且使用相關(guān)的命令來驗證或查看該驅(qū)動模塊運(yùn)行的信息。其操作如下:[root@JLUZH

drvhello]#insmod

hello.ko[root@JLUZH

drvhello]#lsmodModuleSizeUsedbyhello11320…[root@JLUZH

drvhello]#cat/var/log/messages…Jul2109:02:53JLUZHkernel:HelloWorld!rmmod

helloworld.kolinux設(shè)備驅(qū)動開發(fā)流程5.驅(qū)動程序框架頭文件:像C程序需要包含C庫的頭文件那樣,Linux內(nèi)核編程也需要包含Kernel頭文件,大多的Linux驅(qū)動程序需要包含下面三個頭文件:#include<linux/init.h>:定義了驅(qū)動的初始化和退出相關(guān)的函數(shù),#include<linux/module.h>:定義了經(jīng)常用到的函數(shù)原型及宏定義#include<linux/kernel.h>:定義了內(nèi)核模塊相關(guān)的函數(shù)、變量及宏。#include<linux/kernel.h>:定義了內(nèi)核模塊相關(guān)的函數(shù)、變量及宏。linux設(shè)備驅(qū)動開發(fā)流程初始化:任何一個驅(qū)動都去需要提供一個初始化函數(shù),當(dāng)驅(qū)動加載到內(nèi)核中時,這個初始化函數(shù)就會被自動執(zhí)行,初始化的函數(shù)原型定義為:typedef

int(*initcall_t)(void);驅(qū)動程序是通過module_init宏來聲明初始化函數(shù)的:staticint__inithello_init(void){

printk(KERN_ALERT"HelloWorld!n"); return0;}module_init(hello_init);linux設(shè)備驅(qū)動開發(fā)流程卸載:如果驅(qū)動程序編譯成模塊(動態(tài)加載)模式,那么它需要一個清理函數(shù)。當(dāng)移除一個內(nèi)核模塊時這個函數(shù)被調(diào)用執(zhí)行清理工作。清理函數(shù)的函數(shù)原型定義為:typedefvoid(*exitcall_t)(void);驅(qū)動程序是通過module_exit宏來聲明清理函數(shù)的:staticvoid__exithello_exit(void){

printk(KERN_ALERT"GoodbyeWorld!n");}module_exit(hello_exit);linux設(shè)備驅(qū)動開發(fā)流程版權(quán)信息:Linux內(nèi)核是按照GPL發(fā)布的,同樣Linux的驅(qū)動程序也要提供版權(quán)信息,否則當(dāng)加載到內(nèi)核中是系統(tǒng)會給出警告信息。HelloWorld例子中的版權(quán)信息是GPL。MODULE_LICENSE("GPL");linux設(shè)備驅(qū)動開發(fā)流程字符設(shè)備驅(qū)動框架用戶進(jìn)程利用系統(tǒng)調(diào)用在對設(shè)備文件進(jìn)行諸如read/write操作時,系統(tǒng)調(diào)用通過設(shè)備文件的主設(shè)備號找到相應(yīng)的設(shè)備驅(qū)動程序,然后讀取這個數(shù)據(jù)結(jié)構(gòu)相應(yīng)的函數(shù)指針,接著把控制權(quán)交給該函數(shù)。這是linux的設(shè)備驅(qū)動程序工作的基本原理linux設(shè)備驅(qū)動開發(fā)流程/*******drivedemo.c*****/#ifndef__KERNEL__#define__KERNEL__#endif#ifndefMODULE#defineMODULE#endif/***********頭文件******************************/#include<linux/mm.h>#include<linux/module.h>#include<asm/segment.h>#include<asm/uaccess.h>/*COPY_TO_USER*/#include<linux/init.h>#include<linux/kernel.h>/*printk()*/#include<linux/slab.h>/*kmalloc()*/#include<linux/fs.h>/*everything...*/#include<linux/errno.h>/*errorcodes*/#include<linux/types.h>/*size_t*/#include<linux/proc_fs.h>#include<linux/fcntl.h>/*O_ACCMODE*/#include<asm/system.h>/*cli(),*_flags*/linux設(shè)備驅(qū)動開發(fā)流程/***********定義常量、變量、函數(shù)******************/#defineDEVICE_NAME"demodrv"#definedemo_MAJOR267#definedemo_MINOR0staticintMAX_BUF_LEN=1024;staticchardrv_buf[1024];staticintWRI_LENGTH=0;staticssize_t

demo_write(structfile*,constchar*,size_t);staticssize_t

demo_read(structfile*,char*,size_t,loff_t*);/***********定義驅(qū)動接口結(jié)構(gòu)體***********************/staticstruct

file_operations

demo_fops={

owner:THIS_MODULE,//open:demo_open,read:demo_read,write:demo_write, //llseek:demo_llseek(), //ioctl:demo_ioctl,//release:demo_release,};linux設(shè)備驅(qū)動開發(fā)流程/*******do_write()函數(shù):將緩沖區(qū)中的數(shù)逆序****/staticvoiddo_write(){inti;int

len=WRI_LENGTH;chartmp;for(i=0;i<(len>>1);i++,len--){tmp=drv_buf[len-1];drv_buf[len-1]=drv_buf[i];drv_buf[i]=tmp;}}linux設(shè)備驅(qū)動開發(fā)流程/***********demo_read()函數(shù)***********************/staticssize_t

demo_read(structfile*filp,char*buffer,size_tcount,loff_t*ppos){if(count>MAX_BUF_LEN)count=MAX_BUF_LEN;//copy_to_user(buffer,drv_buf,count);printk("userreaddatafromdriver\n");returncount;}linux設(shè)備驅(qū)動開發(fā)流程/**********demo_write()函數(shù)***********************/staticssize_t

demo_write(structfile*filp,constchar*buffer,size_tcount){if(count>MAX_BUF_LEN)count=MAX_BUF_LEN;//copy_from_user(drv_buf,buffer,count);WRI_LENGTH=count;printk("userwritedatatodriver\n");do_write(); returncount;}linux設(shè)備驅(qū)動開發(fā)流程/**********demo_init()函數(shù)***********************/staticint__initdemo_init(void){

intresult;result=register_chrdev(demo_MAJOR,"demodrv",&demo_fops);if(result<0){printk(DEVICE_NAME"initializedfailure\n");returnresult;}else{printk(DEVICE_NAME"initialized\n");return0;}}linux設(shè)備驅(qū)動開發(fā)流程/**********demo_exit()函數(shù)***********************/staticvoid__exitdemo_exit(void){//unregister_chrdev(demo_MAJOR,"demodrv");//kfree(demo_devices);

printk(DEVICE_NAME"unloaded\n");}/**********系統(tǒng)調(diào)用模塊接口***********************/module_init(demo_init);module_exit(demo_exit);MODULE_LICENSE("DualBSD/GPL");linux設(shè)備驅(qū)動開發(fā)流程1.編寫子函數(shù)隨著內(nèi)核不斷增加新的功能,file_operations結(jié)構(gòu)體已逐漸變得越來越大,但是大多數(shù)的驅(qū)動程序只是利用了其中的一部分。對于字符設(shè)備來說,要提供的主要入口有:open()、read()、write()、llseek()、ioctl()、release()等。下面我們重點介紹這幾個函數(shù)。linux設(shè)備驅(qū)動開發(fā)流程open()函數(shù)應(yīng)用程序?qū)υO(shè)備特殊文件進(jìn)行open()系統(tǒng)調(diào)用時,將調(diào)用驅(qū)動程序的demo_open函數(shù),其基本結(jié)構(gòu)如下:staticint

demo_open(struct

inode*inode,structfile*file){//MOD_INC_USE_COUNT;return0;}linux設(shè)備驅(qū)動開發(fā)流程open()的主要任務(wù)是確定硬件處在就緒狀態(tài)、驗證次設(shè)備號的合法性(次設(shè)備號可以用MINOR(inode->i-rdev)取得)、控制使用設(shè)備的進(jìn)程數(shù)、根據(jù)執(zhí)行情況返回狀態(tài)碼(0表示成功,負(fù)數(shù)表示存在錯誤)等;在linux2.4內(nèi)核中通常通過宏“MOD_INC_USE_COUNT”來管理自己被使用的計數(shù)。Linux2.6內(nèi)核中提供了模塊計數(shù)管理接口try_module_get(&module)和module_put(&module),從而取代了linux2.4內(nèi)核中的模塊使用計數(shù)管理宏。Linux2.6內(nèi)核下,驅(qū)動工程師很少親自調(diào)用這兩個函數(shù),從而簡化了設(shè)備驅(qū)動的開發(fā)。linux設(shè)備驅(qū)動開發(fā)流程read()函數(shù)應(yīng)用程序?qū)υO(shè)備特殊文件進(jìn)行read()系統(tǒng)調(diào)用時,將調(diào)用驅(qū)動程序的demo_read

函數(shù),其基本結(jié)構(gòu)如下:staticint

demo_read(struct

inode*inode,structfile*file,char*buf,intcount){//copy_to_user(buffer,drv_buf,count);returncount;}linux設(shè)備驅(qū)動開發(fā)流程write()函數(shù)應(yīng)用程序?qū)υO(shè)備特殊文件進(jìn)行write()系統(tǒng)調(diào)用時,將調(diào)用驅(qū)動程序的demo_write

函數(shù),其基本結(jié)構(gòu)如下staticint

demo_write(struct

inode*inode,structfile*file,constchar*buf,intcount){//copy_from_user(&global_var,buf,sizeof(int)))returncount}linux設(shè)備驅(qū)動開發(fā)流程llseek()函數(shù)該函數(shù)用來修改文件的當(dāng)前讀寫位置,并將新位置作為(正的)返回值返回,原型為:loff_t(*llseek)(structfile*,loff_t,int);linux設(shè)備驅(qū)動開發(fā)流程ioctl()函數(shù)該函數(shù)是特殊的控制函數(shù),可以通過它向設(shè)備傳遞控制信息或從設(shè)備取得狀態(tài)信息,函數(shù)原型為:unsignedint參數(shù)為設(shè)備驅(qū)動程序要執(zhí)行的命令的代碼,由用戶自定義,unsignedlong參數(shù)為相應(yīng)的命令提供參數(shù),類型可以是整型、指針等int(*ioctl)(struct

inode*,structfile*,unsignedint,unsignedlong);linux設(shè)備驅(qū)動開發(fā)流程release()函數(shù)release()函數(shù)是在最后一個打開設(shè)備的用戶進(jìn)程執(zhí)行close()系統(tǒng)調(diào)用時,內(nèi)核將調(diào)用的驅(qū)動函數(shù)。其定義如下:release函數(shù)的主要任務(wù)是清理未結(jié)束的輸入/輸出操作、釋放資源、用戶自定義排他標(biāo)志的復(fù)位等。staticvoiddemo_release(struct

inode*inode,structfile*file){ MOD_DEC_USE_COUNT;}linux設(shè)備驅(qū)動開發(fā)流程poll()函數(shù)poll()函數(shù)poll方法是poll和select這兩個系統(tǒng)調(diào)用的后端實現(xiàn),用來查詢設(shè)備是否可讀或可寫,或是否處于某種特殊狀態(tài),原型為:unsignedint(*poll)(structfile*,struct

poll_table_struct*);linux設(shè)備驅(qū)動開發(fā)流程設(shè)備初始化init_module()函數(shù)staticint__initdemo_init(void){

intresult;result=register_chrdev(demo_MAJOR,"demodrv",&demo_fops);if(result<0){printk(DEVICE_NAME"initializedfailure\n");returnresult;}else{

printk(DEVICE_NAME"initialized\n"); return0;}}linux設(shè)備驅(qū)動開發(fā)流程設(shè)備注銷module_exit()函數(shù)在用rmmod卸載模塊時,module_exit函數(shù)被調(diào)用,它釋放字符設(shè)備在系統(tǒng)字符設(shè)備表中占有的表項。定義如下:staticvoid__exit

溫馨提示

  • 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)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論