新聞中心
隨著計(jì)算機(jī)技術(shù)的不斷發(fā)展,操作系統(tǒng)的種類也越來(lái)越多樣化。其中,Linux操作系統(tǒng)在開(kāi)源方面發(fā)揮了極大的作用,提供了各種各樣的開(kāi)源工具和平臺(tái),讓開(kāi)發(fā)者們可以自由地進(jìn)行開(kāi)發(fā)。

創(chuàng)新互聯(lián)公司致力于互聯(lián)網(wǎng)網(wǎng)站建設(shè)與網(wǎng)站營(yíng)銷,提供網(wǎng)站建設(shè)、成都網(wǎng)站建設(shè)、網(wǎng)站開(kāi)發(fā)、seo優(yōu)化、網(wǎng)站排名、互聯(lián)網(wǎng)營(yíng)銷、成都微信小程序、公眾號(hào)商城、等建站開(kāi)發(fā),創(chuàng)新互聯(lián)公司網(wǎng)站建設(shè)策劃專家,為不同類型的客戶提供良好的互聯(lián)網(wǎng)應(yīng)用定制解決方案,幫助客戶在新的全球化互聯(lián)網(wǎng)環(huán)境中保持優(yōu)勢(shì)。
Linux系統(tǒng)中的字符驅(qū)動(dòng)是其中的一個(gè)重要部分。這種驅(qū)動(dòng)程序可以幫助Linux系統(tǒng)與硬件設(shè)備進(jìn)行通信,使得硬件設(shè)備能夠被正確地識(shí)別和使用。在本文中,我們將詳細(xì)討論Linux字符驅(qū)動(dòng)的開(kāi)發(fā)及其應(yīng)用。
一、Linux字符驅(qū)動(dòng)的基礎(chǔ)知識(shí)
在學(xué)習(xí)Linux字符驅(qū)動(dòng)的開(kāi)發(fā)之前,我們需要了解一些基礎(chǔ)知識(shí)。
在Linux系統(tǒng)中,每一個(gè)設(shè)備都被表示為一個(gè)文件。這種文件稱為設(shè)備文件。設(shè)備文件的特點(diǎn)是可以像普通文件一樣被訪問(wèn)和編輯。但是,由于設(shè)備本身是硬件設(shè)備,因此對(duì)設(shè)備文件的訪問(wèn)會(huì)被轉(zhuǎn)化為對(duì)硬件設(shè)備的操作。
在Linux系統(tǒng)中,硬件設(shè)備通常被分為三種類型:字符設(shè)備、塊設(shè)備和網(wǎng)絡(luò)設(shè)備。在本文中,我們主要討論的是字符設(shè)備。
字符設(shè)備在Linux系統(tǒng)中的表示方式是一個(gè)數(shù)字,稱為主設(shè)備號(hào)。每個(gè)字符設(shè)備可以擁有不同的主設(shè)備號(hào)。除此之外,每個(gè)字符設(shè)備還可以有一些輔助設(shè)備號(hào),被稱為次設(shè)備號(hào)。字符設(shè)備的主設(shè)備號(hào)和次設(shè)備號(hào)可以幫助內(nèi)核確定要使用哪個(gè)設(shè)備驅(qū)動(dòng)程序與硬件設(shè)備進(jìn)行通信。
Linux系統(tǒng)中一般使用C語(yǔ)言編寫驅(qū)動(dòng)程序。這是因?yàn)镃語(yǔ)言具有良好的性能和可移植性,可以很好地適應(yīng)不同的硬件設(shè)備和操作系統(tǒng)。
二、Linux字符驅(qū)動(dòng)的開(kāi)發(fā)過(guò)程
了解了Linux字符驅(qū)動(dòng)的基礎(chǔ)知識(shí)之后,我們來(lái)看一下Linux字符驅(qū)動(dòng)的開(kāi)發(fā)過(guò)程。
我們需要為要開(kāi)發(fā)的字符設(shè)備創(chuàng)建一個(gè)設(shè)備文件。這可以通過(guò)以下命令實(shí)現(xiàn):
“`
sudo mknod /dev/mydev c
“`
其中,mydev是我們?yōu)樵O(shè)備命名的名稱。主設(shè)備號(hào)和次設(shè)備號(hào)可以根據(jù)需要進(jìn)行設(shè)置。
接下來(lái),我們需要編寫字符驅(qū)動(dòng)程序。驅(qū)動(dòng)程序的代碼通常包含以下幾個(gè)部分:
1. 頭文件。這些文件通常包括linux/init.h、linux/module.h、linux/fs.h等。
2. 模塊初始化函數(shù)。該函數(shù)在驅(qū)動(dòng)程序加載時(shí)被調(diào)用。
3. 文件操作函數(shù)。這些函數(shù)包括打開(kāi)、關(guān)閉、讀取、寫入、控制等操作。這些函數(shù)是驅(qū)動(dòng)程序的主要實(shí)現(xiàn)部分。
4. 模塊清理函數(shù)。該函數(shù)在驅(qū)動(dòng)程序卸載時(shí)被調(diào)用,用于清理模塊中的資源。
下面是一個(gè)簡(jiǎn)單的例子:
“`
#include
#include
#include
#include
MODULE_LICENSE(“GPL”);
int mydev_open(struct inode *inode, struct file *filp)
{
printk(KERN_INFO “mydev: device is opened\n”);
return 0;
}
int mydev_release(struct inode *inode, struct file *filp)
{
printk(KERN_INFO “mydev: device is closed\n”);
return 0;
}
ssize_t mydev_read(struct file *filp, char *buffer, size_t length, loff_t *offset)
{
printk(KERN_INFO “mydev: read from device\n”);
return 0;
}
ssize_t mydev_write(struct file *filp, const char *buffer, size_t length, loff_t *offset)
{
printk(KERN_INFO “mydev: write to device\n”);
return length;
}
struct file_operations mydev_fops =
{
.owner = THIS_MODULE,
.open = mydev_open,
.release = mydev_release,
.read = mydev_read,
.write = mydev_write,
};
static int __init mydev_init(void)
{
printk(KERN_INFO “mydev: module is loaded\n”);
return register_chrdev(240, “mydev”, &mydev_fops);
}
static void __exit mydev_exit(void)
{
printk(KERN_INFO “mydev: module is unloaded\n”);
unregister_chrdev(240, “mydev”);
}
module_init(mydev_init);
module_exit(mydev_exit);
“`
在上面的例子中,我們定義了四個(gè)文件操作函數(shù),分別是打開(kāi)、關(guān)閉、讀取和寫入函數(shù)。這些函數(shù)都很簡(jiǎn)單,只是輸出一些信息。
除此之外,我們還定義了一個(gè)`struct file_operations`類型的變量,包含了這些文件操作函數(shù)的指針。然后,在初始化函數(shù)`mydev_init`中,我們調(diào)用了函數(shù)`register_chrdev`,將我們定義的文件操作符和主設(shè)備號(hào)240注冊(cè)到內(nèi)核中。在卸載函數(shù)`mydev_exit`中,我們調(diào)用了函數(shù)`unregister_chrdev`,將之前注冊(cè)的主設(shè)備號(hào)卸載。
三、Linux字符驅(qū)動(dòng)的應(yīng)用
完成了字符驅(qū)動(dòng)的開(kāi)發(fā)之后,我們可以進(jìn)行一些簡(jiǎn)單的測(cè)試。
使用make命令編譯并安裝我們的驅(qū)動(dòng)程序:
“`
make
sudo inod mydev.ko
“`
然后,我們可以使用以下命令打開(kāi)設(shè)備文件并對(duì)其進(jìn)行一些操作:
“`
sudo cat /dev/mydev
“`
這時(shí),我們的讀取函數(shù)將會(huì)被調(diào)用,并輸出一條信息。我們還可以對(duì)設(shè)備文件進(jìn)行寫入操作:
“`
sudo echo “hello” >/dev/mydev
“`
這時(shí),我們的寫入函數(shù)將會(huì)被調(diào)用,并輸出一條信息。
相關(guān)問(wèn)題拓展閱讀:
- 在Linux內(nèi)核中,注冊(cè)字符設(shè)備驅(qū)動(dòng)程序的函數(shù)是?
- Linux字符設(shè)備驅(qū)動(dòng)的組成?
在Linux內(nèi)核中,注冊(cè)字符設(shè)備驅(qū)動(dòng)程序的函數(shù)是?
字符設(shè)備驅(qū)動(dòng)程序框架 1、寫出open、write函數(shù) 2、告訴內(nèi)核 1)、定義一個(gè)struct file_operations結(jié)構(gòu)并填充好 static struct file_operations first_drv_fops = { .owner = THIS_MODULE, /* 這是一個(gè)宏,推向編譯模塊時(shí)自動(dòng)創(chuàng)建的__this_module變量 */ .open = first_drv_open, .write = first_drv_write, }; 2)、把struct file_operations結(jié)構(gòu)體告訴內(nèi)核 major = register_chrdev(0, “first_drv”, &first_drv_fops); // 注冊(cè), 告訴內(nèi)核相關(guān)參數(shù):之一個(gè),設(shè)備號(hào),0自動(dòng)分配主設(shè)備號(hào),否則為主設(shè)備號(hào)0-255 第二個(gè):設(shè)備名第二個(gè):struct file_operations結(jié)構(gòu)體 4)、register_chrdev由誰(shuí)調(diào)用(入口函數(shù)調(diào)用) static int first_drv_init(void) 5)、入口函數(shù)須使用內(nèi)核宏來(lái)修飾 module_init(first_drv_init); module_init會(huì)定義一個(gè)結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體里面有一個(gè)函數(shù)指針指向first_drv_init這個(gè)函數(shù),當(dāng)我們加載或安裝一個(gè)驅(qū)動(dòng)時(shí),內(nèi)核會(huì)自動(dòng)找到這個(gè)結(jié)構(gòu)體,然后調(diào)用里面的函數(shù)指針,這個(gè)函數(shù)指針指向first_drv_init這個(gè)函數(shù),first_drv_init這個(gè)函數(shù)就是把struct file_operations結(jié)構(gòu)體告訴內(nèi)核 6)、有入口函數(shù)就有出口函數(shù) module_exit(first_drv_exit); 最后加上協(xié)議 MODULE_LICENSE(“GPL”); 3、mdev根據(jù)系統(tǒng)信息自動(dòng)創(chuàng)建設(shè)備節(jié)點(diǎn): 每次寫驅(qū)動(dòng)都要手動(dòng)創(chuàng)建設(shè)備文件過(guò)于麻煩,使用設(shè)備管理文件系統(tǒng)則方便很多。在2.6的內(nèi)核以前一直使用的是devfs,但是它存在許多缺陷。它創(chuàng)建了大量的設(shè)備文件,其實(shí)這些設(shè)備更本不存在。而且設(shè)備與設(shè)備文件的映射具有不確定性,比如U盤即可能對(duì)應(yīng)sda,又可能對(duì)應(yīng)sdb。沒(méi)有足夠的主/輔設(shè)備號(hào)。2.6之后的內(nèi)核引入了sysfs文件系統(tǒng),它掛載在/sys上,配合udev使用,可以很好的完成devfs的功能,并彌補(bǔ)了那些缺點(diǎn)。(這里說(shuō)一下,當(dāng)今內(nèi)核已經(jīng)使用netlink了)。 udev是用戶空間的一個(gè)應(yīng)用程序,在嵌入式中用的是mdev,mdev在busybox中。mdev是udev的精簡(jiǎn)版。首先在busybox中添加支持mdev的選項(xiàng): 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 /in/mdev > /proc/sys/kernel/hotplug /in/mdev -s 執(zhí)行mdev -s :以‘-s’為參數(shù)調(diào)用位于 /in目錄寫的mdev(其實(shí)是個(gè)鏈接,作用是傳遞參數(shù)給/bin目錄下的busybox程序并調(diào)用它),mdev掃描 /sys/class 和 /sys/block 中所有的類設(shè)備目錄,如果在目錄中含有名為“dev”的文件,且文件中包含的是設(shè)備號(hào),則mdev就利用這些信息為這個(gè)設(shè)備在/dev 下創(chuàng)建設(shè)備節(jié)點(diǎn)文件。一般只在啟動(dòng)時(shí)才執(zhí)行一次 “mdev -s”。熱插拔事件:由于啟動(dòng)時(shí)運(yùn)行了命 令:echo /in/mdev > /proc/sys/kernel/hotplug ,那么當(dāng)有熱插拔事件產(chǎn)生時(shí),內(nèi)核就會(huì)調(diào)用位于 /in目錄的mdev。這時(shí)mdev通過(guò)環(huán)境變量中的 ACTION 和 DEVPATH,來(lái)確定此次熱插拔事件的動(dòng)作以及影響了/sys中的那個(gè)目錄。接著會(huì)看看這個(gè)目錄中是否“dev”的屬性文件,如果有就利用這些信息為 這個(gè)設(shè)備在/dev 下創(chuàng)建設(shè)備節(jié)點(diǎn)文件重新打包文件系統(tǒng),這樣/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 module *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); 具體使用如下,可參考后面的實(shí)例: 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); 下面再來(lái)看一下應(yīng)用程序如何找到這個(gè)結(jié)構(gòu)體的在應(yīng)用程序中我們使用open打開(kāi)一個(gè)設(shè)備:如:open(/dev/xxx, O_RDWR); xxx有一個(gè)屬性,如字符設(shè)備為c,后面為讀寫權(quán)限,還有主設(shè)備名、次設(shè)備名,我們注冊(cè)時(shí) 通過(guò)register_chrdev(0, “first_drv”, &first_drv_fops)(有主設(shè)備號(hào),設(shè)備名,struct file_operations結(jié)構(gòu)體)將first_drv_fops結(jié)構(gòu)體注冊(cè)到內(nèi)核數(shù)組chrdev中去的,結(jié)構(gòu)體中有open,write函數(shù),那么應(yīng)用程序如何找到它的,事實(shí)上是根據(jù)打開(kāi)的這個(gè)文件的屬性中的設(shè)備類型及主設(shè)備號(hào)在內(nèi)核數(shù)組chrdev里面找到我們注冊(cè)的first_drv_fops,實(shí)例代碼: #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); return 0; } if (strcmp(argv, “on”) == 0) { val = 1; } else { val = 0; } write(fd, &val, 4); return 0; }
Linux字符設(shè)備驅(qū)動(dòng)的組成?
Linux系統(tǒng)下具有三種設(shè)備,分別是字符設(shè)備、塊設(shè)備和網(wǎng)絡(luò)設(shè)備,Linux下的字符設(shè)備是指只能一個(gè)字節(jié)一個(gè)字節(jié)讀寫的設(shè)備,不能隨機(jī)讀取設(shè)備內(nèi)存中某一數(shù)據(jù),讀取數(shù)據(jù)的時(shí)候需要按照先后順序進(jìn)行,字符設(shè)備是面向流的設(shè)備,常見(jiàn)的字符設(shè)備有鼠標(biāo)、鍵盤、串口、控制臺(tái)和LED等,接下來(lái),簡(jiǎn)單介紹Linux字符設(shè)備驅(qū)動(dòng)的基本結(jié)構(gòu)。更多Linux介紹可查看《Linux就該這么學(xué)》。
在Linux中,字符設(shè)備驅(qū)動(dòng)由如下幾個(gè)部分組成。
1.字符設(shè)備驅(qū)動(dòng)模塊加載與卸載函數(shù)
在字符設(shè)備驅(qū)動(dòng)模塊加載函數(shù)中應(yīng)該實(shí)現(xiàn)設(shè)備號(hào)的申請(qǐng)和cdev的注冊(cè),而在卸載函數(shù)中應(yīng)實(shí)現(xiàn)設(shè)備號(hào)
的釋放和cdev的注銷。
Linux內(nèi)核的編碼習(xí)慣是為設(shè)備定義一個(gè)設(shè)備相關(guān)的結(jié)構(gòu)體,該結(jié)構(gòu)體包含設(shè)備所涉及的cdev、私有
數(shù)據(jù)及鎖等信息。2.字符設(shè)備驅(qū)動(dòng)的file_operations結(jié)構(gòu)體中的成員函數(shù)
file_operations結(jié)構(gòu)體中的成員函數(shù)是字符設(shè)備驅(qū)動(dòng)與內(nèi)核虛擬文件系統(tǒng)的接口,是用戶空間對(duì)Linux
進(jìn)行系統(tǒng)調(diào)用最終的落實(shí)者。設(shè)備驅(qū)動(dòng)的讀函數(shù)中,filp是文件結(jié)構(gòu)體指針,buf是用戶空間內(nèi)存的地址,該地址在內(nèi)核空間不宜直
接讀寫,count是要讀的字節(jié)數(shù),f_pos是讀的位置相對(duì)于文件開(kāi)頭的偏移。
設(shè)備驅(qū)動(dòng)的寫函數(shù)中,filp是文件結(jié)構(gòu)體指針,buf是用戶空間內(nèi)存的地址,該地址在內(nèi)核空間不宜直
接讀寫,count是要寫的字節(jié)數(shù),f_pos是寫的位置相對(duì)于文件開(kāi)頭的偏移。
由于用戶空間不能直接訪問(wèn)內(nèi)核空間的內(nèi)存,因此借助了函數(shù)copy_from_user()完成用戶空間緩沖
區(qū)到內(nèi)核空間的復(fù)制,以及copy_to_user()完成內(nèi)核空間到用戶空間緩沖區(qū)的復(fù)制,見(jiàn)代碼第6行和第14
行。
完成內(nèi)核空間和用戶空間內(nèi)存復(fù)制的copy_from_user()和copy_to_user()的原型分別為:
unsigned long copy_from_user(void *to, const void _ _user *from, unsigned long count);
unsigned long copy_to_user(void _ _user *to, const void *from, unsigned long count);
上述函數(shù)均返回不能被復(fù)制的字節(jié)數(shù),因此,如果完全復(fù)制成功,返回值為0。如果復(fù)制失敗,則返
回負(fù)值。如果要復(fù)制的內(nèi)存是簡(jiǎn)單類型,如char、int、long等,則可以使用簡(jiǎn)單的put_user()和
get_user()讀和寫函數(shù)中的_user是一個(gè)宏,表明其后的指針指向用戶空間,實(shí)際上更多地充當(dāng)了代碼自注釋的
功能。內(nèi)核空間雖然可以訪問(wèn)用戶空間的緩沖區(qū),但是在訪問(wèn)之前,一般需要先檢查其合法性,通過(guò)
access_ok(type,addr,size)進(jìn)行判斷,以確定傳入的緩沖區(qū)的確屬于用戶空間。
17 Linux字符設(shè)備驅(qū)動(dòng)概述
關(guān)于linux字符驅(qū)動(dòng)實(shí)例的介紹到此就結(jié)束了,不知道你從中找到你需要的信息了嗎 ?如果你還想了解更多這方面的信息,記得收藏關(guān)注本站。
成都服務(wù)器托管選創(chuàng)新互聯(lián),先上架開(kāi)通再付費(fèi)。
創(chuàng)新互聯(lián)(www.cdcxhl.com)專業(yè)-網(wǎng)站建設(shè),軟件開(kāi)發(fā)老牌服務(wù)商!微信小程序開(kāi)發(fā),APP開(kāi)發(fā),網(wǎng)站制作,網(wǎng)站營(yíng)銷推廣服務(wù)眾多企業(yè)。電話:028-86922220
當(dāng)前題目:實(shí)例詳解:Linux字符驅(qū)動(dòng)的開(kāi)發(fā)與應(yīng)用 (linux字符驅(qū)動(dòng)實(shí)例)
網(wǎng)站URL:http://m.fisionsoft.com.cn/article/djdiiig.html


咨詢
建站咨詢
