三、linux字符驅動詳解

在上一節完成NFS開發環境的搭建后,本節將探討Linux字符設備驅動的開發。字符設備驅動作為Linux內核的重要組成部分,主要負責管理與字符設備(如串口、鍵盤等)的交互,并為用戶空間程序提供統一的讀寫操作接口。

驅動代碼

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/cdev.h> #define DEVICE_NAME "hello_chrdev"
#define BUFFER_SIZE 100// 設備結構體
typedef struct {char buffer[BUFFER_SIZE];struct class *class;struct device *device;dev_t dev_num;struct cdev cdev;
} HelloDevice;static HelloDevice hello_dev;// 打開設備
static int hello_open(struct inode *inode, struct file *filp) {printk(KERN_INFO "Hello device opened\n");return 0;
}// 讀取設備
static ssize_t hello_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {size_t len = strlen(hello_dev.buffer);if (*f_pos >= len) {return 0;}if (count > len - *f_pos) {count = len - *f_pos;}if (copy_to_user(buf, hello_dev.buffer + *f_pos, count)) {return -EFAULT;}*f_pos += count;return count;
}// 寫入設備
static ssize_t hello_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {if (count > BUFFER_SIZE - 1) {count = BUFFER_SIZE - 1;}if (copy_from_user(hello_dev.buffer, buf, count)) {return -EFAULT;}hello_dev.buffer[count] = '\0';*f_pos += count;return count;
}// 關閉設備
static int hello_release(struct inode *inode, struct file *filp) {printk(KERN_INFO "Hello device closed\n");return 0;
}// 文件操作結構體
static struct file_operations hello_fops = {.owner = THIS_MODULE,.open = hello_open,.read = hello_read,.write = hello_write,.release = hello_release,
};// 模塊初始化函數
static int __init hello_init(void) {int ret;// 分配設備號ret = alloc_chrdev_region(&hello_dev.dev_num, 0, 1, DEVICE_NAME);if (ret < 0) {printk(KERN_ERR "Failed to allocate character device number\n");return ret;}// 創建類hello_dev.class = class_create(THIS_MODULE, DEVICE_NAME);if (IS_ERR(hello_dev.class)) {unregister_chrdev_region(hello_dev.dev_num, 1);printk(KERN_ERR "Failed to create class\n");return PTR_ERR(hello_dev.class);}// 創建設備hello_dev.device = device_create(hello_dev.class, NULL, hello_dev.dev_num, NULL, DEVICE_NAME);if (IS_ERR(hello_dev.device)) {class_destroy(hello_dev.class);unregister_chrdev_region(hello_dev.dev_num, 1);printk(KERN_ERR "Failed to create device\n");return PTR_ERR(hello_dev.device);}// 初始化 cdev 結構體cdev_init(&hello_dev.cdev, &hello_fops);hello_dev.cdev.owner = THIS_MODULE;// 添加字符設備到系統ret = cdev_add(&hello_dev.cdev, hello_dev.dev_num, 1);if (ret < 0) {device_destroy(hello_dev.class, hello_dev.dev_num);class_destroy(hello_dev.class);unregister_chrdev_region(hello_dev.dev_num, 1);printk(KERN_ERR "Failed to add character device\n");return ret;}printk(KERN_INFO "Hello device initialized. Major: %d, Minor: %d\n", MAJOR(hello_dev.dev_num), MINOR(hello_dev.dev_num));return 0;
}// 模塊卸載函數
static void __exit hello_exit(void) {cdev_del(&hello_dev.cdev);device_destroy(hello_dev.class, hello_dev.dev_num);class_destroy(hello_dev.class);unregister_chrdev_region(hello_dev.dev_num, 1);printk(KERN_INFO "Hello device removed\n");
}module_init(hello_init);
module_exit(hello_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple hello world character device driver");

函數接口詳解

1. 模塊初始化與退出相關函數
alloc_chrdev_region
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
  • 功能:動態分配一組連續的字符設備號。
  • 參數
    • dev:用于存儲分配到的設備號。
    • baseminor:起始的次設備號。
    • count:要分配的設備號數量。
    • name:設備的名稱,用于在 /proc/devices 中顯示。
  • 返回值:成功返回 0,失敗返回負數錯誤碼。
class_create
struct class *class_create(struct module *owner, const char *name);
  • 功能:在 /sys/class 目錄下創建一個設備類。
  • 參數
    • owner:指向模塊的指針,通常為 THIS_MODULE
    • name:類的名稱。
  • 返回值:成功返回指向 struct class 的指針,失敗返回錯誤指針。
device_create
struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);
  • 功能:在 /sys/class/<class_name> 目錄下創建設備節點,并在 /dev 目錄下創建對應的設備文件。
  • 參數
    • class:指向設備類的指針。
    • parent:父設備指針,通常為 NULL
    • devt:設備號。
    • drvdata:設備驅動數據,通常為 NULL
    • fmt:設備名稱的格式化字符串。
  • 返回值:成功返回指向 struct device 的指針,失敗返回錯誤指針。
cdev_init
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
  • 功能:初始化字符設備結構體 struct cdev,并關聯文件操作結構體 struct file_operations
  • 參數
    • cdev:指向 struct cdev 的指針。
    • fops:指向 struct file_operations 的指針。
cdev_add
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
  • 功能:將字符設備添加到內核中。
  • 參數
    • p:指向 struct cdev 的指針。
    • dev:設備號。
    • count:設備數量。
  • 返回值:成功返回 0,失敗返回負數錯誤碼。
module_initmodule_exit
module_init(hello_init);
module_exit(hello_exit);
  • 功能:分別指定模塊加載和卸載時調用的函數。
2. 文件操作相關函數
  • 在 Linux 內核中,struct file_operations 結構體是字符設備驅動與用戶空間進行交互的關鍵橋梁,其中 openreadwriterelease 是比較常用的操作函數。

    struct file_operations` 結構體中相關成員介紹

    open 函數
    int (*open) (struct inode *inode, struct file *filp);
    
    • 功能:當用戶空間使用 open() 系統調用打開設備文件時,內核會調用驅動中注冊的 open 函數。該函數通常用于執行設備的初始化操作,如分配資源、檢查設備狀態等。
    • 參數
      • struct inode *inode:指向文件對應的索引節點,包含了文件的元信息,如文件類型、權限等。
      • struct file *filp:指向文件對象,代表了一個打開的文件實例,包含了文件的當前狀態、偏移量等信息。
    • 返回值:成功時返回 0,失敗時返回負數錯誤碼。
    read 函數
    ssize_t (*read) (struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
    
    • 功能:當用戶空間使用 read() 系統調用從設備文件讀取數據時,內核會調用驅動中的 read 函數。該函數負責將設備中的數據復制到用戶空間的緩沖區。
    • 參數
      • struct file *filp:指向文件對象。
      • char __user *buf:用戶空間的緩沖區指針,用于存儲從設備讀取的數據。
      • size_t count:用戶請求讀取的字節數。
      • loff_t *f_pos:文件的當前偏移量指針,可通過修改該指針來更新文件的讀寫位置。
    • 返回值:成功時返回實際讀取的字節數,返回 0 表示已到達文件末尾,失敗時返回負數錯誤碼。
    write 函數
    ssize_t (*write) (struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
    
    • 功能:當用戶空間使用 write() 系統調用向設備文件寫入數據時,內核會調用驅動中的 write 函數。該函數負責將用戶空間緩沖區中的數據復制到設備中。
    • 參數
      • struct file *filp:指向文件對象。
      • const char __user *buf:用戶空間的緩沖區指針,包含了要寫入設備的數據。
      • size_t count:用戶請求寫入的字節數。
      • loff_t *f_pos:文件的當前偏移量指針。
    • 返回值:成功時返回實際寫入的字節數,失敗時返回負數錯誤碼。
    release 函數
    int (*release) (struct inode *inode, struct file *filp);
    
    • 功能:當用戶空間使用 close() 系統調用關閉設備文件時,內核會調用驅動中的 release 函數。該函數通常用于執行設備的清理操作,如釋放資源、關閉設備等。
    • 參數
      • struct inode *inode:指向文件對應的索引節點。
      • struct file *filp:指向文件對象。
    • 返回值:成功時返回 0,失敗時返回負數錯誤碼。
3. 模塊卸載相關函數
cdev_del
void cdev_del(struct cdev *p);
  • 功能:從內核中移除字符設備。
  • 參數
    • p:指向 struct cdev 的指針。
device_destroy
void device_destroy(struct class *class, dev_t devt);
  • 功能:銷毀 /sys/class/<class_name> 目錄下的設備節點和 /dev 目錄下的設備文件。
  • 參數
    • class:指向設備類的指針。
    • devt:設備號。
class_destroy
void class_destroy(struct class *cls);
  • 功能:銷毀 /sys/class 目錄下的設備類。
  • 參數
    • cls:指向 struct class 的指針。
unregister_chrdev_region
void unregister_chrdev_region(dev_t from, unsigned count);
  • 功能:釋放之前分配的字符設備號。
  • 參數
    • from:起始的設備號。
    • count:要釋放的設備號數量。

編譯和測試

編寫 Makefile
obj-m += helloworld.o
KDIR := linux-5.15.18/
PWD := $(shell pwd)
default:$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:$(MAKE) -C $(KDIR) M=$(PWD) clean

將 linux-5.15.18/ 替換為實際的 Linux 5.15.18 內核源碼路徑。

編譯驅動

在終端中執行 make 命令編譯驅動模塊。

測試驅動

在 QEMU 終端中:

  1. 使用 insmod helloworld.ko 加載驅動模塊。
  2. 使用 echo “Hello World” > /dev/helloworld 向設備寫入數據。
  3. 使用 cat /dev/helloworld 從設備讀取數據。
  4. 使用 rmmod hello_chrdev.ko 卸載驅動模塊。
    在這里插入圖片描述

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

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

相關文章

Python爬蟲處理網頁中的動態內容

文章目錄 前言一、Python環境搭建1.Python安裝2.選擇Python開發環境 二、Python爬蟲處理網頁中的動態內容1. 使用 Selenium 庫2. 使用 Pyppeteer 庫3. 分析 API 請求 前言 在網頁中&#xff0c;動態內容通常是指那些通過 JavaScript 在頁面加載后動態生成或更新的內容&#xf…

重學SpringBoot3-Spring Retry實踐

更多SpringBoot3內容請關注我的專欄&#xff1a;《SpringBoot3》 期待您的點贊??收藏評論 重學SpringBoot3-Spring Retry實踐 1. 簡介2. 環境準備3. 使用方式 3.1 注解方式 基礎使用自定義重試策略失敗恢復機制重試和失敗恢復效果注意事項 3.2 編程式使用3.3 監聽重試過程 監…

vue3中解決組件間 css 層級問題最佳實踐(Teleport的使用)

定義&#xff1a; <Teleport> 是 Vue 3 中引入的一個內置組件&#xff0c;用于將組件的內容渲染到 DOM 中的指定位置&#xff0c;而不受組件層級結構的限制。這在處理模態框、通知、下拉菜單等需要脫離當前組件層級的情況下非常有用。 通俗來說&#xff0c;Teleport的功…

密度提升30%!Intel 18A工藝正式開放代工

快科技2月23日消息&#xff0c;Intel官方網站悄然更新了對于18A(1.8nm級)工藝節點的描述&#xff0c;稱已經做好了迎接客戶項目的準備&#xff0c;將在今年上半年開始流片&#xff0c;有需求的客戶可以隨時聯系。 Intel宣稱&#xff0c;這是在北美地區率先量產的2nm以下工藝節…

docker中常用的命令

一、服務命令 systemctl start docker.service 啟動docker服務 systemctl stop docker.service 關閉docker服務 systemctl enable docker.service 設置docker服務開機啟動 systemctl disable docker.service .禁止docker服務開機自啟動 二、鏡像命令 d…

架構師論文《智慧醫療系統中的數據集成與共享》

智慧醫療系統中的數據集成與共享 摘要 隨著醫療信息化的發展&#xff0c;如何實現跨系統、跨機構的數據集成與共享成為智慧醫療建設的核心問題。2019年&#xff0c;我所在的醫療科技公司承接了某省衛生健康委員會主導的“區域醫療信息化平臺”項目。該平臺旨在整合區域內三甲醫…

請求go構建緩存,go clean -cache

go clean -cache go 構建時會產生很多緩存&#xff0c; 一般是目錄&#xff1a;/Users/xxx/Library/Caches/go-build 此目錄README&#xff1a; This directory holds cached build artifacts from the Go build system. Run "go clean -cache" if the directory …

mybatis從接口直接跳到xml的插件

在使用 MyBatis(包括 MyBatis-Plus)時,如果你希望從接口方法直接跳轉到對應的 XML 映射文件中的 SQL 語句定義,可以借助一些開發工具或插件來實現這一功能。以下是幾種常見的方法和插件推薦: 方法一:使用 IDE 內置功能 IntelliJ IDEA IntelliJ IDEA 提供了對 MyBatis …

計算機視覺行業洞察--影像行業系列第一期

計算機視覺行業產業鏈的上下游構成相對清晰&#xff0c;從基礎技術研發到具體應用場景的多個環節相對成熟。 以下是我結合VisionChina經歷和行業龍頭企業對計算機視覺行業產業鏈上下游的拆解總結。 上下游總結 上游產業鏈分為軟硬件兩類&#xff0c;視覺的硬件主要指芯片、…

Spring事務原理 二

在上一篇博文《Spring事務原理 一》中&#xff0c;我們熟悉了Spring聲明式事務的AOP原理&#xff0c;以及事務執行的大體流程。 本文中&#xff0c;介紹了Spring事務的核心組件、傳播行為的源碼實現。下一篇中&#xff0c;我們將結合案例&#xff0c;來講解實戰中有關事務的易…

邏輯函數的神經網絡實現

1.單層感知器實現基本邏輯函數 先給大家拋出一道例題 &#xff08;一&#xff09;種類 a.OR函數 目標&#xff1a;當至少一個輸入為1時&#xff0c;輸出1&#xff1b;否則輸出0。 權重設置&#xff1a; 輸入權重&#xff1a;所有 wi1&#xff08;i1,2,...,m&#xff09;。…

SF-HCI-SAP問題收集1

最近在做HCI的集成&#xff0c;是S4的環境&#xff0c;發現很多東西都跑不通&#xff0c;今天開始收集一下錯誤點 如果下圖沖從0001變成0010&#xff0c;sfiom_rprq_osi表就會存數據&#xff0c;系統檢查到此表就會報錯&#xff0c;這個選項的作用就是自定義信息類型也能更新&a…

(面試經典問題之分布式鎖)分布式鎖的基本原理、作用以及實現

一、什么是分布式鎖 分布式鎖指的是在分布式場景中實現互斥類型的鎖。 分布式是什么意思&#xff1f;分布式表示運行的節點可能在不同的機器或不同的網段中&#xff0c;節點間通信通過socket。互斥類型是什么意思&#xff1f;互斥類型表示同一時刻只允許一個執行體進入臨界資…

機械硬盤與固態硬盤的區別-機械硬盤的未來在哪里?

隨著近年來固態硬盤的技術成熟和成本的下探&#xff0c;固態硬盤&#xff08;SSD&#xff09;儼然有要取代傳統機械硬盤&#xff08;HDD&#xff09;的趨勢&#xff0c;但目前單位容量下機械硬盤每GB價格相比閃存還有5-7倍的優勢&#xff0c;那么機械硬盤是否已經發展到極限&am…

06排序 + 查找(D1_排序(D1_基礎學習))

目錄 學習預熱&#xff1a;基礎知識 一、什么是排序 二、為什么要排序 三、排序的穩定性 四、排序穩定性的意義 五、排序分類方式 方式一&#xff1a;內外分類 方式二&#xff1a;比較分類 六、排序算法性能評估 1. 算法的時間復雜度 2. 算法的空間復雜度 七、知識小…

簡訊:Rust 2024 edition and v1.85.0 已發布

詳見 https://blog.rust-lang.org/2025/02/20/Rust-1.85.0.html 升級方法&#xff1a;rustup update stable

Python 錯誤和異常處理

目錄 try-except塊 例子&#xff1a; 輸出&#xff1a; 捕獲多種異常 例子&#xff1a; else和finally 例子&#xff1a; 輸出&#xff1a; 自定義異常 例子&#xff1a; 輸出&#xff1a; 好的&#xff0c;簡單來說&#xff0c;錯誤和異常處理是編程中用來處理程序…

Linux系統使用Docker部署Geoserver并做數據掛載進行地圖服務的發布和游覽

文章目錄 1、前提環境2、拉取geoserver鏡像3、創建數據掛載目錄4、 運行容器5、 測試使用&#xff08;發布shp數據為服務&#xff09;5.1、創建工作區5.2、添加數據存儲5.3、發布圖層5.4、服務游覽 1、前提環境 部署環境&#xff1a;Linux&#xff0c;Centos7 &#xff0c;Doc…

Innovus中快速獲取timing path邏輯深度的golden腳本

在實際項目中我們經常會遇到一條timing path級數特別多&#xff0c;可能是一兩頁都翻不完。此時&#xff0c;我們大都需要手工去數這條path上到底有哪些是設計本身的邏輯&#xff0c;哪些是PR工具插入的buffer和inverter。 數字IC后端手把手培訓教程 | Clock Gating相關clock …

Python爬蟲實戰:從零到一構建數據采集系統

文章目錄 前言一、準備工作1.1 環境配置1.2 選擇目標網站 二、爬蟲實現步驟2.1 獲取網頁內容2.2 解析HTML2.3 數據保存 三、完整代碼示例四、優化與擴展4.1 反爬應對策略4.2 動態頁面處理4.3 數據可視化擴展 五、注意事項六、總結互動環節 前言 在大數據時代&#xff0c;數據采…