日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux设备驱动之mmap设备操作

發布時間:2024/9/21 linux 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux设备驱动之mmap设备操作 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1.mmap系統調用

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

功能:負責把文件內容映射到進程的虛擬地址空間,通過對這段內存的讀取和修改來實現對文件的讀取和修改,而不需要再調用read和write;
參數:addr:映射的起始地址,設為NULL由系統指定;
len:映射到內存的文件長度;
prot:期望的內存保護標志,不能與文件的打開模式沖突。PROT_EXEC,PROT_READ,PROT_WRITE等;
flags:指定映射對象的類型,映射選項和映射頁是否可以共享。MAP_SHARED,MAP_PRIVATE等;
fd:由open返回的文件描述符,代表要映射的文件;
offset:開始映射的文件的偏移。
返回值:成功執行時,mmap()返回被映射區的指針。失敗時,mmap()返回MAP_FAILED。

mmap映射圖:


2.解除映射:
 int munmap(void *start, size_t length);?

3.虛擬內存區域:
虛擬內存區域是進程的虛擬地址空間中的一個同質區間,即具有同樣特性的連續地址范圍。一個進程的內存映象由下面幾個部分組成:程序代碼、數據、BSS和棧區域,以及內存映射的區域。
linux內核使用vm_area_struct結構來描述虛擬內存區。其主要成員:

unsigned long vm_start; /* Our start address within vm_mm. */ unsigned long vm_end; /* The first byte after our end address within vm_mm. */ unsigned long vm_flags; /* Flags, see mm.h. 該區域的標記。如VM_IO(該VMA標記為內存映射的IO區域,會阻止系統將該區域包含在進程的存放轉存中)和VM_RESERVED(標志內存區域不能被換出)。*/


4.mmap設備操作:
映射一個設備是指把用戶空間的一段地址(虛擬地址區間)關聯到設備內存上,當程序讀寫這段用戶空間的地址時,它實際上是在訪問設備。
mmap方法是file_operations結構的成員,在mmap系統調用的發出時被調用。在此之前,內核已經完成了很多工作。
mmap設備方法所需要做的就是建立虛擬地址到物理地址的頁表(虛擬地址和設備的物理地址的關聯通過頁表)。

static int mmap(struct file *file, struct vm_area_struct *vma);


mmap如何完成頁表的建立?(兩種方法)
(1)使用remap_pfn_range一次建立所有頁表。

int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t prot); /** * remap_pfn_range - remap kernel memory to userspace * @vma: user vma to map to:內核找到的虛擬地址區間 * @addr: target user address to start at:要關聯的虛擬地址 * @pfn: physical address of kernel memory:要關聯的設備的物理地址,也即要映射的物理地址所在的物理幀號,可將物理地址>>PAGE_SHIFT * @size: size of map area * @prot: page protection flags for this mapping * * Note: this is only safe if the mm semaphore is held when called. */


(2)使用nopage VMA方法每次建立一個頁表;

?

5.源碼分析:

(1)memdev.h

#ifndef _MEMDEV_H_ #define _MEMDEV_H_#ifndef MEMDEV_MAJOR #define MEMDEV_MAJOR 0 /*預設的mem的主設備號*/ #endif#ifndef MEMDEV_NR_DEVS #define MEMDEV_NR_DEVS 2 /*設備數*/ #endif#ifndef MEMDEV_SIZE #define MEMDEV_SIZE 4096 #endif /*mem設備描述結構體*/ struct mem_dev { char *data; unsigned long size; };#endif /* _MEMDEV_H_ */


(2)memdev.c

#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h>#include <linux/kernel.h> #include "memdev.h" static int mem_major = MEMDEV_MAJOR;module_param(mem_major, int, S_IRUGO);struct mem_dev *mem_devp; /*設備結構體指針*/struct cdev cdev; /*文件打開函數*/ int mem_open(struct inode *inode, struct file *filp) {struct mem_dev *dev;/*獲取次設備號*/int num = MINOR(inode->i_rdev);if (num >= MEMDEV_NR_DEVS) return -ENODEV;dev = &mem_devp[num];/*將設備描述結構指針賦值給文件私有數據指針*/filp->private_data = dev;return 0; }/*文件釋放函數*/ int mem_release(struct inode *inode, struct file *filp) {return 0; } static int memdev_mmap(struct file*filp, struct vm_area_struct *vma) {struct mem_dev *dev = filp->private_data; /*獲得設備結構體指針*/vma->vm_flags |= VM_IO;vma->vm_flags |= VM_RESERVED;if (remap_pfn_range(vma,vma->vm_start,virt_to_phys(dev->data)>>PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot))return -EAGAIN;return 0; }/*文件操作結構體*/ static const struct file_operations mem_fops = {.owner = THIS_MODULE,.open = mem_open,.release = mem_release,.mmap = memdev_mmap, };/*設備驅動模塊加載函數*/ static int memdev_init(void) {int result;int i;dev_t devno = MKDEV(mem_major, 0);/* 靜態申請設備號*/if (mem_major)result = register_chrdev_region(devno, 2, "memdev");else /* 動態分配設備號 */{result = alloc_chrdev_region(&devno, 0, 2, "memdev");mem_major = MAJOR(devno);} if (result < 0)return result;/*初始化cdev結構*/cdev_init(&cdev, &mem_fops);cdev.owner = THIS_MODULE;cdev.ops = &mem_fops;/* 注冊字符設備 */cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);/* 為設備描述結構分配內存*/mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);if (!mem_devp) /*申請失敗*/{result = - ENOMEM;goto fail_malloc;}memset(mem_devp, 0, sizeof(struct mem_dev));/*為設備分配內存*/for (i=0; i < MEMDEV_NR_DEVS; i++) {mem_devp[i].size = MEMDEV_SIZE;mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);memset(mem_devp[i].data, 0, MEMDEV_SIZE);}return 0;fail_malloc: unregister_chrdev_region(devno, 1);return result; }/*模塊卸載函數*/ static void memdev_exit(void) {cdev_del(&cdev); /*注銷設備*/kfree(mem_devp); /*釋放設備結構體內存*/unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*釋放設備號*/ }MODULE_AUTHOR("David Xie"); MODULE_LICENSE("GPL");module_init(memdev_init); module_exit(memdev_exit);


(3)app-mmap.c

#include <stdio.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> #include<sys/mman.h>int main() {int fd;char *start;//char buf[100]; char *buf;/*打開文件*/fd = open("/dev/memdev0",O_RDWR);buf = (char *)malloc(100);memset(buf, 0, 100);start=mmap(NULL,100,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);/* 讀出數據 */strcpy(buf,start);sleep (1);printf("buf 1 = %s\n",buf); /* 寫入數據 */strcpy(start,"Buf Is Not Null!");memset(buf, 0, 100);strcpy(buf,start);sleep (1);printf("buf 2 = %s\n",buf);munmap(start,100); /*解除映射*/free(buf);close(fd); return 0; }


測試步驟:

(1)編譯安裝內核模塊:insmod memdev.ko

(2)查看設備名、主設備號:cat /proc/devices

(3)手工創建設備節點:mknod ?/dev/memdev0 ?c ?*** ?0

  查看設備文件是否存在:ls -l /dev/* | grep memdev

(4)編譯下載運行應用程序:./app-mmap

  結果:buf 1 =?

     buf 2 =?Buf Is Not Null!


  總結:mmap設備方法實現將用戶空間的一段內存關聯到設備內存上,對用戶空間的讀寫就相當于對字符設備的讀寫;不是所有的設備都能進行mmap抽象,比如像串口和其他面向流的設備就不能做mmap抽象。

總結

以上是生活随笔為你收集整理的Linux设备驱动之mmap设备操作的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。