【Linux】用戶向硬件寄存器寫入值過程理解

思考一下,當我們咋用戶態向寄存器寫入一個值,這個過程是怎么樣的呢?以下是應用程序通過標準庫函數(如?write()ioctl()?或?mmap())向硬件寄存器寫入值的詳細過程,從用戶空間到內核再到硬件的完整流程:


1. 用戶空間(應用程序)

應用程序通過系統調用與內核交互,最終將數據傳遞給驅動。以下是三種常見方法的詳細流程:


方法一:使用?ioctl()?系統調用

ioctl()?是最靈活的方式,適用于需要直接控制硬件寄存器的場景。

步驟:
  1. 打開設備文件
    應用程序通過?open()?系統調用打開設備文件(如?/dev/mydevice),獲得文件描述符。

    int fd = open("/dev/mydevice", O_RDWR);
    
  2. 發送命令到內核
    使用?ioctl()?系統調用,將自定義命令和參數傳遞給內核驅動。例如,寫入寄存器的命令可能定義為?MY_IOCTL_WRITE_REGISTER

    uint32_t value = 0x1234;
    ioctl(fd, MY_IOCTL_WRITE_REGISTER, &value); // 第三個參數是用戶空間的指針
    
  3. 系統調用觸發
    ioctl()?觸發軟中斷(如?syscall?或?int 0x80),進入內核空間。


方法二:使用?write()?系統調用

write()?通常用于流式設備(如文件或網絡),但某些驅動可能通過?write()?實現寄存器寫入(需驅動支持)。

步驟:
  1. 打開設備文件

    int fd = open("/dev/mydevice", O_RDWR);
    
  2. 寫入數據
    使用?write()?將數據寫入設備文件。驅動需要解析寫入的數據并映射到寄存器操作。

    uint32_t value = 0x1234;
    write(fd, &value, sizeof(value)); // 需要驅動支持將寫入的數據解析為寄存器操作
    
  3. 系統調用觸發
    write()?觸發軟中斷,進入內核空間。


方法三:使用?mmap()?內存映射

mmap()?將物理地址直接映射到用戶空間,允許直接訪問寄存器,效率最高但需謹慎操作。

步驟:
  1. 打開設備文件

    int fd = open("/dev/mydevice", O_RDWR);
    
  2. 內存映射
    使用?mmap()?將設備寄存器的物理地址映射到用戶空間的虛擬地址。

    void *reg_base = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    
  3. 直接寫入寄存器
    通過映射后的指針直接操作寄存器。

    *(volatile uint32_t *)(reg_base + 0x100) = 0x1234; // 0x100 是寄存器偏移
    

2. 內核空間(驅動層)

內核驅動負責處理用戶空間的請求,并最終將數據寫入硬件寄存器。


步驟一:系統調用處理(以?ioctl?為例)
  1. 驅動的?ioctl?方法
    內核根據文件描述符找到對應的驅動,調用驅動的?ioctl?方法。驅動解析命令和參數,執行寫寄存器操作。

    static long my_driver_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {switch (cmd) {case MY_IOCTL_WRITE_REGISTER:// 獲取用戶傳遞的值uint32_t value;if (copy_from_user(&value, (void __user *)arg, sizeof(value))) {return -EFAULT; // 復制失敗}// 寫入寄存器writel(value, reg_base + 0x100); // reg_base 是內核映射的寄存器基地址return 0;default:return -ENOTTY; // 不支持的命令}
    }
    
    Collapse
  2. 寄存器訪問函數
    內核使用?writel()readl()?等原子操作函數(或直接通過指針)寫入寄存器值。


步驟二:write()?系統調用處理
  1. 驅動的?write?方法
    驅動需要解析用戶寫入的數據,并映射到寄存器操作。
    static ssize_t my_driver_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) {uint32_t value;if (copy_from_user(&value, buf, sizeof(value))) {return -EFAULT;}writel(value, reg_base + 0x100);return sizeof(value); // 返回寫入的字節數
    }
    

步驟三:mmap()?內存映射處理
  1. 驅動的?mmap?方法
    驅動將物理地址映射到用戶空間的虛擬地址。

    static int my_driver_mmap(struct file *filp, struct vm_area_struct *vma) {unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;unsigned long size = vma->vm_end - vma->vm_start;// 將物理地址映射到用戶空間if (remap_pfn_range(vma, vma->vm_start,PHYSICAL_ADDR >> PAGE_SHIFT, // 物理地址轉換為頁幀號size, vma->vm_page_prot)) {return -EAGAIN;}return 0;
    }
    
    Collapse
  2. 物理地址映射
    驅動通過?ioremap()?將硬件寄存器的物理地址映射到內核虛擬地址空間:

    void __iomem *reg_base = ioremap(PHYSICAL_ADDR, 4096); // 4096 是映射區域大小
    

3. 硬件層

內核通過內存映射的地址直接訪問硬件寄存器:

  1. 物理地址訪問
    內核的?reg_base?是通過?ioremap()?映射的虛擬地址,對應硬件的物理地址。當內核執行?writel(value, reg_base + OFFSET)?時:

    • CPU 將虛擬地址轉換為物理地址。
    • 通過總線(如 AXI、APB)將數據寫入硬件寄存器。
  2. 硬件響應
    硬件檢測到寄存器值變化后,根據寄存器的功能執行操作(如啟動模塊、配置時鐘等)。


關鍵流程總結

階段操作
用戶空間1. 打開設備文件<br>2. 通過?ioctlwrite?或?mmap?發送請求
內核空間1. 處理系統調用(如?ioctlwrite)或內存映射(mmap)<br>2. 映射物理地址到內核虛擬地址<br>3. 執行寄存器寫入操作
硬件層1. 通過總線接收數據<br>2. 更新寄存器值并觸發硬件行為

關鍵函數與機制

  1. **ioremap()/ioremap_nocache()**:
    將硬件寄存器的物理地址映射到內核虛擬地址空間,允許內核直接訪問。

  2. **writel()/readl()**:
    內核提供的原子操作函數,用于安全地讀寫寄存器(處理內存屏障等)。

  3. **copy_from_user()**:
    將用戶空間的數據復制到內核空間(如?ioctl?和?write?中的參數傳遞)。

  4. **mmap()?和?remap_pfn_range()**:
    將物理地址映射到用戶空間,允許用戶直接訪問寄存器。

  5. **ioctl()?和?write()**:
    用戶空間與驅動通信的接口,通過自定義命令或數據流傳遞參數。


注意事項

  1. 權限控制

    • 設備文件(如?/dev/mydevice)需設置正確的權限(如?666?或?600)。
    • 驅動的?open?方法可進一步檢查用戶權限。
  2. 緩存一致性

    • 如果寄存器映射到緩存區域,需使用?volatile?關鍵字或?mb()?等內存屏障確保數據同步。
  3. 錯誤處理

    • 內核需檢查?ioremapmmap?等操作是否成功,避免空指針。
    • 用戶空間需處理?ioctlwrite?和?mmap?的返回值。

示例代碼片段

驅動代碼(my_driver.c
#include <linux/io.h>
#include <linux/uaccess.h>#define PHYSICAL_ADDR 0x40000000  // 硬件寄存器的物理地址
#define OFFSET 0x100              // 寄存器偏移static void __iomem *reg_base;// ioctl 處理函數
static long my_driver_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {switch (cmd) {case MY_IOCTL_WRITE_REGISTER:uint32_t value;if (copy_from_user(&value, (void __user *)arg, sizeof(value))) {return -EFAULT;}writel(value, reg_base + OFFSET);return 0;default:return -ENOTTY;}
}// write 處理函數
static ssize_t my_driver_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) {uint32_t value;if (copy_from_user(&value, buf, sizeof(value))) {return -EFAULT;}writel(value, reg_base + OFFSET);return sizeof(value);
}// mmap 處理函數
static int my_driver_mmap(struct file *filp, struct vm_area_struct *vma) {unsigned long size = vma->vm_end - vma->vm_start;if (remap_pfn_range(vma, vma->vm_start,PHYSICAL_ADDR >> PAGE_SHIFT,size, vma->vm_page_prot)) {return -EAGAIN;}return 0;
}// 驅動初始化
static int __init my_driver_init(void) {reg_base = ioremap(PHYSICAL_ADDR, 4096);if (!reg_base) {pr_err("ioremap failed\n");return -ENOMEM;}// 注冊字符設備...return 0;
}
module_init(my_driver_init);

Collapse

用戶空間代碼(user_app.c
#include <fcntl.h>
#include <sys/mman.h>int main() {int fd = open("/dev/mydevice", O_RDWR);if (fd < 0) {perror("open");return -1;}// 方法一:通過 ioctluint32_t value = 0x1234;ioctl(fd, MY_IOCTL_WRITE_REGISTER, &value);// 方法二:通過 writewrite(fd, &value, sizeof(value));// 方法三:通過 mmapvoid *reg_base = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (reg_base == MAP_FAILED) {perror("mmap");return -1;}*(volatile uint32_t *)(reg_base + 0x100) = 0x5678;close(fd);return 0;
}

Collapse


總結

應用程序通過以下方式將值寫入硬件寄存器:

  1. **ioctl()**:通過自定義命令傳遞參數,驅動解析后寫入寄存器。
  2. **write()**:驅動需解析寫入的數據流,映射到寄存器操作。
  3. **mmap()**:直接映射物理地址到用戶空間,高效但需謹慎操作。

內核通過?ioremap?映射物理地址,驅動通過原子操作函數(如?writel)確保安全訪問,最終通過總線將數據寫入硬件寄存器。

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

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

相關文章

自動駕駛02:點云預處理——02(運動補償篇LIO-SAM)

當激光雷達&#xff08;LiDAR&#xff09;在運動中采集點云時&#xff0c;每個點的時間戳不同&#xff0c;而車輛在移動&#xff0c;導致點云在不同時間點的坐標與實際情況不符&#xff0c;這種現象稱為運動畸變&#xff08;Motion Distortion&#xff09;。為了得到無畸變的點…

基礎算法篇(3)(藍橋杯常考點)—圖論

前言 這期是基礎算法篇的第三節&#xff0c;其中的dijkstra算法更是藍橋杯中的高頻考點 圖的基本相關概念 有向圖和無向圖 自環和重邊 稠密圖和稀疏圖 對于不帶權的圖&#xff0c;一條路徑的路徑長度是指該路徑上各邊權值的總和 對于帶權的圖&#xff0c;一條路徑長度時指該路…

Crawl4AI:專為AI設計的開源網頁爬蟲工具,釋放大語言模型的潛能

在當今數據驅動的AI時代,高效獲取結構化網頁數據是模型訓練和應用落地的關鍵。Crawl4AI作為一款專為大型語言模型(LLMs)設計的開源爬蟲工具,憑借其極速性能、AI友好輸出和模塊化設計,正在成為開發者社區的熱門選擇。本文將深入解析其核心特性與技術優勢。 一、Crawl4AI的核…

前后端數據序列化:從數組到字符串的旅程(附優化指南)

&#x1f310; 前后端數據序列化&#xff1a;從數組到字符串的旅程&#xff08;附優化指南&#xff09; &#x1f4dc; 背景&#xff1a;為何需要序列化&#xff1f; 在前后端分離架構中&#xff0c;復雜數據類型&#xff08;如數組、對象&#xff09;的傳輸常需序列化為字符…

匯編學習之《移位指令》

這章節學習前需要回顧之前的標志寄存器的內容&#xff1a; 匯編學習之《標志寄存器》 算數移位指令 SAL (Shift Arithmetic Left)算數移位指令 : 左移一次&#xff0c;最低位用0補位&#xff0c;最高位放入EFL標志寄存器的CF位&#xff08;進位標志&#xff09; OllyDbg查看…

NLP高頻面試題(二十九)——大模型解碼常見參數解析

在大語言模型的實際應用中&#xff0c;如何更有效地控制文本生成的質量與多樣性&#xff0c;一直是熱門研究話題。其中&#xff0c;模型解碼&#xff08;decode&#xff09;策略至關重要&#xff0c;涉及的主要參數包括 top_k、top_p 和 temperature 等。本文將詳細介紹這些常見…

【C#】Task 線程停止

CancellationTokenSource cts 是用于控制任務&#xff08;線程&#xff09;停止運行的。我們一步步來解釋它的作用。 &#x1f50d; 現在的代碼結構大概是這樣的&#xff1a; Task.Run(() > {while (true){// 不斷循環采集圖像} });這種寫法雖然簡單&#xff0c;但最大的問…

WebRTC的ICE之TURN協議的交互流程中繼轉發Relay媒體數據的turnserver的測試

WebRTC的ICE之TURN協議的交互流程和中繼轉發Relay媒體數據的turnserver的測試 WebRTC的ICE之TURN協議的交互流程中繼轉發Relay媒體數據的turnserver的測試 WebRTC的ICE之TURN協議的交互流程和中繼轉發Relay媒體數據的turnserver的測試前言一、TURN協議1、連接Turn Server 流程①…

Redis + Caffeine多級緩存電商場景深度解析

Redis Caffeine多級緩存 Redis Caffeine多級緩存電商場景深度解析一、實施目的二、具體實施2.1 架構設計2.2 組件配置2.3 核心代碼實現 三、實施效果3.1 性能指標對比3.2 業務指標改善3.3 系統穩定性 四、關鍵策略4.1 緩存預熱4.2 一致性保障4.3 監控配置Prometheus監控指標 …

前端開發3D-基于three.js

基于 three.js 渲染任何畫面&#xff0c;都要基于這 3 個要素來實現 1場景scene&#xff1a;放置物體的容器 2攝像機&#xff1a;類似人眼&#xff0c;可調整位置&#xff0c;角度等信息&#xff0c;展示不同畫面 3渲染器&#xff1a;接收場景和攝像機對象&#xff0c;計算在瀏…

代碼隨想錄算法訓練營--打卡day4

一.移除鏈表元素 1.題目鏈接 203. 移除鏈表元素 - 力扣&#xff08;LeetCode&#xff09; 2.思路 通過 while 循環來遍歷鏈表&#xff0c;只要 cur 的下一個節點不為空&#xff0c;就繼續循環。在循環中&#xff0c;對 cur 的下一個節點的值進行判斷&#xff1a; 值不等于…

虛擬電廠:多元能源聚合,開啟綠色電力新時代

虛擬電廠&#xff1a;多元能源聚合&#xff0c;開啟綠色電力新時代 在“雙碳”目標驅動下&#xff0c;電力系統正經歷從集中式向分布式、從單一能源向多能互補的深刻變革。 作為能源互聯網的核心載體&#xff0c;虛擬電廠通過數字化技術整合多種能源資源&#xff0c;而是像指…

高通Android10 鈴聲通話音頻80%音量修改

先修改最高的音量step --- a/SC60_AP/frameworks/base/services/core/java/com/android/server/audio/AudioService.javab/SC60_AP/frameworks/base/services/core/java/com/android/server/audio/AudioService.java-311,14 311,14 public class AudioService extends IAudio…

類加載過程?類隔離了解過嗎?

類加載過程詳解 類加載是 JVM 將類的字節碼從磁盤、網絡或其他來源加載到內存&#xff0c;并轉換為 Class 對象的過程&#xff0c;主要分為以下 五個階段&#xff1a; 1. 加載&#xff08;Loading&#xff09; 任務&#xff1a;查找類的二進制字節流&#xff08;如 .class 文…

使用msmtp和mutt在CentOS上發送指定目錄下的所有文件作為郵件附件

1.安裝 msmtp&#xff1a; 如果尚未安裝&#xff0c;請先通過以下命令安裝msmtp。 sudo yum install msmtp 2.配置 msmtp 使用新浪郵箱&#xff1a; 創建或編輯配置文件~/.msmtprc&#xff0c;輸入以下內容&#xff08;記得替換授權碼&#xff09;。 defaults tls on tls_st…

Vue+Elementui首頁看板

源碼 <template><!-- 查詢條件--><div class="optimize-norm" v-loading="selectDataLoading"><el-form :model="queryParams" ref="queryRef" style="padding-bottom:8px" :inline="true"…

匯編學習之《指針寄存器大小端學習》

什么是指針寄存器&#xff1f; 操作棧的寄存器 棧&#xff1a; 保存函數里面傳遞的參數&#xff0c;局部變量等。 EBP&#xff1a; 指向棧底的指針 ESP&#xff1a; 指向棧頂的指針。 計算入棧地址變化規則 通過OllDbg查看 有可能點擊安裝的時候棧區域第一次查看會沒有顯…

Oracle數據庫數據編程SQL<3.7 PL/SQL 觸發器(Trigger)>

觸發器是Oracle數據庫中的一種特殊存儲過程&#xff0c;它會在特定數據庫事件發生時自動執行。觸發器通常用于實現復雜的業務規則、數據驗證、審計跟蹤等功能。 目錄 一、觸發器基本概念 1. 觸發器特點 2. 觸發器組成要素 二、觸發器類型 1. DML觸發器 2. DDL觸發器 3.…

2025年滲透測試面試題總結-某 攜程旅游-基礎安全工程師(題目+回答)

網絡安全領域各種資源&#xff0c;學習文檔&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各種好玩的項目及好用的工具&#xff0c;歡迎關注。 目錄 攜程旅游-基礎安全工程師 反序列化原理 核心原理 擴展分析 SQL注入本質 核心原理 擴展分析 SQL注…

CSS 邊框(Border)樣式詳解

CSS 邊框&#xff08;Border&#xff09;樣式詳解 CSS 提供了多種邊框樣式&#xff0c;使我們能夠控制元素的外觀。本文將詳細介紹 CSS 邊框的各種屬性及應用示例。 1. 基本邊框屬性 CSS 主要使用 border 相關屬性定義邊框&#xff0c;基本語法如下&#xff1a; border: [邊…