linux 之dma_buf (4)- mmap

一、前言

前面幾篇都是在 kernel space 對 dma-buf 進行訪問的,本篇我們將一起來學習,如何在 user space 訪問 dma-buf。當然,user space 訪問 dma-buf 也屬于 CPU Access 的一種。

二、mmap

為了方便應用程序能直接在用戶空間讀寫 dma-buf 的內存,dma_buf_ops?為我們提供了一個?mmap?回調接口,可以把 dma-buf 的物理內存直接映射到用戶空間,這樣應用程序就可以像訪問普通文件那樣訪問 dma-buf 的物理內存了。

在linux??設備驅動中,大多數驅動的?mmap?操作接口都是通過調用?remap_pfn_range()?函數來實現的,dma-buf 也不例外。

除了?dma_buf_ops?提供的?mmap?回調接口外,dma-buf 還為我們提供了?dma_buf_mmap()?內核 API,使得我們可以在其他設備驅動中就地取材,直接引用 dma-buf 的?mmap?實現,以此來間接的實現設備驅動的?mmap?文件操作接口.

?

接下來,我們將通過兩個示例來演示如何在 Userspace 訪問 dma-buf 的物理內存。

  • 示例一:直接使用 dma-buf 的 fd 做?mmap()?操作
  • 示例二:使用 exporter 的 fd 做?mmap()?操作

三、直接使用 dma-buf 的 fd 做?mmap()?操作

本示例主要演示如何在驅動層實現 dma-buf 的?mmap?回調接口,以及如何在用戶空間直接使用 dma-buf 的 fd 進行?mmap()?操作。

export_test.c

#include <linux/dma-buf.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>struct dma_buf *dmabuf_export;
EXPORT_SYMBOL(dmabuf_export);static int exporter_attach(struct dma_buf* dmabuf,  struct dma_buf_attachment *attachment)
{pr_info("dmanbuf attach device :%s \n",dev_name(attachment->dev));return 0;}static void exporter_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attachment)
{pr_info("dmabuf detach device :%s \n",dev_name(attachment->dev));
}static struct sg_table *exporter_map_dma_buf(struct dma_buf_attachment *attachment,enum dma_data_direction dir)
{
//      void *vaddr = attachment->dmabuf->priv;struct sg_table *table;int ret;table = kmalloc(sizeof(struct sg_table),GFP_KERNEL);ret = sg_alloc_table(table, 1, GFP_KERNEL);if(ret)pr_info("sg_alloc_table err\n");sg_dma_len(table->sgl) = PAGE_SIZE;pr_info("sg_dma_len: %d\n ", sg_dma_len(table->sgl));//      sg_dma_address(table->sgl) = dma_map_single(NULL, vaddr, PAGE_SIZE,dir);
//      pr_info("sg_dma_address: 0x%llx\n",(unsigned long long)sg_dma_address(table->sgl));return table;
}static void exporter_unmap_dma_buf(struct dma_buf_attachment *attachment,struct sg_table *table,enum dma_data_direction dir)
{dma_unmap_single(NULL, sg_dma_address(table->sgl), PAGE_SIZE, dir);sg_free_table(table);kfree(table);
}static void exporter_release(struct dma_buf *dmabuf)
{return kfree(dmabuf->priv);
}/*static void *exporter_kmap_atomic(struct dma_buf *dmabuf, unsigned long page_num)
{return NULL;
}static void *exporter_kmap(struct dma_buf *dmabuf, unsigned long page_num)
{return NULL;
}*/
static void* exporter_vmap(struct dma_buf *dmabuf)
{return dmabuf->priv;}
static int exporter_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
{void *vaddr = dmabuf->priv;struct page * page_ptr = virt_to_page(vaddr);return remap_pfn_range(vma,vma->vm_start, page_to_pfn(page_ptr),PAGE_SIZE, vma->vm_page_prot);
}static const struct dma_buf_ops exp_dmabuf_ops = {.attach = exporter_attach,.detach = exporter_detach,.map_dma_buf = exporter_map_dma_buf,.unmap_dma_buf = exporter_unmap_dma_buf,.release = exporter_release,
//      .map_atomic = exporter_kmap_atomic,
//      .map = exporter_kmap,.vmap = exporter_vmap,.mmap = exporter_mmap,
};static struct dma_buf *exporter_alloc_page(void)
{DEFINE_DMA_BUF_EXPORT_INFO(exp_info);struct dma_buf *dmabuf;void *vaddr;vaddr = kzalloc(PAGE_SIZE,GFP_KERNEL);exp_info.ops = &exp_dmabuf_ops;exp_info.size = PAGE_SIZE;exp_info.flags = O_CLOEXEC;exp_info.priv = vaddr;dmabuf= dma_buf_export(&exp_info);if(dmabuf == NULL)printk(KERN_INFO"DMA buf export error\n");sprintf(vaddr, "hello world");return dmabuf;
}static long exporter_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{int fd = dma_buf_fd(dmabuf_export, O_CLOEXEC);if(unlikely(copy_to_user((void __user*)arg, &fd,sizeof(fd)))){return -EFAULT;}return 0;}
static struct file_operations  exporter_fops = {.owner = THIS_MODULE,.unlocked_ioctl = exporter_ioctl,};static struct miscdevice mdev ={.minor = MISC_DYNAMIC_MINOR,.name = "exporter",.fops = &exporter_fops,};static int __init exporter_init(void)
{dmabuf_export = exporter_alloc_page();return misc_register(&mdev);}static void __exit exporter_exit(void)
{misc_deregister(&mdev);}
module_init(exporter_init);
module_exit(exporter_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZWQ");
MODULE_DESCRIPTION("zwq dma used buffer");

從上面的示例可以看到,除了要實現 dma-buf 的 mmap 回調接口外,我們還引入了 misc driver,目的是想通過 misc driver 的 ioctl 接口將 dma-buf 的 fd 傳遞給上層應用程序,這樣才能實現應用程序直接使用這個 dma-buf fd 做 mmap() 操作。

補充:

?

static int exporter_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
{
? ? ? ? void *vaddr = dmabuf->priv;
? ? ? ? struct page * page_ptr = virt_to_page(vaddr);


? ? ? ? return remap_pfn_range(vma,vma->vm_start, page_to_pfn(page_ptr),
? ? ? ? ? ? ? ? ? ? ? ? PAGE_SIZE, vma->vm_page_prot);
}
?

?上面的虛擬地址 vaddr 如果使用:

remap_pfn_range(vma, vma->vm_start, virt_to_pfn(vaddr), PAGE_SIZE, vma->vm_page_prot);

這個會編譯不過,只能先把vaddr 轉換page ,在page 轉換成頁號

為什么非要通過 ioctl 的方式來傳遞 fd ?這個問題我會在下一篇中詳細討論。

?在?ioctl?接口中,我們使用到了?dma_buf_fd()?函數,該函數用于創建一個新的 fd,并與該 dma-buf 的文件相綁定。關于該函數,我也會在下一篇中做詳細介紹。

userspace 程序

mmap_dmabuf.c

#include <stdio.h>
#include <stddef.h>#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/mman.h>int main(int argc, char *argv[])
{int fd;int dmabuf_fd = 0;fd = open("/dev/exporter", O_RDONLY);ioctl(fd, 0, &dmabuf_fd);close(fd);char *str = mmap(NULL, 4096, PROT_READ, MAP_SHARED, dmabuf_fd, 0);printf("read from dmabuf mmap: %s\n", str);return 0;
}

?編譯運行后,結果如下:

可以看到,userspace 程序通過?mmap()?接口成功的訪問到 dma-buf 的物理內存。

?

四、使用 exporter 的 fd 做?mmap()?操作

本示例主要演示如何使用?dma_buf_mmap()?內核 API,以此來簡化設備驅動的?mmap?文件操作接口的實現。

export_test.c

新增?exporter_misc_mmap()?函數, 具體修改如下:

#include <linux/dma-buf.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>struct dma_buf *dmabuf_export;
EXPORT_SYMBOL(dmabuf_export);static int exporter_attach(struct dma_buf* dmabuf,  struct dma_buf_attachment *attachment)
{pr_info("dmanbuf attach device :%s \n",dev_name(attachment->dev));return 0;}static void exporter_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attachment)
{pr_info("dmabuf detach device :%s \n",dev_name(attachment->dev));
}static struct sg_table *exporter_map_dma_buf(struct dma_buf_attachment *attachment,enum dma_data_direction dir)
{
//      void *vaddr = attachment->dmabuf->priv;struct sg_table *table;int ret;table = kmalloc(sizeof(struct sg_table),GFP_KERNEL);ret = sg_alloc_table(table, 1, GFP_KERNEL);if(ret)pr_info("sg_alloc_table err\n");sg_dma_len(table->sgl) = PAGE_SIZE;pr_info("sg_dma_len: %d\n ", sg_dma_len(table->sgl));//      sg_dma_address(table->sgl) = dma_map_single(NULL, vaddr, PAGE_SIZE,dir);
//      pr_info("sg_dma_address: 0x%llx\n",(unsigned long long)sg_dma_address(table->sgl));return table;
}static void exporter_unmap_dma_buf(struct dma_buf_attachment *attachment,struct sg_table *table,enum dma_data_direction dir)
{dma_unmap_single(NULL, sg_dma_address(table->sgl), PAGE_SIZE, dir);sg_free_table(table);kfree(table);
}static void exporter_release(struct dma_buf *dmabuf)
{return kfree(dmabuf->priv);
}/*static void *exporter_kmap_atomic(struct dma_buf *dmabuf, unsigned long page_num)
{return NULL;
}static void *exporter_kmap(struct dma_buf *dmabuf, unsigned long page_num)
{return NULL;
}*/
static void* exporter_vmap(struct dma_buf *dmabuf)
{return dmabuf->priv;}
static int exporter_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
{void *vaddr = dmabuf->priv;struct page * page_ptr = virt_to_page(vaddr);return remap_pfn_range(vma,vma->vm_start, page_to_pfn(page_ptr),PAGE_SIZE, vma->vm_page_prot);
}static const struct dma_buf_ops exp_dmabuf_ops = {.attach = exporter_attach,.detach = exporter_detach,.map_dma_buf = exporter_map_dma_buf,.unmap_dma_buf = exporter_unmap_dma_buf,.release = exporter_release,
//      .map_atomic = exporter_kmap_atomic,
//      .map = exporter_kmap,.vmap = exporter_vmap,.mmap = exporter_mmap,
};static struct dma_buf *exporter_alloc_page(void)
{DEFINE_DMA_BUF_EXPORT_INFO(exp_info);struct dma_buf *dmabuf;void *vaddr;vaddr = kzalloc(PAGE_SIZE,GFP_KERNEL);exp_info.ops = &exp_dmabuf_ops;exp_info.size = PAGE_SIZE;exp_info.flags = O_CLOEXEC;exp_info.priv = vaddr;dmabuf= dma_buf_export(&exp_info);if(dmabuf == NULL)printk(KERN_INFO"DMA buf export error\n");sprintf(vaddr, "hello world");return dmabuf;
}static long exporter_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{int fd = dma_buf_fd(dmabuf_export, O_CLOEXEC);if(unlikely(copy_to_user((void __user*)arg, &fd,sizeof(fd)))){return -EFAULT;}return 0;}
static int exporter_misc_mmap(struct file *file, struct vm_area_struct *vma)
{return dma_buf_mmap(dmabuf_export, vma, 0);
}static struct file_operations  exporter_fops = {.owner = THIS_MODULE,.unlocked_ioctl = exporter_ioctl,.mmap = exporter_misc_mmap,
};static struct miscdevice mdev ={.minor = MISC_DYNAMIC_MINOR,.name = "exporter",.fops = &exporter_fops,};
static int __init exporter_init(void)
{dmabuf_export = exporter_alloc_page();return misc_register(&mdev);}static void __exit exporter_exit(void)
{misc_deregister(&mdev);}
module_init(exporter_init);
module_exit(exporter_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZWQ");
MODULE_DESCRIPTION("zwq dma used buffer");

與示例一的驅動相比,示例二的驅動可以不再需要把 dma-buf 的 fd 通過 ioctl 傳給上層,而是直接將 dma-buf 的 mmap 回調接口嫁接到 misc driver 的 mmap 文件操作接口上。這樣上層在對 misc device 進行 mmap() 操作時,實際映射的是 dma-buf 的物理內存。

userspace 程序

mmap_dmabuf.c

#include <stdio.h>
#include <stddef.h>#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/mman.h>int main(int argc, char *argv[])
{int fd;
//      int dmabuf_fd = 0;fd = open("/dev/exporter", O_RDONLY);
#if 0ioctl(fd, 0, &dmabuf_fd);close(fd);char *str = mmap(NULL, 4096, PROT_READ, MAP_SHARED, dmabuf_fd, 0);printf("read from dmabuf mmap: %s\n", str);
#endifchar *str = mmap(NULL, 4096, PROT_READ, MAP_SHARED,fd, 0);printf("read from dmabuf mmap: %s\n", str);return 0;
}

與示例一的 userspace 程序相比,示例二不再通過 ioctl() 方式獲取 dma-buf 的 fd,而是直接使用 exporter misc device 的 fd 進行 mmap() 操作,此時執行的則是 misc driver 的 mmap 文件操作接口。當然最終輸出的結果都是一樣的

運行結果:

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/17479.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/17479.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/17479.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

nbcio-vue升級遷移flowable到最新的jeeg-boot-vue3的問題記錄(一)

因為這個項目license問題無法開源&#xff0c;更多技術支持與服務請加入我的知識星球。 1、vue3 jeeg-boot-vue3新版本的流程定義的頁面&#xff0c;刷新出現下面問題&#xff0c;或第一次進去也一樣 看著好像就一個警告的信息&#xff0c;不知道是什么原因引起的&#xff0c;應…

111.二叉樹的最小深度

給定一個二叉樹&#xff0c;找出其最小深度。 最小深度是從根節點到最近葉子節點的最短路徑上的節點數量。 說明: 葉子節點是指沒有子節點的節點。 示例: 給定二叉樹 [3,9,20,null,null,15,7], 返回它的最小深度 2. 思路&#xff1a; 后序遍歷&#xff08;左右中&#xff…

深入理解 Nginx Concat 模塊:示例、安裝和使用方法

Nginx 是一個高性能的開源 Web 服務器,廣泛用于構建可靠的 Web 應用程序和服務。其中的 Concat 模塊為用戶提供了在服務器端快速合并和傳輸多個文件的能力,從而提高了網頁加載速度和性能。在本文中,我們將深入探討 Nginx Concat 模塊的安裝、示例以及使用場景。 什么是 Ngi…

【設計模式深度剖析】【5】【結構型】【橋接模式】| 以電視和遙控器為例加深理解

&#x1f448;?上一篇:組合模式 設計模式-專欄&#x1f448;? 目 錄 橋接模式(Bridge Pattern)定義英文原話是&#xff1a;直譯理解 4個角色UML類圖代碼示例 應用優點缺點使用場景 示例解析&#xff1a;電視和遙控器UML類圖 橋接模式(Bridge Pattern) 定義 英文原話是&am…

band對應頻段列表(2G、4G、5G)

5G BAND對應頻段 n1:2.1G n3:1.8 n5:850 n8:900 n28:700 n41:2.6G n77:3.3G n78:3.5G n79:4.9G n257、258、260&#xff1a;毫米波頻段(26G&#xff0c;28G,39G) 4G BAND對應頻段 Band1:2.1G–上行1920-1980 MHz&#xff0c;下行2110-2170 MHz Band3:1.8G–上行1710-1785 MH…

CC工具箱使用指南:【淹沒區分析(BHM)】

一、簡介 群友定制工具。 這個工具適用面比較小。 工具的應用場景如下&#xff1a; 提供一個淹沒區范圍&#xff0c;類型是面要素。統計這個范圍內的一些線、面要素的面積或長度。 給定的幾個數據有&#xff1a;耕地、永久基本農田、房臺、道路&#xff08;線&#xff09;…

基于Docker搭建屬于你的CC++集成編譯環境

常常&#xff0c;我會幻想著擁有一個隨時可以攜帶、隨時可以使用的開發環境&#xff0c;那該是多么美好的事情。 在工作中&#xff0c;編譯環境的復雜性常常讓我頭疼不已。稍有不慎&#xff0c;刪除了一些關鍵文件&#xff0c;整個編譯鏈就會瞬間崩潰。更糟糕的是&#xff0c;…

【Go語言入門學習筆記】Part6.包和兩個幾乎用不到的小Tip

一、前言 這個文章簡單了寫了一下包、init函數、匿名函數。 二、學習代碼 1.包 package packTestimport "fmt"func init() { //如果主函數引用了這個包&#xff0c;主函數執行的時候會先執行包的initfmt.Println("hello world") }func Add(num1 int, num…

如何保養和維護氣膜體育館—輕空間

隨著經濟的飛速發展&#xff0c;氣膜體育館以其新穎的外觀、優美的造型、節能環保的特點&#xff0c;迅速進入體育市場。然而&#xff0c;對于氣膜體育館的維護和保養是不容忽視的問題&#xff0c;必須引起重視。下面我們將詳細介紹氣膜體育館的維護需要從哪些方面著手。 一、保…

公路行業交通工程乙級資質的動態考核要點

技術人員保持與更新&#xff1a; 核查技術人員的在職狀態、專業資格證書的有效性&#xff0c;以及新增或離職技術人員的情況&#xff0c;確保關鍵崗位人員的穩定性和資質要求的持續達標。評估技術人員的專業發展&#xff0c;包括繼續教育、培訓和參與專業活動的情況&#xff0c…

【電路筆記】-狀態可變濾波器

狀態可變濾波器 文章目錄 狀態可變濾波器1、概述2、**狀態可變濾波器電路**3、狀態可變濾波器示例4、陷波濾波器設計5、總結狀態可變濾波器是一種多反饋濾波器電路,可以從同一單個有源濾波器設計中同時產生所有三種濾波器響應:低通、高通和帶通。 1、概述 狀態可變濾波器使用…

基于Java+SpringBoot+Mybaties-plus+Vue+elememt + uniapp 新聞資訊 的設計與實現

一.項目介紹 本系統分為 后端 和 小程序端 后端&#xff1a;點擊登錄按鈕 設置個人中心、 管理員賬號數據維護、 基礎數據維護、 短視頻信息維護(包括查看短視頻留言、短視頻收藏)、 論壇維護(增刪改查帖子信息&#xff0c;包括查…

Rabbit MQ學習之《基礎概念》

Message Queue 1 什么是MQ MQ(message queue)&#xff0c;本質是個隊列&#xff0c;FIFO 先入先出&#xff0c;只不過隊列中存放的內容是message而已&#xff0c;同時是一種跨進程的通信機制&#xff0c;用于上下游傳遞消息。 在互聯網架構中&#xff0c;MQ 是一種非常常見的…

深入解析力扣166題:分數到小數(模擬長除法與字符串操作詳解及模擬面試問答)

力扣166題&#xff1a;分數到小數 在本篇文章中&#xff0c;我們將詳細解讀力扣第166題“分數到小數”。通過學習本篇文章&#xff0c;讀者將掌握如何使用多種方法來解決這一問題&#xff0c;并了解相關的復雜度分析和模擬面試問答。每種方法都將配以詳細的解釋和ASCII圖解&am…

鋇錸技術BL205模塊在智能制造產線的靈活配置與優化

鋇錸技術的OPC UA耦合器BL205模塊在智能制造產線中的靈活配置與優化是當今工業領域中的一個關鍵議題。隨著工業4.0和數字化轉型的不斷推進&#xff0c;生產線的靈活性和智能化程度成為了企業追求的目標。在這一背景下&#xff0c;BL205模塊以其分布式、可插拔、結構緊湊、可編程…

【Python快速上手(三十三)】- Python operator 模塊

目錄 Python快速上手&#xff08;三十三&#xff09;- Python operator 模塊Python operator 模塊詳解1. 模塊簡介2. 算術運算符函數3. 比較運算符函數4. 位運算符函數5. 序列操作函數6. 方法調用函數7. 操作函數對象8. 實際應用案例9. 小結 Python快速上手&#xff08;三十三&…

Java基礎入門day57

day57 JSP、Servlet&#xff0c;Java bean和JDBC整合項目 index.jsp頁面 <% page contentType"text/html; charsetUTF-8" pageEncoding"UTF-8" %> <!DOCTYPE html> <html> <head><title>JSP - Hello World</title> …

CSS 媒體查詢 響應式開發

介紹 媒體查詢&#xff08;Media Queries&#xff09;是CSS3的技術&#xff0c;可以根據設備的特性&#xff08;如屏幕寬度、高度、方向等&#xff09;來應用不同的樣式規則。媒體查詢可以使網頁在不同設備上呈現不同的樣式&#xff0c;以實現響應式設計。 語法 media scree…

Pytorch中的torch.save()文件保存格式探索以及mmdetection加載預訓練模型參數對不齊和收到意外參數報錯解決方案

使用mmdetection時遇到的問題比較多&#xff0c;首先要對自己要使用的預訓練模型有一定的了解&#xff0c;并且懂得使用各種分類模型時不同的模型不同任務執行階段需要參數上的對其。&#xff08;比如mask-rcnn和它的三個頭之間的參數&#xff09;。 首先&#xff0c;談談torc…

什么是聲明式事務管理?

聲明式事務管理是Spring提供的一種事務管理機制&#xff0c;它允許開發者通過聲明的方式&#xff0c;而不是通過編程的方式&#xff0c;來管理事務的邊界和行為。在聲明式事務管理中&#xff0c;你可以通過注解或XML配置來指定方法或類上的事務屬性和行為。 在Spring中&#x…