亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何解析Linux驅動中mmap內存映射

發布時間:2021-10-22 11:45:57 來源:億速云 閱讀:156 作者:柒染 欄目:互聯網科技

今天就跟大家聊聊有關如何解析Linux驅動中mmap內存映射,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

mmap在linux哪里?

如何解析Linux驅動中mmap內存映射

什么是mmap?

上圖說了,mmap是操作這些設備的一種方法,所謂操作設備,比如IO端口(點亮一個LED)、LCD控制器、磁盤控制器,實際上就是往設備的物理地址讀寫數據。

但是,由于應用程序不能直接操作設備硬件地址,所以操作系統提供了這樣的一種機制——內存映射,把設備地址映射到進程虛擬地址,mmap就是實現內存映射的接口。

操作設備還有很多方法,如ioctl、ioremap

mmap的好處是,mmap把設備內存映射到虛擬內存,則用戶操作虛擬內存相當于直接操作設備了,省去了用戶空間到內核空間的復制過程,相對IO操作來說,增加了數據的吞吐量。

什么是內存映射?

既然mmap是實現內存映射的接口,那么內存映射是什么呢?看下圖

如何解析Linux驅動中mmap內存映射

驅動程序運行在內核空間,所以驅動程序是面向所有進程的。

用戶空間切換到內核空間有兩種方法:

(1)系統調用,即軟中斷

(2)硬件中斷

虛擬地址空間里面是什么?

了解了什么是虛擬地址空間,那么虛擬地址空間里面裝的是什么?看下圖

如何解析Linux驅動中mmap內存映射

現在已經知道了內存映射是把設備地址映射到進程空間地址(注意:并不是所有內存映射都是映射到進程地址空間的,ioremap是映射到內核虛擬空間的,mmap是映射到進程虛擬地址的),實質上是分配了一個vm_area_struct結構體加入到進程的地址空間,也就是說,把設備地址映射到這個結構體,映射過程就是驅動程序要做的事了。

內存映射的實現

以字符設備驅動為例,一般對字符設備的操作都如下框圖

如何解析Linux驅動中mmap內存映射

以下是mmap_driver.c的源代碼

[cpp] view plain copy

  1. //所有的模塊代碼都包含下面兩個頭文件  

  2. #include <linux/module.h>  

  3. #include <linux/init.h>  

  4.   

  5. #include <linux/types.h> //定義dev_t類型  

  6. #include <linux/cdev.h> //定義struct cdev結構體及相關操作  

  7. #include <linux/slab.h> //定義kmalloc接口  

  8. #include <asm/io.h>//定義virt_to_phys接口  

  9. #include <linux/mm.h>//remap_pfn_range  

  10. #include <linux/fs.h>  

  11.   

  12. #define MAJOR_NUM 990  

  13. #define MM_SIZE 4096  

  14.   

  15. static char driver_name[] = "mmap_driver1";//驅動模塊名字  

  16. static int dev_major = MAJOR_NUM;  

  17. static int dev_minor = 0;  

  18. char *buf = NULL;  

  19. struct cdev *cdev = NULL;  

  20.   

  21. static int device_open(struct inode *inode, struct file *file)  

  22. {  

  23.     printk(KERN_ALERT"device open\n");  

  24.     buf = (char *)kmalloc(MM_SIZE, GFP_KERNEL);//內核申請內存只能按頁申請,申請該內存以便后面把它當作虛擬設備  

  25.     return 0;  

  26. }  

  27.   

  28. static int device_close(struct inode *indoe, struct file *file)  

  29. {  

  30.     printk("device close\n");  

  31.     if(buf)  

  32.     {  

  33.         kfree(buf);  

  34.     }  

  35.     return 0;  

  36. }  

  37.   

  38. static int device_mmap(struct file *file, struct vm_area_struct *vma)  

  39. {  

  40.     vma->vm_flags |= VM_IO;//表示對設備IO空間的映射  

  41.     vma->vm_flags |= VM_RESERVED;//標志該內存區不能被換出,在設備驅動中虛擬頁和物理頁的關系應該是長期的,應該保留起來,不能隨便被別的虛擬頁換出  

  42.     if(remap_pfn_range(vma,//虛擬內存區域,即設備地址將要映射到這里  

  43.                        vma->vm_start,//虛擬空間的起始地址  

  44.                        virt_to_phys(buf)>>PAGE_SHIFT,//與物理內存對應的頁幀號,物理地址右移12位  

  45.                        vma->vm_end - vma->vm_start,//映射區域大小,一般是頁大小的整數倍  

  46.                        vma->vm_page_prot))//保護屬性,  

  47.     {  

  48.         return -EAGAIN;  

  49.     }  

  50.     return 0;  

  51. }  

  52.   

  53. static struct file_operations device_fops =  

  54. {  

  55.     .owner = THIS_MODULE,  

  56.     .open  = device_open,  

  57.     .release = device_close,  

  58.     .mmap = device_mmap,  

  59. };  

  60.   

  61. static int __init char_device_init( void )  

  62. {  

  63.     int result;  

  64.     dev_t dev;//高12位表示主設備號,低20位表示次設備號  

  65.     printk(KERN_ALERT"module init2323\n");  

  66.     printk("dev=%d", dev);  

  67.     dev = MKDEV(dev_major, dev_minor);  

  68.     cdev = cdev_alloc();//為字符設備cdev分配空間  

  69.     printk(KERN_ALERT"module init\n");  

  70.     if(dev_major)  

  71.     {  

  72.         result = register_chrdev_region(dev, 1, driver_name);//靜態分配設備號  

  73.         printk("result = %d\n", result);  

  74.     }  

  75.     else  

  76.     {  

  77.         result = alloc_chrdev_region(&dev, 0, 1, driver_name);//動態分配設備號  

  78.         dev_major = MAJOR(dev);  

  79.     }  

  80.       

  81.     if(result < 0)  

  82.     {  

  83.         printk(KERN_WARNING"Cant't get major %d\n", dev_major);  

  84.         return result;  

  85.     }  

  86.       

  87.       

  88.     cdev_init(cdev, &device_fops);//初始化字符設備cdev  

  89.     cdev->ops = &device_fops;  

  90.     cdev->owner = THIS_MODULE;  

  91.       

  92.     result = cdev_add(cdev, dev, 1);//向內核注冊字符設備  

  93.     printk("dffd = %d\n", result);  

  94.     return 0;  

  95. }  

  96.   

  97. static void __exit char_device_exit( void )  

  98. {  

  99.     printk(KERN_ALERT"module exit\n");  

  100.     cdev_del(cdev);  

  101.     unregister_chrdev_region(MKDEV(dev_major, dev_minor), 1);  

  102. }  

  103.   

  104. module_init(char_device_init);//模塊加載  

  105. module_exit(char_device_exit);//模塊退出  

  106.   

  107. MODULE_LICENSE("GPL");  

  108. MODULE_AUTHOR("ChenShengfa");  

下面是測試代碼test_mmap.c

[cpp] view plain copy

  1. #include <stdio.h>  

  2. #include <fcntl.h>  

  3. #include <sys/mman.h>  

  4. #include <stdlib.h>  

  5. #include <string.h>  

  6.   

  7. int main( void )  

  8. {  

  9.     int fd;  

  10.     char *buffer;  

  11.     char *mapBuf;  

  12.     fd = open("/dev/mmap_driver", O_RDWR);//打開設備文件,內核就能獲取設備文件的索引節點,填充inode結構  

  13.     if(fd<0)  

  14.     {  

  15.         printf("open device is error,fd = %d\n",fd);  

  16.         return -1;  

  17.     }  

  18.     /*測試一:查看內存映射段*/  

  19.     printf("before mmap\n");  

  20.     sleep(15);//睡眠15秒,查看映射前的內存圖cat /proc/pid/maps  

  21.     buffer = (char *)malloc(1024);  

  22.     memset(buffer, 0, 1024);  

  23.     mapBuf = mmap(NULL, 1024, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);//內存映射,會調用驅動的mmap函數  

  24.     printf("after mmap\n");  

  25.     sleep(15);//睡眠15秒,在命令行查看映射后的內存圖,如果多出了映射段,說明映射成功  

  26.       

  27.     /*測試二:往映射段讀寫數據,看是否成功*/  

  28.     strcpy(mapBuf, "Driver Test");//向映射段寫數據  

  29.     memset(buffer, 0, 1024);  

  30.     strcpy(buffer, mapBuf);//從映射段讀取數據  

  31.     printf("buf = %s\n", buffer);//如果讀取出來的數據和寫入的數據一致,說明映射段的確成功了  

  32.       

  33.       

  34.     munmap(mapBuf, 1024);//去除映射  

  35.     free(buffer);  

  36.     close(fd);//關閉文件,最終調用驅動的close  

  37.     return 0;  

  38. }  

下面是makefile文件

[plain] view plain copy

  1. ifneq ($(KERNELRELEASE),)  

  2.   

  3. obj-m := mmap_driver.o  

  4.   

  5. else  

  6. KDIR := /lib/modules/3.2.0-52-generic/build  

  7.   

  8. all:  

  9.     make -C $(KDIR) M=$(PWD) modules  

  10. clean:  

  11.     rm -f *.ko *.o *.mod.o *.mod.c *~ *.symvers *.order  

  12.   

  13. endif  

下面命令演示一下驅動程序的編譯、安裝、測試過程(注:其他用戶在mknod之后還需要chmod改變權限)

# make    //編譯驅動

# insmod mmap_driver.ko    //安裝驅動

# mknod /dev/mmap_driver c 999 0    //創建設備文件

# gcc test_mmap.c -o test.o    //編譯應用程序

# ./test.o    //運行應用程序來測試驅動程序


拓展:

關于這個過程,涉及一些術語

(1)設備文件:linux中對硬件虛擬成設備文件,對普通文件的各種操作均適用于設備文件

(2)索引節點:linux使用索引節點來記錄文件信息(如文件長度、創建修改時間),它存儲在磁盤中,讀入內存后就是一個inode結構體,文件系統維護了一個索引節點的數組,每個元素都和文件或者目錄一一對應。

(3)主設備號:如上面的999,表示設備的類型,比如該設備是lcd還是usb等

(4)次設備號:如上面的0,表示該類設備上的不同設備

(5)文件(普通文件或設備文件)的三個結構

        ①文件操作:struct file_operations

        ②文件對象:struct file

        ③文件索引節點:struct inode

關于驅動程序中內存映射的實現,先了解一下open和close的流程

(1)設備驅動open流程

如何解析Linux驅動中mmap內存映射

(3)設備驅動mmap流程

由open和close得知,同理,應用程序調用mmap最終也會調用到驅動程序中mmap方法

①應用程序test.mmap.c中mmap函數

void* mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

addr:映射后虛擬地址的起始地址,通常為NULL,內核自動分配

length:映射區的大小

prot:頁面訪問權限(PROT_READ、PROT_WRITE、PROT_EXEC、PROT_NONE)

flags:參考網絡資料

fd:文件描述符

offset:文件映射開始偏移量

②驅動程序的mmap_driver.c中mmap函數

上面說了,mmap的主要工作是把設備地址映射到進程虛擬地址,也即是一個vm_area_struct的結構體,這里說的映射,是一個很懸的東西,那它在程序中的表現是什么呢?——頁表,沒錯,就是頁表,映射就是要建立頁表。進程地址空間就可以通過頁表(軟件)和MMU(硬件)映射到設備地址上了

如何解析Linux驅動中mmap內存映射

virt_to_phys(buf),buf是在open時申請的地址,這里使用virt_to_phys把buf轉換成物理地址,是模擬了一個硬件設備,即把虛擬設備映射到虛擬地址,在實際中可以直接使用物理地址。

總結

①從以上看到,內核各個模塊錯綜復雜、相互交叉

②單純一個小小驅動模塊,就涉及了進程管理(進程地址空間)、內存管理(頁表與頁幀映射)、虛擬文件系統(structfile、structinode)

③并不是所有設備驅動都可以使用mmap來映射,比如像串口和其他面向流的設備,并且必須按照頁大小進行映射。

看完上述內容,你們對如何解析Linux驅動中mmap內存映射有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

达拉特旗| 茌平县| 城步| 儋州市| 西充县| 紫阳县| 中西区| 如东县| 池州市| 精河县| 陆良县| 平度市| 兴和县| 湛江市| 涞水县| 定安县| 都昌县| 博野县| 含山县| 新龙县| 佛教| 金门县| 个旧市| 锡林郭勒盟| 勃利县| 浦县| 甘肃省| 鹤岗市| 肥乡县| 亳州市| 讷河市| 建昌县| 建宁县| 屯留县| 黄陵县| 阿克苏市| 永州市| 大足县| 临沭县| 运城市| 平果县|