久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费
標(biāo)題:
Linux系統(tǒng)的硬件驅(qū)動(dòng)程序編寫原理
[打印本頁(yè)]
作者:
51黑tt
時(shí)間:
2016-3-5 17:38
標(biāo)題:
Linux系統(tǒng)的硬件驅(qū)動(dòng)程序編寫原理
顧名思義,驅(qū)動(dòng)程序是用來(lái)控制計(jì)算機(jī)外圍設(shè)備的,Linux系統(tǒng)將所有的外圍設(shè)備都高度地抽象成一些字節(jié)的序列,并且以文件的形式來(lái)表示這些設(shè)備。我們可以來(lái)看一下Linux的I/O子系統(tǒng)(圖1)。
從圖上我們可以看出,內(nèi)核緊緊地包圍在硬件周圍,內(nèi)核是一些軟件包的組合,它們可以直接訪問(wèn)系統(tǒng)的硬件,包括處理器、內(nèi)存和I/O設(shè)備。而用戶進(jìn)程則通過(guò)內(nèi)核提供的用戶服務(wù)來(lái)和內(nèi)核通訊,從而間接地控制系統(tǒng)硬件。
我們可以通過(guò)圖2來(lái)了解這些動(dòng)作的具體情況。
圖上顯示了用戶級(jí)的程序使用內(nèi)核提供的標(biāo)準(zhǔn)系統(tǒng)調(diào)用來(lái)與內(nèi)核通訊,這些系統(tǒng)調(diào)用有:open(), read(), write(), ioctl(), close() 等等。
Linux的內(nèi)核是一個(gè)有機(jī)的整體。每一個(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)行。
Linux內(nèi)核使用"設(shè)備無(wú)關(guān)"的I/O子系統(tǒng)來(lái)為所有的設(shè)備服務(wù)。每個(gè)設(shè)備都提供標(biāo)準(zhǔn)接口給內(nèi)核,從而盡可能地隱藏了自己的特性。圖3展示了用戶程序使用一些基本的系統(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)例程來(lái)操縱硬件。
每個(gè)設(shè)備在Linux系統(tǒng)上看起來(lái)都像一個(gè)文件,它們存放在/dev目錄中并被稱為"特殊文件"或是"設(shè)備節(jié)點(diǎn)"。大家可以使用ls -l /dev/lp* 來(lái)得到以下的輸出:
crw-rw-rw 1 root root 6, 0 April 23 1994 /dev/lp0
這行輸出表示lp0是一個(gè)字符設(shè)備(屬性字段的第一個(gè)字符是'c'),主設(shè)備號(hào)是6,次設(shè)備號(hào)是0。主設(shè)備號(hào)用來(lái)向內(nèi)核表明這一設(shè)備節(jié)點(diǎn)所代表的驅(qū)動(dòng)程序的類型(比如:主設(shè)備號(hào)是3的塊設(shè)備是IDE磁盤驅(qū)動(dòng)程序,而主設(shè)備號(hào)為8的塊設(shè)備是SCSI磁盤驅(qū)動(dòng)程序);每個(gè)驅(qū)動(dòng)程序負(fù)責(zé)管理它所驅(qū)動(dòng)的幾個(gè)硬件實(shí)例,這些硬件實(shí)例則由次設(shè)備號(hào)來(lái)表示(例如:次設(shè)備號(hào)為0的SCSI磁盤代表整個(gè)也可以說(shuō)是"第一個(gè)"SCSI磁盤,而次設(shè)備號(hào)為1到15的磁盤代表此SCSI磁盤上的15個(gè)分區(qū))。
設(shè)備驅(qū)動(dòng)程序是一組由內(nèi)核中的相關(guān)子例程和數(shù)據(jù)組成的I/O設(shè)備軟件接口。每當(dāng)內(nèi)核意識(shí)到要對(duì)某個(gè)設(shè)備進(jìn)行特殊的操作時(shí),它就調(diào)用相應(yīng)的驅(qū)動(dòng)例程。這就使得控制從用戶進(jìn)程轉(zhuǎn)移到了驅(qū)動(dòng)例程,當(dāng)驅(qū)動(dòng)例程完成后,控制又被返回至用戶進(jìn)程。圖5就顯示了以上的過(guò)程。
每個(gè)設(shè)備驅(qū)動(dòng)程序都具有以下幾個(gè)特性:
l 具有一整套的和硬件設(shè)備通訊的例程,并且提供給操作系統(tǒng)一套標(biāo)準(zhǔn)的軟件接口;
l 具有一個(gè)可以被操作系統(tǒng)動(dòng)態(tài)地調(diào)用和移除的自包含組件;
l 可以控制和管理用戶程序和物理設(shè)備之間的數(shù)據(jù)流。
接下來(lái)我們來(lái)了解一下字符設(shè)備和塊設(shè)備,它們是Linux系統(tǒng)中兩種主要的外圍設(shè)備。我們常見(jiàn)的磁盤是塊設(shè)備,而終端和打印機(jī)是字符設(shè)備。塊設(shè)備被用戶程序通過(guò)系統(tǒng)緩沖來(lái)訪問(wèn)。特別是系統(tǒng)內(nèi)存分配和管理進(jìn)程就沒(méi)有必要來(lái)充當(dāng)從外設(shè)讀寫的數(shù)據(jù)傳輸者了。正好與之相反的是,字符設(shè)備直接與用戶程序進(jìn)行通訊,而且兩者似乎沒(méi)有緩沖區(qū)。Linux的傳輸控制機(jī)制會(huì)根據(jù)用戶程序的需要來(lái)正確地操縱內(nèi)存和磁盤等外設(shè)來(lái)取得數(shù)據(jù)。在Linux系統(tǒng)中字符設(shè)備驅(qū)動(dòng)器被保存為/usr/src/linux/drivers/char目錄中。下面我們重點(diǎn)介紹字符設(shè)備驅(qū)動(dòng)程序的開(kāi)發(fā)方法。
首先了解一下Linux的內(nèi)核編程環(huán)境。我們知道每個(gè)Linux用戶進(jìn)程都在一個(gè)獨(dú)立的系統(tǒng)空間中運(yùn)行著,與系統(tǒng)區(qū)和其他用戶進(jìn)程相隔離。這樣就保護(hù)了一個(gè)用戶進(jìn)程的運(yùn)行環(huán)境,以免被其他用戶進(jìn)程所破壞。與這種情況正相反的是,設(shè)備驅(qū)動(dòng)程序運(yùn)行在內(nèi)核模式,它們具有很大的自由度。這些設(shè)備驅(qū)動(dòng)程序都是被假設(shè)為正確和可靠的,它們是內(nèi)核的一部分,可以處理系統(tǒng)中斷請(qǐng)求和訪問(wèn)外圍設(shè)備,同時(shí)它們有效地處理中斷請(qǐng)求以便系統(tǒng)調(diào)度程序保持系統(tǒng)需求的平衡。所以設(shè)備驅(qū)動(dòng)程序可以脫離系統(tǒng)的限制來(lái)使用系統(tǒng)區(qū),比如系統(tǒng)的緩沖區(qū)等等。
一個(gè)設(shè)備驅(qū)動(dòng)程序同時(shí)包括中斷和同步區(qū)域。其中中斷區(qū)域處理實(shí)時(shí)事件并且被設(shè)備的中斷所驅(qū)動(dòng);而同步區(qū)域則組成了設(shè)備的剩余部分,處理進(jìn)程的同步事件。所以,當(dāng)一個(gè)設(shè)備需要一些軟件服務(wù)時(shí),就發(fā)出一個(gè)"中斷",然后中斷處理器得到產(chǎn)生中斷的原因同時(shí)進(jìn)行相應(yīng)的動(dòng)作。
一個(gè)Linux進(jìn)程可能會(huì)在事件發(fā)生之前一直等待下去。例如,一個(gè)進(jìn)程可能會(huì)在運(yùn)行中等待一些寫入硬件設(shè)備的信息的到來(lái)。其中一種方式是進(jìn)程可以使用sleep()和wakeup()這兩個(gè)系統(tǒng)調(diào)用,進(jìn)程先使自己處于睡眠狀態(tài),等待事件的到來(lái),一旦事件發(fā)生,進(jìn)程即可被喚醒。舉個(gè)例子來(lái)說(shuō):interruptible_sleep_on(&dev_wait_queue)函數(shù)使進(jìn)程睡眠并且將此進(jìn)程的進(jìn)程號(hào)加到進(jìn)程睡眠列表dev_wait_queue中,一旦設(shè)備準(zhǔn)備好后,設(shè)備發(fā)出一個(gè)中斷,從而導(dǎo)致設(shè)備驅(qū)動(dòng)程序中相應(yīng)的例程被調(diào)用,這個(gè)驅(qū)動(dòng)程序例程處理完一些設(shè)備要求的事宜后會(huì)發(fā)出一個(gè)喚醒進(jìn)程的信號(hào),通常使用wake_up_interruptible(&dev_wait_queue)函數(shù),它可以喚醒dev_wait_queue所示列表中的所有進(jìn)程。
特別要注意的是,如果兩個(gè)和兩個(gè)以上的進(jìn)程共享一些公共數(shù)據(jù)區(qū)時(shí),我們必須將之視為臨界區(qū),臨界區(qū)保證了進(jìn)程間互斥地訪問(wèn)公共數(shù)據(jù)。在Linux系統(tǒng)中我們可以使用cli()和sti()兩個(gè)內(nèi)核例程來(lái)處理這種互斥,當(dāng)一個(gè)進(jìn)程在訪問(wèn)臨界區(qū)時(shí)可以使用cli()來(lái)關(guān)閉中斷,離開(kāi)時(shí)則使用sti()再將中斷打開(kāi),就像下面的寫法:
cli()
臨界區(qū)
sti()
除了以上這些,我們還得了解一下虛擬文件系統(tǒng)交換(VFS)的概念。
圖6中的"文件操作結(jié)構(gòu)"在/usr/include/linux/fs.h文件中定義,此結(jié)構(gòu)包含了驅(qū)動(dòng)程序中的函數(shù)列表。圖上的初始化例程xxx_init()根據(jù)VFS和設(shè)備的主設(shè)備號(hào)來(lái)注冊(cè)"文件操作結(jié)構(gòu)"。
下面是一些設(shè)備驅(qū)動(dòng)程序的支撐函數(shù)(具體使用方法詳見(jiàn)Linux編程手冊(cè),使用man命令):
add_timer()
定時(shí)間一過(guò),可以引發(fā)函數(shù)的執(zhí)行;
cli()
關(guān)閉中斷,阻止中斷的捕獲;
end_request()
當(dāng)一個(gè)請(qǐng)求被完成或被撤銷時(shí)被執(zhí)行;
free_irq()
釋放一個(gè)先前被request_irq()和irqaction()捕獲的的中斷請(qǐng)求;
get_fs*()
允許一個(gè)設(shè)備驅(qū)動(dòng)程序訪問(wèn)用戶區(qū)數(shù)據(jù)(一塊不屬于內(nèi)核的內(nèi)存區(qū));
inb(), inb_p()
從一個(gè)端口讀取一個(gè)字節(jié),其中inb_p() 會(huì)一直阻塞直到從端口得到字節(jié)為止;
irqaction()
注冊(cè)一個(gè)中斷;
IS_*(inode)
測(cè)試inode是否在一個(gè)被mount了的文件系統(tǒng)上;
kfree*()
放先前被kmalloc()分配的內(nèi)存區(qū);
kmalloc()
分配大于4096個(gè)字節(jié)的大塊內(nèi)存區(qū);
MAJOR()
返回設(shè)備的主設(shè)備號(hào);
MINOR()
返回設(shè)備的次設(shè)備號(hào);
memcpy_*fs()
在用戶區(qū)和內(nèi)核區(qū)之間復(fù)制大塊的內(nèi)存;
outb(), outb_p()
向一個(gè)端口寫一個(gè)字節(jié),其中outb_p()一直阻塞直到寫字節(jié)成功為止;
printk()
內(nèi)核使用的printf()版本;
put_fs*()
允許設(shè)備驅(qū)動(dòng)程序?qū)?shù)據(jù)寫入用戶區(qū);
register_*dev()
在內(nèi)核中注冊(cè)一個(gè)設(shè)備;
request_irq()
向內(nèi)核申請(qǐng)一個(gè)中斷請(qǐng)求IRQ,如果成功則安裝一個(gè)中斷請(qǐng)求處理器;
select_wait()
將一個(gè)進(jìn)程加到相應(yīng)select_wait隊(duì)列中;
*sleep_on()
使進(jìn)程睡眠以等待事件的到來(lái),并且將wait_queue 入口點(diǎn)加到列表中以便事件到來(lái)時(shí)將進(jìn)程喚醒;
sti()
和cti()相對(duì)應(yīng),恢復(fù)中斷捕獲;
sys_get*()
系統(tǒng)調(diào)用,得到進(jìn)程的有關(guān)信息;
wake_up*()
喚醒先前被*sleep_on() 睡眠的進(jìn)程
Linux的用戶進(jìn)程不能直接訪問(wèn)系統(tǒng)物理內(nèi)存。每個(gè)用戶進(jìn)程都有自己的內(nèi)存空間(用戶虛擬地址空間,開(kāi)始于虛擬0地址)。同樣內(nèi)核也具有自己特定的內(nèi)存空間--系統(tǒng)虛擬地址空間。每當(dāng)用戶使用系統(tǒng)調(diào)用read()或write()時(shí),設(shè)備驅(qū)動(dòng)程序就在內(nèi)核地址空間和用戶程序地址空間之間拷貝數(shù)據(jù)。許多Linux例程,比如memcpy_*fs() 和 put_fs*()可以使設(shè)備驅(qū)動(dòng)程序穿越"用戶-系統(tǒng)"邊界來(lái)傳輸數(shù)據(jù)。而且數(shù)據(jù)可以是字節(jié)、字或任意長(zhǎng)度的數(shù)據(jù)塊。例如,memcpy_fromfs()可以從用戶內(nèi)存空間傳輸任意長(zhǎng)度的數(shù)據(jù)塊到設(shè)備,而get_fs_byte()則只從用戶內(nèi)存空間傳輸一個(gè)字節(jié);相同的memcpy_tofs()和 put_fs_byte()也是如此,只不過(guò)它們是寫數(shù)據(jù)到用戶內(nèi)存空間。
然而,在內(nèi)核可訪問(wèn)內(nèi)存空間和設(shè)備本身之間傳輸數(shù)據(jù)則要視不同的計(jì)算機(jī)而定。一些計(jì)算機(jī)需要使用一些特殊CPU輸入輸出指令來(lái)完成這項(xiàng)工作,這通常被稱為DMA(直接內(nèi)存訪問(wèn))。而另一種方案則是使用內(nèi)存映射I/O來(lái)解決,通常使用系統(tǒng)提供的I/O函數(shù),比如inb()和outb()來(lái)分別地從I/O地址(即端口)讀取和向I/O地址輸出一單字節(jié),可以使用以下的語(yǔ)句:
unsigned char inb(int port)
outb(char data, int port)
好,下面就可以來(lái)看看字符設(shè)備驅(qū)動(dòng)程序的基本結(jié)構(gòu)。如圖6所示xxx_write()例程輪詢?cè)O(shè)備是否已經(jīng)準(zhǔn)備好接收數(shù)據(jù),如果準(zhǔn)備好了,則將指定長(zhǎng)度的字符串從用戶內(nèi)存空間發(fā)送到字符設(shè)備。另外還可以使用中斷來(lái)通知設(shè)備是否準(zhǔn)備好,這樣就不需要程序?yàn)榱溯喸兌却瑥亩岣逤PU的利用率。xxx_table[]是一個(gè)結(jié)構(gòu)的數(shù)組,它包含很多成員變量,包括xxx_wait_queue和bytes_xfered(兩者都被用于讀寫操作)。xxx_open()使用request_irq()或irqaction()來(lái)調(diào)用xxx_interrupt()例程。
為了使設(shè)備驅(qū)動(dòng)程序被正確地初始化,每當(dāng)系統(tǒng)啟動(dòng)時(shí),xxx_init() 例程必須被調(diào)用。為了確保這一操作,需要將語(yǔ)句mem_start = xxx_init(mem_start); 加到/usr/src/linux/driver/char/mem.c文件的chr_drv_init()函數(shù)末。接下來(lái)的工作就是將驅(qū)動(dòng)設(shè)備安裝到內(nèi)核中去了(注意:字符設(shè)備驅(qū)動(dòng)程序只能被安裝在/usr/src/linux/drivers/char/char.a庫(kù)文件中)。
歡迎光臨 (http://www.zg4o1577.cn/bbs/)
Powered by Discuz! X3.1
主站蜘蛛池模板:
日韩美香港a一级毛片免费 国产综合av
|
久久久久久久夜
|
成年人在线视频
|
国产精品久久久久久中文字
|
国产一区黄色
|
国产成人精品综合
|
欧美成人在线网站
|
99国内精品久久久久久久
|
国产精品成人一区二区三区
|
国产视频一区二区
|
日本精品视频在线
|
午夜精品久久久久久久
|
麻豆久久精品
|
欧美日韩精品一区二区三区四区
|
日日爱av
|
亚洲一区国产
|
国产欧美一区二区三区在线看
|
国产精品99999999
|
午夜不卡福利视频
|
国产成人啪免费观看软件
|
国产在线观看一区二区三区
|
久久草在线视频
|
国产极品车模吞精高潮呻吟
|
国产美女福利在线观看
|
自拍偷拍中文字幕
|
国产综合久久
|
午夜精品久久久久久久
|
亚洲精品乱码久久久久久按摩观
|
日韩欧美三级电影在线观看
|
永久免费视频
|
国产福利91精品一区二区三区
|
免费成人高清在线视频
|
国产999精品久久久久久绿帽
|
一区二区三区高清不卡
|
久久国产精品一区
|
国产亚洲一区精品
|
成人欧美一区二区三区黑人孕妇
|
欧美5区
|
福利片在线观看
|
亚洲精品一区二区三区免
|
国产最新视频在线
|