linux 驅動編寫之虛擬字符設備的編寫實例詳解
linux 驅動編寫
前言:
昨天我們說了一些簡單模塊編寫方法,但是終歸沒有涉及到設備的編寫內容,今天我們就可以了解一下相關方面的內容,并且用一個實例來說明在Linux上面設備是如何編寫的。雖然我不是專門做linux驅動的,卻也經常收到一些朋友們的來信。在信件中,很多做驅動的朋友對自己的工作不是很滿意,認為自己的工作就是把代碼拷貝來拷貝去,或者說是改來改去,沒有什么技術含量。有這種想法的朋友不在少數,我想這主要還是因為他們對自己的工作缺少了解導致。如果有可能,我們可以問問自己這樣幾個問題:
(1)我真的搞懂設備的開發(fā)驅動流程了嗎?我是否可以從0開始,編寫一個獨立的驅動代碼呢?
(2)我真的了解設備的初始化、關閉、運行的流程嗎?
(3)當前的設備驅動流程是否合理,有沒有可以改進的地方?
(4)對于內核開發(fā)中涉及的api調用,我自己是否真正了解、是否明白它們在使用上有什么區(qū)別?
(5)如果我要驅動的設備只是在一個前后臺系統(tǒng)中運行,在沒有框架幫助的情況下,我是否有信心把它啟動和運行起來?
當然,上面的內容只是我個人的想法,也不一定都正確。但是,知其然,更要知其所以然,熟悉了當前開發(fā)流程的優(yōu)缺點才能真正掌握和了解驅動開發(fā)的本質。這聽上去有些玄乎,其實也很簡單,就是要有一種刨根問底、不斷改進的精神,這樣才能做好自己的工作。因為我們是在pc linux上學習驅動的,因此暫時沒有真實的外接設備可以使用,但是這絲毫不影響我們學習的熱情。通過定時器、進程,我們可以仿真出真實設備的各種需求,所以對于系統(tǒng)來說,它是無所謂真設備、假設備的,基本的處理流程對它來說都是一樣的。只要大家一步一步做下去,肯定可以了解linux驅動設備的開發(fā)工程的。
下面,為了說明問題,我們可以編寫一段簡單的char設備驅動代碼,文件名為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中的函數是模塊加載時處理的函數,而模塊卸載的函數則是在module_exit中。每一個設備都要對應一個基本的設備數據,當然為了使得這個設備注冊在整個系統(tǒng)當中,我們還需要分配一個設備節(jié)點,alloc_chrdev_region就完成這樣一個功能。等到cdev_add的時候,整個設備注冊的過程就全部完成了,就是這么簡單。當然為了編寫這個文件,我們還需要編寫一個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
這個Makefile文件和我們之前編寫的makefile基本上沒有區(qū)別,唯一的區(qū)別就是文件名稱改成了char.o,僅此而已。為了編寫模塊,我們直接輸入make即可。這時候,char.ko文件就可以生成了。然后,模塊需要被注冊在系統(tǒng)當中,insmod char.ko是少不了的。如果此時,我們還不確信是否模塊已經加入到系統(tǒng)當中,完全可以通過輸入lsmod | grep char進行查找和驗證。為了創(chuàng)建設備節(jié)點,我們需要知道設備為我們創(chuàng)建的major、minor數值是多少,所以dmesg | tail 查找一下數值。在我hp的機器上,這兩個數值分別是249和0,所以下面可以利用它們直接創(chuàng)建設備節(jié)點了,輸入mknod /dev/chr_dev c 249 0即可,此時可以輸入ls /dev/chr_dev驗證一下。那么,按照這種方法,真的可以訪問這個虛擬設備了嗎,我們可以編寫一段簡單的代碼驗證一下,
#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;
}
代碼的內容非常簡單,就是利用CHAR_DEV_NAME直接打開設備,讀寫設備。當然。首先還是需要對這個文件進行編譯,文件名為test.c,輸入gcc test.c -o test,其次就是運行這個文件,直接輸入./test即可。如果沒有問題的話,那么說明我們的代碼是ok的,但是我們還是沒有看到任何內容。沒關系,我們還是通過dmesg這個命令查看內核中是否存在相關的打印內容,直接輸入dmesg | tail即可。此時如果沒有意外的話,我們就可以看到之前在chr_open和chr_read中留下的printk打印,這說明我們的代碼完全是ok的。
上面的代碼只是一段小例子,真實的內容要比這復雜一下。不過既然我們都已經入門了,那么后面的內容其實也沒有什么好怕的了。最后有兩個事情補充一下:(1)如果大家在創(chuàng)建節(jié)點后想刪除設備節(jié)點,直接rm -rf /dev/chr_dev即可;(2)上面這段代碼的原型來自于《深入linux設備驅動程序內核機制》這本書,稍作修改,如果大家對內核機制的內容感興趣,可以參考這本書的內容。
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
相關文章
Win10 + Ubuntu 16.04雙系統(tǒng)完美安裝教程【詳細】
這篇文章主要介紹了Win10 + Ubuntu 16.04雙系統(tǒng)完美安裝教程,本文圖文并茂給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-10-10
jmeter在linux系統(tǒng)下運行及本地內存調優(yōu)的方法詳解
這篇文章主要介紹了jmeter在linux系統(tǒng)下運行及本地內存調優(yōu)的方法,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07
CentOS7.4開機出現welcome to emergency mode的解決方法
CentOS7.4開機出現welcome to emergency mode,報這個錯誤多數情況下是因為/etc/fstab文件的錯誤。注意一下是不是加載了外部硬盤、存儲器或者是網絡共享空間,在重啟時沒有加載上導致的2018-09-09
Ubuntu環(huán)境編譯安裝PHP和Nginx的方法
這篇文章主要介紹了Ubuntu環(huán)境編譯安裝PHP和Nginx的方法,較為詳細的分析了Ubuntu環(huán)境編譯安裝PHP和Nginx的具體步驟、相關命令與操作技巧,需要的朋友可以參考下2019-08-08

