設備驅動程序包括哪些功能函數
A. Linux設備驅動程序與外界的介面函數有哪些
驅動程序工作在內核空間,由內核來調用 比如某硬體的驅動程序中實現了hd_write()函數,則用戶在用戶空間打開這硬體的設備文件並調用系統調用函數write()時,內核就調用hd_write()函數。
B. 設備驅動程序的功能是什麼
「設備驅動程序」,是一種可以使計算機和設備通信的特殊程序,可以說相當於硬體的介面,操作系統只能通過這個介面,才能控制硬體設備的工作,假如某設備的驅動程序未能正確安裝,便不能正常工作。
正因為這個原因,驅動程序在系統中的所佔的地位十分重要,一般當操作系統安裝完畢後,首要的便是安裝硬體設備的驅動程序。不過,大多數情況下,我們並不需要安裝所有硬體設備的驅動程序,例如硬碟、顯示器、光碟機、鍵盤、滑鼠等就不需要安裝驅動程序,而顯卡、音效卡、掃描儀、攝像頭、Modem等就需要安裝驅動程序。另外,不同版本的操作系統對硬體設備的支持也是不同的,一般情況下版本越高所支持的硬體設備也越多,例如筆者使用了Windows XP,裝好系統後一個驅動程序也不用安裝。
設備驅動程序用來將硬體本身的功能告訴操作系統,完成硬體設備電子信號與操作系統及軟體的高級編程語言之間的互相翻譯。當操作系統需要使用某個硬體時,比如:讓音效卡播放音樂,它會先發送相應指令到音效卡驅動程序,音效卡驅動程序接收到後,馬上將其翻譯成音效卡才能聽懂的電子信號命令,從而讓音效卡播放音樂。
所以簡單的說驅動程序提供了硬體到操作系統的一個介面以及協調二者之間的關系,而因為驅動程序有如此重要的作用,所以人們都稱「驅動程序是硬體的靈魂」、「硬體的主宰」,同時驅動程序也被形象的稱為「硬體和系統之間的橋梁」。
C. 設備驅動程序需通過什麼函數將內核空間的數據傳遞至應用程序空間
linux驅動程序一般工作在內核空間,但也可以工作在用戶空間。下面我們將詳細解析,什麼是內核空間,什麼是用戶空間,以及如何判斷他們。
Linux簡化了分段機制,使得虛擬地址與線性地址總是一致,因此,Linux的虛擬地址空間也為0~4G.Linux內核將這4G位元組的空間分為兩部分。將最高的1G位元組(從虛擬地址0xC0000000到0xFFFFFFFF),供內核使用,稱為"內核空間".而將較低的3G位元組(從虛擬地址 0x00000000到0xBFFFFFFF),供各個進程使用,稱為"用戶空間)。因為每個進程可以通過系統調用進入內核,因此,Linux內核由系統內的所有進程共享。於是,從具體進程的角度來看,每個進程可以擁有4G位元組的虛擬空間。
Linux使用兩級保護機制:0級供內核使用,3級供用戶程序使用。從圖中可以看出(這里無法表示圖),每個進程有各自的私有用戶空間(0~3G),這個空間對系統中的其他進程是不可見的。最高的1GB位元組虛擬內核空間則為所有進程以及內核所共享。
內核空間中存放的是內核代碼和數據,而進程的用戶空間中存放的是用戶程序的代碼和數據。不管是內核空間還是用戶空間,它們都處於虛擬空間中。
雖然內核空間占據了每個虛擬空間中的最高1GB位元組,但映射到物理內存卻總是從最低地址(0x00000000)開始。對內核空間來說,其地址映射是很簡單的線性映射,0xC0000000就是物理地址與線性地址之間的位移量,在Linux代碼中就叫做PAGE_OFFSET.
內核空間和用戶空間之間如何進行通訊?
內核空間和用戶空間一般通過系統調用進行通信。
如何判斷一個驅動是用戶模式驅動還是內核模式驅動? 判斷的標準是什麼?
用戶空間模式的驅動一般通過系統調用來完成對硬體的訪問,如通過系統調用將驅動的io空間映射到用戶空間等。因此,主要的判斷依據就是系統調用。
內核空間和用戶空間上不同太多了,說不完,比如用戶態的鏈表和內核鏈表不一樣;用戶態用printf,內核態用printk;用戶態每個應用程序空間是虛擬的,相對獨立的,內核態中卻不是獨立的,所以編程要非常小心。等等。
還有用戶態和內核態程序通訊的方法很多,不單單是系統調用,實際上系統調用是個不好的選擇,因為需要系統調用號,這個需要統一分配。
可以通過ioctl、sysfs、proc等來完成。
在進行設備驅動程序,內核功能模塊等系統級開發時,通常需要在內核和用戶程序之間交換信息。Linux提供了多種方法可以用來完成這些任務。本文總結了各種常用的信息交換方法,並用簡單的例子演示這些方法各自的特點及用法。其中有大家非常熟悉的方法,也有特殊條件下方可使用的手段。通過對比明確這些方法,可以加深我們對Linux內核的認識,更重要的是,可以讓我們更熟練駕御linux內核級的應用開發技術。
內核空間(kernel-space) VS 用戶空間(user-space)
作為一個Linux開發者,首先應該清楚內核空間和用戶空間的區別。關於這個話題,已經有很多相關資料,我們在這里簡單描述如下:
現代的計算機體系結構中存儲管理通常都包含保護機制。提供保護的目的,是要避免系統中的一個任務訪問屬於另外的或屬於操作系統的存儲區域。如在IntelX86體系中,就提供了特權級這種保護機制,通過特權級別的區別來限制對存儲區域的訪問。 基於這種構架,Linux操作系統對自身進行了劃分:一部分核心軟體獨立於普通應用程序,運行在較高的特權級別上,(Linux使用Intel體系的特權級3來運行內核。)它們駐留在被保護的內存空間上,擁有訪問硬體設備的所有許可權,Linux將此稱為內核空間。
相對的,其它部分被作為應用程序在用戶空間執行。它們只能看到允許它們使用的部分系統資源,並且不能使用某些特定的系統功能,不能直接訪問硬體,不能直接訪問內核空間,當然還有其他一些具體的使用限制。(Linux使用Intel體系的特權級0來運行用戶程序。)
D. Linux有哪三類設備驅動程序並說說這些設備驅動程序的功能
同上,只有兩種設備。但其中網卡又是一種特殊的設備,所以劃出網路設備雖太它馬勉強,也可以說的過去!
E. 在編寫設備驅動程序時,如果用到中斷,會用到兩個函數,分別是什麼並解釋
標准c語言沒有中斷調用機制,但是不同編譯器都有相應的中斷處理方式,可以使用戶實現中斷功能。
解決方案:
1、採取輪詢的方式解決,就是每10毫秒檢查一下是否有鍵盤請求,總的來說,這樣基本上可以解決問題,而且簡單易行,但每10毫秒都要檢查,系統消耗太大。
2、採取中斷的方式:
(1)用高級語言調用中斷來處理問題。中斷是cpu響應一個中斷外圍設備8259A的一個過程,當鍵盤敲擊,cpu保存斷點暫停執行並且跳到相應的中斷處理程序繼續執行,結束後根據斷點再跳回來。通過這種方式可以輕松+愉快地解決這個問題。但是需要用到高級語言調用匯編,根據編譯器的不同而有所差別。
(2)自己模擬中斷。可以另外建立一個線程專門響應鍵盤的敲擊,如果有敲擊則打斷主線程。這樣做實現起來很復雜,而且涉及到不少復雜的關鍵技術,比如信號量之類的東西。
3、強大的vc
vc採取了消息映射的機制來處理外部設備的請求,比如時鍾中斷、鍵盤中斷等等。通過此可以灰常灰常容易的處理外部中斷。
F. 簡述設備驅動程序的功能
一、什麼是驅動程序
驅動程序,英文名為「Device Driver」,全稱為「設備驅動程序」, 是一種可以使計算機和設備通信的特殊程序,可以 說相當於硬體的介面,操作系統只有通過這個介面,才能控制硬體設備的工作,假如某設備的驅動程序未能正確安裝,便不能正常工作。 因此,驅動程序被譽為「 硬體的靈魂」、「硬體的主宰」、和「硬體和系統之間的橋梁」等。
剛安裝好的系統操作系統,很可能驅動程序安裝得不完整。硬體越新,這種可能性越大。菜菜熊之前看到的「圖標很大且顏色難看」就是沒有安裝好驅動的原因。
二、驅動程序的作用
硬體如果缺少了驅動程序的「驅動」,那麼本來性能非常強大的硬體就無法根據軟體發出的指令進行工作,硬體就是空有一身本領都無從發揮,毫無用武 之地。這時候,電腦就正如古人所說的「萬事俱備,只欠東風」,這「東風」的角色就落在了驅動程序身上。如此看來,驅動程序在電腦使用上還真起著舉足輕重的 作用。
從理論上講,所有的硬體設備都需要安裝相應的驅動程序才能正常工作。但像CPU、內存、主板、軟碟機、鍵盤、顯示器等設備卻並不需要安裝驅動程序也可以正常工作,而顯卡、音效卡、網卡等卻一定要安裝驅動程序,否則便無法正常工作。這是為什麼呢?
這主要是由於這些硬體對於一台個人電腦來說是必需的,所以早期的設計人員將這些硬體列為BIOS能直接支持的硬體。換句話說,上述硬體安裝後就 可以被BIOS和操作系統直接支持,不再需要安裝驅動程序。從這個角度來說,BIOS也是一種驅動程序。但是對於其他的硬體,例如:網卡,音效卡,顯卡等等 卻必須要安裝驅動程序,不然這些硬體就無法正常工作。
G. 字元設備驅動程序由哪幾部分組成
字元設備驅動程序框架
1、寫出open、write函數
2、告訴內核
1)、定義一個struct file_operations結構並填充好
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 這是一個宏,推向編譯模塊時自動創建的__this_mole變數 */
.open = first_drv_open,
.write = first_drv_write,
};
2)、把struct file_operations結構體告訴內核
major = register_chrdev(0, "first_drv", &first_drv_fops); // 注冊, 告訴內核
相關參數:第一個,設備號,0自動分配主設備號,否則為主設備號0-255
第二個:設備名
第二個:struct file_operations結構體
4)、register_chrdev由誰調用(入口函數調用)
static int first_drv_init(void)
5)、入口函數須使用內核宏來修飾
mole_init(first_drv_init);
mole_init會定義一個結構體,這個結構體裡面有一個函數指針指向first_drv_init這個函數,當我們載入或安裝一個驅動時,內核會自動找到這個結構體,然後調用裡面的函數指針,這個函數指針指向first_drv_init這個函數,first_drv_init這個函數就是把struct file_operations結構體告訴內核
6)、有入口函數就有出口函數
mole_exit(first_drv_exit);
最後加上協議
MODULE_LICENSE("GPL");
3、mdev根據系統信息自動創建設備節點:
每次寫驅動都要手動創建設備文件過於麻煩,使用設備管理文件系統則方便很多。在2.6的內核以前一直使用的是devfs,但是它存在許多缺陷。它創建了大量的設備文件,其實這些設備更本不存在。而且設備與設備文件的映射具有不確定性,比如U盤即可能對應sda,又可能對應sdb。沒有足夠的主/輔設備號。2.6之後的內核引入了sysfs文件系統,它掛載在/sys上,配合udev使用,可以很好的完成devfs的功能,並彌補了那些缺點。(這里說一下,當今內核已經使用netlink了)。
udev是用戶空間的一個應用程序,在嵌入式中用的是mdev,mdev在busybox中。mdev是udev的精簡版。
首先在busybox中添加支持mdev的選項:
Linux System Utilities --->
[*] mdev
[*] Support /etc/mdev.conf
[*] Support subdirs/symlinks
[*] Support regular expressions substitutions when renaming device
[*] Support command execution at device addition/removal
然後修改/etc/init.d/rcS:
echo /sbin/mdev > /proc/sys/kernel/hotplug
/sbin/mdev -s
執行mdev -s :以『-s』為參數調用位於 /sbin目錄寫的mdev(其實是個鏈接,作用是傳遞參數給/bin目錄下的busybox程序並調用它),mdev掃描 /sys/class 和 /sys/block 中所有的類設備目錄,如果在目錄中含有名為「dev」的文件,且文件中包含的是設備號,則mdev就利用這些信息為這個設備在/dev 下創建設備節點文件。一般只在啟動時才執行一次 「mdev -s」。
熱插拔事件:由於啟動時運行了命 令:echo /sbin/mdev > /proc/sys/kernel/hotplug ,那麼當有熱插拔事件產生時,內核就會調用位於 /sbin目錄的mdev。這時mdev通過環境變數中的 ACTION 和 DEVPATH,來確定此次熱插拔事件的動作以及影響了/sys中的那個目錄。接著會看看這個目錄中是否「dev」的屬性文件,如果有就利用這些信息為 這個設備在/dev 下創建設備節點文件
重新打包文件系統,這樣/sys目錄,/dev目錄就有東西了
下面是create_class的原型:
#define class_create(owner, name) /
({ /
static struct lock_class_key __key; /
__class_create(owner, name, &__key); /
})
extern struct class * __must_check __class_create(struct mole *owner,
const char *name,
struct lock_class_key *key);
class_destroy的原型如下:
extern void class_destroy(struct class *cls);
device_create的原型如下:
extern struct device *device_create(struct class *cls, struct device *parent,
dev_t devt, void *drvdata,
const char *fmt, ...)
__attribute__((format(printf, 5, 6)));
device_destroy的原型如下:
extern void device_destroy(struct class *cls, dev_t devt);
具體使用如下,可參考後面的實例:
static struct class *firstdrv_class;
static struct class_device *firstdrv_class_dev;
firstdrv_class = class_create(THIS_MODULE, "firstdrv");
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
class_device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);
下面再來看一下應用程序如何找到這個結構體的
在應用程序中我們使用open打開一個設備:如:open(/dev/xxx, O_RDWR);
xxx有一個屬性,如字元設備為c,後面為讀寫許可權,還有主設備名、次設備名,我們注冊時 通過register_chrdev(0, "first_drv", &first_drv_fops)(有主設備號,設備名,struct file_operations結構體)將first_drv_fops結構體注冊到內核數組chrdev中去的,結構體中有open,write函數,那麼應用程序如何找到它的,事實上是根據打開的這個文件的屬性中的設備類型及主設備號在內核數組chrdev裡面找到我們注冊的first_drv_fops,
實例代碼:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static struct class *firstdrv_class;
static struct class_device *firstdrv_class_dev;
volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;
static int first_drv_open(struct inode *inode, struct file *file)
{
//printk("first_drv_open\n");
/* 配置GPF4,5,6為輸出 */
*gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));
*gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));
return 0;
}
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val;
//printk("first_drv_write\n");
_from_user(&val, buf, count); // _to_user();
if (val == 1)
{
// 點燈
*gpfdat &= ~((1<<4) | (1<<5) | (1<<6));
}
else
{
// 滅燈
*gpfdat |= (1<<4) | (1<<5) | (1<<6);
}
return 0;
}
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 這是一個宏,推向編譯模塊時自動創建的__this_mole變數 */
.open = first_drv_open,
.write = first_drv_write,
};
int major;
static int first_drv_init(void)
{
major = register_chrdev(0, "first_drv", &first_drv_fops); // 注冊, 告訴內核
firstdrv_class = class_create(THIS_MODULE, "firstdrv");
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
gpfdat = gpfcon + 1;
return 0;
}
static void first_drv_exit(void)
{
unregister_chrdev(major, "first_drv"); // 卸載
class_device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);
iounmap(gpfcon);
}
mole_init(first_drv_init);
mole_exit(first_drv_exit);
MODULE_LICENSE("GPL");
編譯用Makefile文件
KERN_DIR = /work/system/linux-2.6.22.6
all:
make -C $(KERN_DIR) M=`pwd` moles
clean:
make -C $(KERN_DIR) M=`pwd` moles clean
rm -rf moles.order
obj-m += first_drv.o
測試程序:
#include
#include
#include
#include
/* firstdrvtest on
* firstdrvtest off
*/
int main(int argc, char **argv)
{
int fd;
int val = 1;
fd = open("/dev/xyz", O_RDWR);
if (fd < 0)
{
printf("can't open!\n");
}
if (argc != 2)
{
printf("Usage :\n");
printf("%s \n", argv[0]);
return 0;
}
if (strcmp(argv[1], "on") == 0)
{
val = 1;
}
else
{
val = 0;
}
write(fd, &val, 4);
return 0;
}
H. .常見的linux字元設備驅動程序由哪些組成 及其主要函數的作用
關於linux字元設備驅動涉及內容很多哈
具體設計時並不是單純的字元設備,還得專糅合其他匯流排啊什麼的
主要屬的數據結構: cdev , inode, file_operation還有自己定義的描述設備的結構
主要的機制:各種鎖,如自旋鎖,互斥鎖等等
模塊初始化
添加設備
激活設備
實現file_operation中需要的幾個函數(其中包括對中斷的處理)
釋放資源
有本書叫 linux設備驅動程序 ,裡面有更詳細更全面的介紹,可以看看哈
I. 試說明設備驅動程序應具有哪些功能
試說明設備驅動程序應具有哪些功能?
答:設備驅動程序的主要功能包括:
(1)將接收到的抽象要求轉為具體要求;
(2)檢查用戶 I/O 請求合法性,了解 I/O 設備狀態,傳遞有關參數,設置設備工作方式;
(3)發出 I/O 命令,啟動分配到的 I/O 設備,完成指定 I/O 操作;
(4)及時響應由控制器或通道發來的中斷請求,根據 中斷類型調用相應中斷處理程序處理;
(5)對於有通道的計算機,驅動程序還應該根 據用戶 I/O 請求自動構成通道程序。
J. 試說明設備驅動程序應具有哪些特點
設備驅動程序的特點
(1)驅動程序是與設備無關的軟體和設備控制器之間通信和轉換的程序。
(2)驅動程序,與設備控制器和I/O設備的硬體特性,緊密相關。
(3)驅動程序與I/O設備所採用的I/O控制方式緊密相關。
(4)由於驅動程序與硬體緊密相關,因而其中的一部分必須用匯編語言編寫。
(5)驅動程序應允許可重入,一個正在運行的驅動程序常會在一次調用完成前被再次調用。
作者:殺手的手剎
鏈接:https://www.jianshu.com/p/74c91ea25970
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請註明出處。