linux 驅(qū)動(dòng)編寫之虛擬字符設(shè)備的編寫實(shí)例詳解
linux 驅(qū)動(dòng)編寫
前言:
昨天我們說(shuō)了一些簡(jiǎn)單模塊編寫方法,但是終歸沒(méi)有涉及到設(shè)備的編寫內(nèi)容,今天我們就可以了解一下相關(guān)方面的內(nèi)容,并且用一個(gè)實(shí)例來(lái)說(shuō)明在Linux上面設(shè)備是如何編寫的。雖然我不是專門做linux驅(qū)動(dòng)的,卻也經(jīng)常收到一些朋友們的來(lái)信。在信件中,很多做驅(qū)動(dòng)的朋友對(duì)自己的工作不是很滿意,認(rèn)為自己的工作就是把代碼拷貝來(lái)拷貝去,或者說(shuō)是改來(lái)改去,沒(méi)有什么技術(shù)含量。有這種想法的朋友不在少數(shù),我想這主要還是因?yàn)樗麄儗?duì)自己的工作缺少了解導(dǎo)致。如果有可能,我們可以問(wèn)問(wèn)自己這樣幾個(gè)問(wèn)題:
(1)我真的搞懂設(shè)備的開(kāi)發(fā)驅(qū)動(dòng)流程了嗎?我是否可以從0開(kāi)始,編寫一個(gè)獨(dú)立的驅(qū)動(dòng)代碼呢?
(2)我真的了解設(shè)備的初始化、關(guān)閉、運(yùn)行的流程嗎?
(3)當(dāng)前的設(shè)備驅(qū)動(dòng)流程是否合理,有沒(méi)有可以改進(jìn)的地方?
(4)對(duì)于內(nèi)核開(kāi)發(fā)中涉及的api調(diào)用,我自己是否真正了解、是否明白它們?cè)谑褂蒙嫌惺裁磪^(qū)別?
(5)如果我要驅(qū)動(dòng)的設(shè)備只是在一個(gè)前后臺(tái)系統(tǒng)中運(yùn)行,在沒(méi)有框架幫助的情況下,我是否有信心把它啟動(dòng)和運(yùn)行起來(lái)?
當(dāng)然,上面的內(nèi)容只是我個(gè)人的想法,也不一定都正確。但是,知其然,更要知其所以然,熟悉了當(dāng)前開(kāi)發(fā)流程的優(yōu)缺點(diǎn)才能真正掌握和了解驅(qū)動(dòng)開(kāi)發(fā)的本質(zhì)。這聽(tīng)上去有些玄乎,其實(shí)也很簡(jiǎn)單,就是要有一種刨根問(wèn)底、不斷改進(jìn)的精神,這樣才能做好自己的工作。因?yàn)槲覀兪窃趐c linux上學(xué)習(xí)驅(qū)動(dòng)的,因此暫時(shí)沒(méi)有真實(shí)的外接設(shè)備可以使用,但是這絲毫不影響我們學(xué)習(xí)的熱情。通過(guò)定時(shí)器、進(jìn)程,我們可以仿真出真實(shí)設(shè)備的各種需求,所以對(duì)于系統(tǒng)來(lái)說(shuō),它是無(wú)所謂真設(shè)備、假設(shè)備的,基本的處理流程對(duì)它來(lái)說(shuō)都是一樣的。只要大家一步一步做下去,肯定可以了解linux驅(qū)動(dòng)設(shè)備的開(kāi)發(fā)工程的。
下面,為了說(shuō)明問(wèn)題,我們可以編寫一段簡(jiǎn)單的char設(shè)備驅(qū)動(dòng)代碼,文件名為char.c,
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/cdev.h> static struct cdev chr_dev; static dev_t ndev; static int chr_open(struct inode* nd, struct file* filp) { int major ; int minor; major = MAJOR(nd->i_rdev); minor = MINOR(nd->i_rdev); printk("chr_open, major = %d, minor = %d\n", major, minor); return 0; } static ssize_t chr_read(struct file* filp, char __user* u, size_t sz, loff_t* off) { printk("chr_read process!\n"); return 0; } struct file_operations chr_ops = { .owner = THIS_MODULE, .open = chr_open, .read = chr_read }; static int demo_init(void) { int ret; cdev_init(&chr_dev, &chr_ops); ret = alloc_chrdev_region(&ndev, 0, 1, "chr_dev"); if(ret < 0 ) { return ret; } printk("demo_init(): major = %d, minor = %d\n", MAJOR(ndev), MINOR(ndev)); ret = cdev_add(&chr_dev, ndev, 1); if(ret < 0) { return ret; } return 0; } static void demo_exit(void) { printk("demo_exit process!\n"); cdev_del(&chr_dev); unregister_chrdev_region(ndev, 1); } module_init(demo_init); module_exit(demo_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("feixiaoxing@163.com"); MODULE_DESCRIPTION("A simple device example!");
在module_init中的函數(shù)是模塊加載時(shí)處理的函數(shù),而模塊卸載的函數(shù)則是在module_exit中。每一個(gè)設(shè)備都要對(duì)應(yīng)一個(gè)基本的設(shè)備數(shù)據(jù),當(dāng)然為了使得這個(gè)設(shè)備注冊(cè)在整個(gè)系統(tǒng)當(dāng)中,我們還需要分配一個(gè)設(shè)備節(jié)點(diǎn),alloc_chrdev_region就完成這樣一個(gè)功能。等到cdev_add的時(shí)候,整個(gè)設(shè)備注冊(cè)的過(guò)程就全部完成了,就是這么簡(jiǎn)單。當(dāng)然為了編寫這個(gè)文件,我們還需要編寫一個(gè)Makefile文件,
ifneq ($(KERNELRELEASE),) obj-m := char.o else PWD := $(shell pwd) KVER := $(shell uname -r) KDIR := /lib/modules/$(KVER)/build all: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions modules.* Module.* endif
這個(gè)Makefile文件和我們之前編寫的makefile基本上沒(méi)有區(qū)別,唯一的區(qū)別就是文件名稱改成了char.o,僅此而已。為了編寫模塊,我們直接輸入make即可。這時(shí)候,char.ko文件就可以生成了。然后,模塊需要被注冊(cè)在系統(tǒng)當(dāng)中,insmod char.ko是少不了的。如果此時(shí),我們還不確信是否模塊已經(jīng)加入到系統(tǒng)當(dāng)中,完全可以通過(guò)輸入lsmod | grep char進(jìn)行查找和驗(yàn)證。為了創(chuàng)建設(shè)備節(jié)點(diǎn),我們需要知道設(shè)備為我們創(chuàng)建的major、minor數(shù)值是多少,所以dmesg | tail 查找一下數(shù)值。在我hp的機(jī)器上,這兩個(gè)數(shù)值分別是249和0,所以下面可以利用它們直接創(chuàng)建設(shè)備節(jié)點(diǎn)了,輸入mknod /dev/chr_dev c 249 0即可,此時(shí)可以輸入ls /dev/chr_dev驗(yàn)證一下。那么,按照這種方法,真的可以訪問(wèn)這個(gè)虛擬設(shè)備了嗎,我們可以編寫一段簡(jiǎn)單的代碼驗(yàn)證一下,
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #define CHAR_DEV_NAME "/dev/chr_dev" int main() { int ret; int fd; char buf[32]; fd = open(CHAR_DEV_NAME, O_RDONLY | O_NDELAY); if(fd < 0) { printf("open failed!\n"); return -1; } read(fd, buf, 32); close(fd); return 0; }
代碼的內(nèi)容非常簡(jiǎn)單,就是利用CHAR_DEV_NAME直接打開(kāi)設(shè)備,讀寫設(shè)備。當(dāng)然。首先還是需要對(duì)這個(gè)文件進(jìn)行編譯,文件名為test.c,輸入gcc test.c -o test,其次就是運(yùn)行這個(gè)文件,直接輸入./test即可。如果沒(méi)有問(wèn)題的話,那么說(shuō)明我們的代碼是ok的,但是我們還是沒(méi)有看到任何內(nèi)容。沒(méi)關(guān)系,我們還是通過(guò)dmesg這個(gè)命令查看內(nèi)核中是否存在相關(guān)的打印內(nèi)容,直接輸入dmesg | tail即可。此時(shí)如果沒(méi)有意外的話,我們就可以看到之前在chr_open和chr_read中留下的printk打印,這說(shuō)明我們的代碼完全是ok的。
上面的代碼只是一段小例子,真實(shí)的內(nèi)容要比這復(fù)雜一下。不過(guò)既然我們都已經(jīng)入門了,那么后面的內(nèi)容其實(shí)也沒(méi)有什么好怕的了。最后有兩個(gè)事情補(bǔ)充一下:(1)如果大家在創(chuàng)建節(jié)點(diǎn)后想刪除設(shè)備節(jié)點(diǎn),直接rm -rf /dev/chr_dev即可;(2)上面這段代碼的原型來(lái)自于《深入linux設(shè)備驅(qū)動(dòng)程序內(nèi)核機(jī)制》這本書,稍作修改,如果大家對(duì)內(nèi)核機(jī)制的內(nèi)容感興趣,可以參考這本書的內(nèi)容。
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
Win10 + Ubuntu 16.04雙系統(tǒng)完美安裝教程【詳細(xì)】
這篇文章主要介紹了Win10 + Ubuntu 16.04雙系統(tǒng)完美安裝教程,本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-10-10jmeter在linux系統(tǒng)下運(yùn)行及本地內(nèi)存調(diào)優(yōu)的方法詳解
這篇文章主要介紹了jmeter在linux系統(tǒng)下運(yùn)行及本地內(nèi)存調(diào)優(yōu)的方法,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07CentOS7.4開(kāi)機(jī)出現(xiàn)welcome to emergency mode的解決方法
CentOS7.4開(kāi)機(jī)出現(xiàn)welcome to emergency mode,報(bào)這個(gè)錯(cuò)誤多數(shù)情況下是因?yàn)?etc/fstab文件的錯(cuò)誤。注意一下是不是加載了外部硬盤、存儲(chǔ)器或者是網(wǎng)絡(luò)共享空間,在重啟時(shí)沒(méi)有加載上導(dǎo)致的2018-09-09Shell中如何刪除文本比較長(zhǎng)的行的實(shí)現(xiàn)方法
這篇文章主要介紹了 Shell中如何刪除文本比較長(zhǎng)的行的實(shí)現(xiàn)方法的相關(guān)資料,希望通過(guò)本文能幫助到大家,讓大家掌握這部分內(nèi)容,需要的朋友可以參考下2017-10-10讓DOSBox啟動(dòng)后自動(dòng)執(zhí)行命令的方法講解
今天小編就為大家分享一篇關(guān)于讓DOSBox啟動(dòng)后自動(dòng)執(zhí)行命令的方法講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-02-02Ubuntu環(huán)境編譯安裝PHP和Nginx的方法
這篇文章主要介紹了Ubuntu環(huán)境編譯安裝PHP和Nginx的方法,較為詳細(xì)的分析了Ubuntu環(huán)境編譯安裝PHP和Nginx的具體步驟、相關(guān)命令與操作技巧,需要的朋友可以參考下2019-08-08