久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 1843|回復: 0
打印 上一主題 下一主題
收起左側

Linux系統的硬件驅動程序編寫原理

[復制鏈接]
跳轉到指定樓層
樓主
ID:107189 發表于 2016-3-5 17:38 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
顧名思義,驅動程序是用來控制計算機外圍設備的,Linux系統將所有的外圍設備都高度地抽象成一些字節的序列,并且以文件的形式來表示這些設備。我們可以來看一下Linux的I/O子系統(圖1)。

從圖上我們可以看出,內核緊緊地包圍在硬件周圍,內核是一些軟件包的組合,它們可以直接訪問系統的硬件,包括處理器、內存和I/O設備。而用戶進程則通過內核提供的用戶服務來和內核通訊,從而間接地控制系統硬件。
我們可以通過圖2來了解這些動作的具體情況。

圖上顯示了用戶級的程序使用內核提供的標準系統調用來與內核通訊,這些系統調用有:open(), read(), write(), ioctl(), close() 等等。
 Linux的內核是一個有機的整體。每一個用戶進程運行時都好像有一份內核的拷貝,每當用戶進程使用系統調用時,都自動地將運行模式從用戶級轉為內核級,此時進程在內核的地址空間中運行。

 Linux內核使用"設備無關"的I/O子系統來為所有的設備服務。每個設備都提供標準接口給內核,從而盡可能地隱藏了自己的特性。圖3展示了用戶程序使用一些基本的系統調用從設備讀取數據并且將它們存入緩沖的例子。我們可以看到,每當一個系統調用被使用時,內核就轉到相應的設備驅動例程來操縱硬件。

  每個設備在Linux系統上看起來都像一個文件,它們存放在/dev目錄中并被稱為"特殊文件"或是"設備節點"。大家可以使用ls -l /dev/lp* 來得到以下的輸出:

  crw-rw-rw 1 root root 6, 0 April 23 1994 /dev/lp0

  這行輸出表示lp0是一個字符設備(屬性字段的第一個字符是'c'),主設備號是6,次設備號是0。主設備號用來向內核表明這一設備節點所代表的驅動程序的類型(比如:主設備號是3的塊設備是IDE磁盤驅動程序,而主設備號為8的塊設備是SCSI磁盤驅動程序);每個驅動程序負責管理它所驅動的幾個硬件實例,這些硬件實例則由次設備號來表示(例如:次設備號為0的SCSI磁盤代表整個也可以說是"第一個"SCSI磁盤,而次設備號為1到15的磁盤代表此SCSI磁盤上的15個分區)。
設備驅動程序是一組由內核中的相關子例程和數據組成的I/O設備軟件接口。每當內核意識到要對某個設備進行特殊的操作時,它就調用相應的驅動例程。這就使得控制從用戶進程轉移到了驅動例程,當驅動例程完成后,控制又被返回至用戶進程。圖5就顯示了以上的過程。

每個設備驅動程序都具有以下幾個特性:

  l 具有一整套的和硬件設備通訊的例程,并且提供給操作系統一套標準的軟件接口;

  l 具有一個可以被操作系統動態地調用和移除的自包含組件;

  l 可以控制和管理用戶程序和物理設備之間的數據流。

  接下來我們來了解一下字符設備和塊設備,它們是Linux系統中兩種主要的外圍設備。我們常見的磁盤是塊設備,而終端和打印機是字符設備。塊設備被用戶程序通過系統緩沖來訪問。特別是系統內存分配和管理進程就沒有必要來充當從外設讀寫的數據傳輸者了。正好與之相反的是,字符設備直接與用戶程序進行通訊,而且兩者似乎沒有緩沖區。Linux的傳輸控制機制會根據用戶程序的需要來正確地操縱內存和磁盤等外設來取得數據。在Linux系統中字符設備驅動器被保存為/usr/src/linux/drivers/char目錄中。下面我們重點介紹字符設備驅動程序的開發方法。
首先了解一下Linux的內核編程環境。我們知道每個Linux用戶進程都在一個獨立的系統空間中運行著,與系統區和其他用戶進程相隔離。這樣就保護了一個用戶進程的運行環境,以免被其他用戶進程所破壞。與這種情況正相反的是,設備驅動程序運行在內核模式,它們具有很大的自由度。這些設備驅動程序都是被假設為正確和可靠的,它們是內核的一部分,可以處理系統中斷請求和訪問外圍設備,同時它們有效地處理中斷請求以便系統調度程序保持系統需求的平衡。所以設備驅動程序可以脫離系統的限制來使用系統區,比如系統的緩沖區等等。

  一個設備驅動程序同時包括中斷和同步區域。其中中斷區域處理實時事件并且被設備的中斷所驅動;而同步區域則組成了設備的剩余部分,處理進程的同步事件。所以,當一個設備需要一些軟件服務時,就發出一個"中斷",然后中斷處理器得到產生中斷的原因同時進行相應的動作。

  一個Linux進程可能會在事件發生之前一直等待下去。例如,一個進程可能會在運行中等待一些寫入硬件設備的信息的到來。其中一種方式是進程可以使用sleep()和wakeup()這兩個系統調用,進程先使自己處于睡眠狀態,等待事件的到來,一旦事件發生,進程即可被喚醒。舉個例子來說:interruptible_sleep_on(&dev_wait_queue)函數使進程睡眠并且將此進程的進程號加到進程睡眠列表dev_wait_queue中,一旦設備準備好后,設備發出一個中斷,從而導致設備驅動程序中相應的例程被調用,這個驅動程序例程處理完一些設備要求的事宜后會發出一個喚醒進程的信號,通常使用wake_up_interruptible(&dev_wait_queue)函數,它可以喚醒dev_wait_queue所示列表中的所有進程。

  特別要注意的是,如果兩個和兩個以上的進程共享一些公共數據區時,我們必須將之視為臨界區,臨界區保證了進程間互斥地訪問公共數據。在Linux系統中我們可以使用cli()和sti()兩個內核例程來處理這種互斥,當一個進程在訪問臨界區時可以使用cli()來關閉中斷,離開時則使用sti()再將中斷打開,就像下面的寫法:

  cli()

   臨界區

  sti()

除了以上這些,我們還得了解一下虛擬文件系統交換(VFS)的概念。

圖6中的"文件操作結構"在/usr/include/linux/fs.h文件中定義,此結構包含了驅動程序中的函數列表。圖上的初始化例程xxx_init()根據VFS和設備的主設備號來注冊"文件操作結構"。
下面是一些設備驅動程序的支撐函數(具體使用方法詳見Linux編程手冊,使用man命令):

  add_timer()

  定時間一過,可以引發函數的執行;

  cli()

  關閉中斷,阻止中斷的捕獲;

  end_request()

  當一個請求被完成或被撤銷時被執行;

  free_irq()

  釋放一個先前被request_irq()和irqaction()捕獲的的中斷請求;

  get_fs*()

  允許一個設備驅動程序訪問用戶區數據(一塊不屬于內核的內存區);

  inb(), inb_p()

  從一個端口讀取一個字節,其中inb_p() 會一直阻塞直到從端口得到字節為止;

  irqaction()

  注冊一個中斷;

  IS_*(inode)

  測試inode是否在一個被mount了的文件系統上;

  kfree*()

  放先前被kmalloc()分配的內存區;

  kmalloc()

  分配大于4096個字節的大塊內存區;

  MAJOR()

  返回設備的主設備號;

  MINOR()

  返回設備的次設備號;

  memcpy_*fs()

  在用戶區和內核區之間復制大塊的內存;

  outb(), outb_p()

  向一個端口寫一個字節,其中outb_p()一直阻塞直到寫字節成功為止;

  printk()

  內核使用的printf()版本;

  put_fs*()

  允許設備驅動程序將數據寫入用戶區;

  register_*dev()

  在內核中注冊一個設備;

  request_irq()

  向內核申請一個中斷請求IRQ,如果成功則安裝一個中斷請求處理器;

  select_wait()

  將一個進程加到相應select_wait隊列中;

  *sleep_on()

  使進程睡眠以等待事件的到來,并且將wait_queue 入口點加到列表中以便事件到來時將進程喚醒;

  sti()

  和cti()相對應,恢復中斷捕獲;

  sys_get*()

  系統調用,得到進程的有關信息;

  wake_up*()

  喚醒先前被*sleep_on() 睡眠的進程
 Linux的用戶進程不能直接訪問系統物理內存。每個用戶進程都有自己的內存空間(用戶虛擬地址空間,開始于虛擬0地址)。同樣內核也具有自己特定的內存空間--系統虛擬地址空間。每當用戶使用系統調用read()或write()時,設備驅動程序就在內核地址空間和用戶程序地址空間之間拷貝數據。許多Linux例程,比如memcpy_*fs() 和 put_fs*()可以使設備驅動程序穿越"用戶-系統"邊界來傳輸數據。而且數據可以是字節、字或任意長度的數據塊。例如,memcpy_fromfs()可以從用戶內存空間傳輸任意長度的數據塊到設備,而get_fs_byte()則只從用戶內存空間傳輸一個字節;相同的memcpy_tofs()和 put_fs_byte()也是如此,只不過它們是寫數據到用戶內存空間。

  然而,在內核可訪問內存空間和設備本身之間傳輸數據則要視不同的計算機而定。一些計算機需要使用一些特殊CPU輸入輸出指令來完成這項工作,這通常被稱為DMA(直接內存訪問)。而另一種方案則是使用內存映射I/O來解決,通常使用系統提供的I/O函數,比如inb()和outb()來分別地從I/O地址(即端口)讀取和向I/O地址輸出一單字節,可以使用以下的語句:

  unsigned char inb(int port)

  outb(char data, int port)

好,下面就可以來看看字符設備驅動程序的基本結構。如圖6所示xxx_write()例程輪詢設備是否已經準備好接收數據,如果準備好了,則將指定長度的字符串從用戶內存空間發送到字符設備。另外還可以使用中斷來通知設備是否準備好,這樣就不需要程序為了輪詢而等待,從而提高CPU的利用率。xxx_table[]是一個結構的數組,它包含很多成員變量,包括xxx_wait_queue和bytes_xfered(兩者都被用于讀寫操作)。xxx_open()使用request_irq()或irqaction()來調用xxx_interrupt()例程。

  為了使設備驅動程序被正確地初始化,每當系統啟動時,xxx_init() 例程必須被調用。為了確保這一操作,需要將語句mem_start = xxx_init(mem_start); 加到/usr/src/linux/driver/char/mem.c文件的chr_drv_init()函數末。接下來的工作就是將驅動設備安裝到內核中去了(注意:字符設備驅動程序只能被安裝在/usr/src/linux/drivers/char/char.a庫文件中)。

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 国产 日韩 欧美 在线 | 国产精品不卡 | 色综合一区二区三区 | 黄色片av | 在线一区视频 | 日韩欧美一区二区三区免费看 | 国产三区视频在线观看 | 国产高清一区二区三区 | a网站在线观看 | 精品国产一区二区三区免费 | 免费激情网站 | 99精品视频免费观看 | 99re99| 一区二区三区国产在线观看 | 精品视频一区二区三区 | 日韩精品在线免费 | 久久久.com| 色综合一区二区三区 | 欧美激情一区二区三区 | 毛片a级毛片免费播放100 | 免费观看一级特黄欧美大片 | 欧美日韩高清 | 亚洲日本欧美 | 一区二区视频在线 | 日韩精品一区中文字幕 | 国产精品久久久爽爽爽麻豆色哟哟 | 在线观看你懂的网站 | 亚洲第一色站 | 日韩一二区| 久久久91精品国产一区二区三区 | 欧美成人猛片aaaaaaa | 高清视频一区二区三区 | 99热在线观看精品 | 日韩免费视频一区二区 | 一级做a爰片性色毛片 | 黄色91在线 | 成人午夜在线观看 | 男女视频在线观看免费 | 国产成人自拍av | 国产精品乱码一区二区三区 | 欧美日韩国产在线 |