自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Linux驅(qū)動實(shí)踐:一步一步編寫字符設(shè)備驅(qū)動程序

系統(tǒng) Linux
字符設(shè)備的驅(qū)動程序,有兩套不同的API函數(shù),并且在文中詳細(xì)演示了利用舊的API函數(shù)來編寫驅(qū)動程序。這篇文章,我們繼續(xù)這個話題,實(shí)際演示一下:字符設(shè)備驅(qū)動程序的另一套API函數(shù)的使用方法。

[[436073]]

別人的經(jīng)驗(yàn),我們的階梯!

大家好,我是道哥,今天我們繼續(xù)討論: Linux 中字符設(shè)備的驅(qū)動程序。

在上一篇文章中Linux驅(qū)動實(shí)踐:你知道【字符設(shè)備驅(qū)動程序】的兩種寫法嗎?我們說過:字符設(shè)備的驅(qū)動程序,有兩套不同的API函數(shù),并且在文中詳細(xì)演示了利用舊的API函數(shù)來編寫驅(qū)動程序。

這篇文章,我們繼續(xù)這個話題,實(shí)際演示一下:字符設(shè)備驅(qū)動程序的另一套API函數(shù)的使用方法。

API 函數(shù)

這里主要關(guān)注下面這 3 個函數(shù):

  1. // 靜態(tài)注冊設(shè)備 
  2. int register_chrdev_region(dev_t from, unsigned count, const char *name); 
  3.  
  4. // 動態(tài)注冊設(shè)備 
  5. int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name); 
  6.  
  7. // 卸載設(shè)備 
  8. void unregister_chrdev_region(dev_t from, unsigned count); 

關(guān)于靜態(tài)和動態(tài)注冊,主要的區(qū)別就在于:主設(shè)備號由誰來主導(dǎo)分配!

靜態(tài)注冊:由我們的驅(qū)動程序來指定主設(shè)備號,即參數(shù)1:from;

動態(tài)注冊:由操作系統(tǒng)來分配,驅(qū)動程序提供一個變量來接收該設(shè)備號,即參數(shù)1: dev 指針;

另外,在Linux 2.6后期的內(nèi)核版本中,引入了 cdev 結(jié)構(gòu)來描述一個字符設(shè)備,它的結(jié)構(gòu)體成員是:

  1. struct cdev { 
  2.     struct kobject kobj;    // 內(nèi)嵌的kobject對象 
  3.     struct module *owner;   // 所屬模塊 
  4.     const struct file_operations *ops;//文件操作結(jié)構(gòu)體 
  5.     struct list_head list;  // 鏈表句柄 
  6.     dev_t dev;              // 設(shè)備號 
  7.     unsigned int count
  8. }; 

與這個結(jié)構(gòu)體相關(guān)的處理函數(shù)有:

  • void cdev_init(struct cdev *,struct file_operations *);
  • 初始化 cdev 的成員,主要是設(shè)置 file_operations。
  • strcut cdev *cdev_alloc(void);
  • 動態(tài)申請 cdev 內(nèi)存。
  • void cdev_put(strcut cdev *p);
  • 與 count 計數(shù)相關(guān)的操作。
  • int cdev_add(struct cdev *,dev_t ,unsigned );
  • 向系統(tǒng)中添加一個 cdev,注冊字符設(shè)備,需要在驅(qū)動被加載的時候調(diào)用。
  • void cdev_del(struct cdev *);
  • 從系統(tǒng)中刪除一個 cdev,注銷字符設(shè)備,需要在驅(qū)動被卸載的時候調(diào)用。
  • 后面在代碼演示的時候,可以看到cdev結(jié)構(gòu)是如何被使用的。

編寫驅(qū)動

按照慣例,我們?nèi)匀话凑詹襟E,來討論如何利用上述的APIs,來手寫一個字符設(shè)備的驅(qū)動程序。

以下所有操作的工作目錄,都是與上一篇文章相同的,即:~/tmp/linux-4.15/drivers/。

創(chuàng)建驅(qū)動目錄和驅(qū)動程序

  1. $ cd linux-4.15/drivers/ 
  2. $ mkdir my_driver2 
  3. $ cd my_driver2 
  4. $ touch driver2.c 

driver2.c 文件的內(nèi)容如下(不需要手敲,文末有代碼下載鏈接):

  1. #include <linux/module.h> 
  2. #include <linux/kernel.h> 
  3. #include <linux/ctype.h> 
  4. #include <linux/device.h> 
  5. #include <linux/cdev.h> 
  6.  
  7. static struct cdev my_cdev; 
  8. static dev_t dev_no; 
  9.  
  10. int driver2_open(struct inode *inode, struct file *file) 
  11.     printk("driver2_open is called. \n"); 
  12.     return 0; 
  13.  
  14. ssize_t driver2_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) 
  15.     printk("driver2_read is called. \n"); 
  16.     return 0; 
  17.  
  18. ssize_t driver2_write (struct file *file, const char __user *buf, size_t size, loff_t *ppos) 
  19.     printk("driver2_write is called. \n"); 
  20.     return 0; 
  21.   
  22. static const struct file_operations driver2_ops={ 
  23.     .owner = THIS_MODULE, 
  24.     .open  = driver2_open, 
  25.     .read  = driver2_read, 
  26.     .write = driver2_write, 
  27. }; 
  28.   
  29. static int __init driver2_init(void) 
  30.     printk("driver2_init is called. \n"); 
  31.  
  32.     // 初始化cdev結(jié)構(gòu) 
  33.     cdev_init(&my_cdev, &driver2_ops); 
  34.  
  35.     // 注冊字符設(shè)備 
  36.     alloc_chrdev_region(&dev_no, 0, 2, "driver2"); 
  37.     cdev_add(&my_cdev, dev_no, 2); 
  38.  
  39.     return 0; 
  40.   
  41. static void __exit driver2_exit(void) 
  42.     printk("driver2_exit is called. \n"); 
  43.  
  44.     // 注銷設(shè)備 
  45.     cdev_del(&my_cdev);  
  46.  
  47.     // 注銷設(shè)備號 
  48.     unregister_chrdev_region(dev_no, 2); 
  49.   
  50. MODULE_LICENSE("GPL"); 
  51. module_init(driver2_init); 
  52. module_exit(driver2_exit); 

這里看一下加載驅(qū)動模塊時調(diào)用的 driver2_init( ) 函數(shù),其中的 cdev_init 用來把cdev結(jié)構(gòu)體與 file_operations 發(fā)生關(guān)聯(lián)。

在調(diào)用 alloc_chrdev_region( ) 時,操作系統(tǒng)分配了主設(shè)備號,并且保存在 dev_no 變量中,然后 cdev_add() 再把設(shè)備號與cdev結(jié)構(gòu)體進(jìn)行關(guān)聯(lián)。

創(chuàng)建 Makefile 文件

  1. $ touch Makefile 

內(nèi)容如下:

  1. ifneq ($(KERNELRELEASE),) 
  2.     obj-m := driver2.o 
  3. else 
  4.     KERNELDIR ?= /lib/modules/$(shell uname -r)/build 
  5.     PWD := $(shell pwd) 
  6. default
  7.     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 
  8. clean: 
  9.     $(MAKE) -C $(KERNEL_PATH) M=$(PWD) clean 
  10. endif 

編譯驅(qū)動模塊

  1. $ make 

得到驅(qū)動程序: driver2.ko 。

加載驅(qū)動模塊

在加載驅(qū)動模塊之前,先來檢查一下系統(tǒng)中,幾個與驅(qū)動設(shè)備相關(guān)的地方。

先看一下 /dev 目錄下,目前還沒有我們的設(shè)備節(jié)點(diǎn)( /dev/driver2 )。

  1. $ ll /dev/driver2 
  2. ls: cannot access '/dev/driver2'No such file or directory 

再來查看一下 /proc/devices 目錄下,也沒有 driver2 設(shè)備的設(shè)備號。

  1. $ cat /proc/devices 

 

/proc/devices 文件: 列出字符和塊設(shè)備的主設(shè)備號,以及分配到這些設(shè)備號的設(shè)備名稱。

為了方便查看打印信息,把dmesg輸出信息清理一下:

  1. $ sudo dmesg -c 

執(zhí)行如下指令,加載驅(qū)動模塊:

  1. $ sudo insmod driver2.ko 

當(dāng)驅(qū)動程序被加載的時候,通過 module_init( ) 注冊的函數(shù) driver2_init() 將會被執(zhí)行,那么其中的打印信息就會輸出。

還是通過 dmesg 指令來查看驅(qū)動模塊的打印信息:

  1. $ dmesg 

此時,驅(qū)動模塊已經(jīng)被加載了!

來查看一下 /proc/devices 目錄下顯示的設(shè)備號:

  1. $ cat /proc/devices 

設(shè)備已經(jīng)注冊了,主設(shè)備號是: 244 。

但是,此時在/dev目錄下,還沒有我們需要的設(shè)備節(jié)點(diǎn)。

在上一篇文章中介紹過,還可以利用 Linux 用戶態(tài)的 udev 服務(wù)來自動創(chuàng)建設(shè)備節(jié)點(diǎn)。

現(xiàn)在,我們手動創(chuàng)建設(shè)備節(jié)點(diǎn):

  1. $ sudo mknod -m 660 /dev/driver2 c 244 0 

主設(shè)備號 244 是從 /proc/devices 查到的。

檢查一下是否創(chuàng)建成功:

  1. $ ll /dev/driver2 

現(xiàn)在,設(shè)備的驅(qū)動程序已經(jīng)加載了,設(shè)備節(jié)點(diǎn)也被創(chuàng)建好了,應(yīng)用程序就可以來操作(讀、寫)這個設(shè)備了。

應(yīng)用程序

應(yīng)用程序仍然放在 ~/tmp/App/ 目錄下。

  1. $ mkdir ~/tmp/App/app_driver2 
  2. $ cd ~/tmp/App/app_driver2 
  3. $ touch app_driver2.c 

文件內(nèi)容如下:

  1. #include <stdio.h> 
  2. #include <unistd.h> 
  3. #include <fcntl.h> 
  4.  
  5.  
  6. int main(void) 
  7.     int ret; 
  8.     int read_data[4] = { 0 }; 
  9.     int write_data[4] = {1, 2, 3, 4}; 
  10.     int fd = open("/dev/driver2", O_RDWR); 
  11.     if (-1 != fd) 
  12.     { 
  13.         ret = read(fd, read_data, 4); 
  14.         printf("read ret = %d \n", ret); 
  15.  
  16.         ret = write(fd, write_data, 4); 
  17.         printf("write ret = %d \n", ret); 
  18.     } 
  19.     else 
  20.     { 
  21.         printf("open /dev/driver2 failed! \n"); 
  22.     } 
  23.  
  24.     return 0; 

接下來就是編譯和測試了:

  1. $ gcc app_driver2.c -o app_driver2 
  2. $  
  3. $ sudo ./app_driver2  
  4. [sudo] password for xxx: <輸入用戶密碼> 
  5. read ret = 0  
  6. write ret = 0 

從返回值來看,成功打開了設(shè)備,并且調(diào)用讀函數(shù)、寫函數(shù)都成功了!

繼續(xù)用dmesg命令查看一下:

卸載驅(qū)動模塊

卸載指令:

  1. $ sudo rmmod driver2 

此時,/proc/devices 下主設(shè)備號 244 的 driver2 已經(jīng)不存在了。

再來看一下 dmesg的打印信息:

可以看到:驅(qū)動程序中的 driver2_exit( ) 被調(diào)用執(zhí)行了!

小結(jié)

以上就是利用“新的” API 函數(shù),來編寫字符設(shè)備的驅(qū)動程序。

代碼結(jié)構(gòu)還是非常清晰的,這得益于Linux良好的驅(qū)動程序架構(gòu)設(shè)計!這也是每一名架構(gòu)師需要學(xué)習(xí)、努力模仿的地方。

本文轉(zhuǎn)載自微信公眾號「IOT物聯(lián)網(wǎng)小鎮(zhèn)」

 

責(zé)任編輯:姜華 來源: IOT物聯(lián)網(wǎng)小鎮(zhèn)
相關(guān)推薦

2021-11-12 11:28:01

Linux 內(nèi)核驅(qū)動Linux 系統(tǒng)

2018-12-24 10:04:06

Docker存儲驅(qū)動

2019-03-05 14:09:27

Docker存儲容器

2019-07-09 15:23:22

Docker存儲驅(qū)動

2021-11-29 07:55:45

Linux GPIO Linux 系統(tǒng)

2022-08-29 15:19:09

CSS煙花動畫

2009-07-06 19:29:37

云計算私有云服務(wù)器虛擬化

2011-01-10 18:21:38

linux編寫程序

2010-07-12 17:10:23

Android應(yīng)用程序

2011-06-07 16:03:48

匿名SQL Server

2018-03-07 15:24:41

PythonMySQL

2013-03-18 16:09:27

JavaEEOpenfire

2012-03-22 10:33:33

思杰XenDesktop

2009-12-18 16:27:43

Cisco路由器配置

2022-09-30 15:37:19

Web網(wǎng)站服務(wù)器

2017-09-28 09:40:36

圖像分類準(zhǔn)確率

2021-09-15 16:13:13

人工智能AI深度學(xué)習(xí)

2024-07-22 11:43:28

LVMPnetLab網(wǎng)絡(luò)

2017-08-24 08:31:41

2009-12-17 08:57:28

Windows 7磁盤分區(qū)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號