当前位置: 首页 > news >正文

基于 Linux 内核模块的字符设备互斥访问实验

这个信号量实验是基于 Linux 内核模块的字符设备互斥访问实验,核心是用信号量实现多进程对设备的互斥访问,步骤如下(含代码修正、编译、测试全流程):

一、环境准备

  1. 工具链与内核源码
    • 安装对应架构的交叉编译工具链(比如实验中ARCH=arm64,需安装aarch64-linux-gnu-)。
    • 准备开发板对应的Linux 内核源码,并记录其路径(后续MakefileKDIR需填写此路径)。

二、代码准备(修正实验中笔误)

将实验代码整理为 3 个文件:

1. 内核模块代码:semaphore_test.c

(修正semaphpresemaphoresleppsleep等笔误)

#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/uaccess.h> #include <linux/delay.h> #include <linux/semaphore.h> struct semaphore semaphore_test; // 定义信号量 static int open_test(struct inode *inode, struct file *file) { printk("this is open_test \n"); down(&semaphore_test); // 获取信号量(值-1) return 0; } static ssize_t read_test(struct file *file, char __user *ubuf, size_t len, loff_t *off) { int ret; char kbuf[10] = "topeet"; // 内核缓冲区 printk("read_test \n"); ret = copy_to_user(ubuf, kbuf, strlen(kbuf)); // 内核→用户空间 if (ret != 0) { printk("copy_to_user is error \n"); } printk("copy_to_user is ok \n"); return 0; } static char kbuf[10] = {0}; // 全局内核缓冲区 static ssize_t write_test(struct file *file, const char __user *ubuf, size_t len, loff_t *off) { int ret; ret = copy_from_user(kbuf, ubuf, len); // 用户→内核空间 if (ret != 0) { printk("copy_from_user is error\n"); } if(strcmp(kbuf, "topeet") == 0) { // 传"topeet"则sleep4秒 sleep(4); } else if(strcmp(kbuf, "te") == 0) { // 传"te"则sleep2秒 sleep(2); } printk("copy_from_user buf is %s \n", kbuf); return 0; } static int release_test(struct inode *inode, struct file *file) { up(&semaphore_test); // 释放信号量(值+1) printk("this is release_test \n"); return 0; } // 字符设备结构 struct chrdrv_test { dev_t dev_num; // 设备号 int major, minor; // 主/次设备号 struct cdev cdev_test; // cdev结构 struct class *class_test; // 设备类 }; struct chrdrv_test devi; // 文件操作集合 static const struct file_operations fops_test = { .owner = THIS_MODULE, // 所属模块 .open = open_test, // 打开设备 .read = read_test, // 读设备 .write = write_test, // 写设备 .release = release_test // 释放设备 }; static int __init atomic_init(void) { sema_init(&semaphore_test, 1); // 初始化信号量(初始值1,互斥信号量) // 自动分配设备号 if(alloc_chrdev_region(&devi.dev_num, 0, 1, "chrdrv_name") < 0) { printk("alloc_chrdev_region is error \n"); return -1; } printk("alloc chrdrv region is ok \n"); devi.major = MAJOR(devi.dev_num); devi.minor = MINOR(devi.dev_num); printk("major is %d,minor is %d\n", devi.major, devi.minor); // 初始化cdev cdev_init(&devi.cdev_test, &fops_test); devi.cdev_test.owner = THIS_MODULE; cdev_add(&devi.cdev_test, devi.dev_num, 1); // 添加cdev到内核 // 创建设备类和设备文件 devi.class_test = class_create(THIS_MODULE, "class_test"); device_create(devi.class_test, NULL, devi.dev_num, NULL, "device_test"); return 0; } static void __exit atomic_exit(void) { device_destroy(devi.class_test, devi.dev_num); class_destroy(devi.class_test); cdev_del(&devi.cdev_test); unregister_chrdev_region(devi.dev_num, 1); } module_init(atomic_init); module_exit(atomic_exit); MODULE_LICENSE("GPL");
2. Makefile(编译内核模块)

makefile

export ARCH=arm64 export CROSS_COMPILE=aarch64-linux-gnu- obj-m = semaphore_test.o KDIR := /path/to/your/linux-kernel-source # 替换为实际内核源码路径 PWD ?= $(shell pwd) all: make -C $(KDIR) M=$(PWD) modules # 编译模块 clean: make -C $(KDIR) M=$(PWD) clean # 清理编译产物
3. 应用层测试代码:app.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <unistd.h> int main(int argc, char *argv[]) { int fd; char str[16] = {0}; // 打开设备文件(argv[1]是设备路径,如/dev/device_test) fd = open(argv[1], O_RDWR); if(fd < 0) { printf("file open failed \n"); return -1; } // 向设备写数据(argv[2]是要写入的内容,如"test") if (strcmp(argv[2], "test") == 0) { write(fd, "topeet", 10); } else if (strcmp(argv[2], "te") == 0) { write(fd, "te", 10); } close(fd); return 0; }

三、编译

  1. 编译内核模块:在semaphore_test.cMakefile所在目录执行:

    make

    生成semaphore_test.ko(内核模块文件)。

  2. 编译应用层程序:用交叉编译器编译app.c

    aarch64-linux-gnu-gcc app.c -o app

四、部署与测试(在开发板上操作)

  1. 上传文件:将semaphore_test.koapp传到开发板(比如用scp)。

  2. 加载内核模块

    insmod semaphore_test.ko

    执行dmesg查看输出,确认模块加载成功(会显示major is xxx, minor is 0等信息)。

  3. 测试互斥访问:打开2 个终端,分别运行测试程序:

    • 终端 1(写 "topeet",会 sleep4 秒):
      ./app /dev/device_test test
    • 终端 2(写 "te",会 sleep2 秒):
      ./app /dev/device_test te

    观察dmesg输出:终端 2 的进程会等待终端 1 的进程执行release_test(释放信号量)后,才会执行open_test(获取信号量),实现了多进程对设备的互斥访问。

五、收尾

测试完成后,卸载内核模块:

rmmod semaphore_test
http://www.jsqmd.com/news/167591/

相关文章:

  • Markdown撰写技术博客|Miniconda-Python3.10环境实测PyTorch性能
  • 领域专用提示词系列扩展
  • 微爱帮监狱寄信云存储技术实现方案
  • 近视为什么一到冬天就狂涨?第四个原因,90%的人没意识到
  • Linux 字符设备驱动中 “主次设备号的静态 / 动态分配” 实验
  • ThinkLink 正式上线 Docker 安装版本:快速部署的一体化 LoRaWAN 物联网平台
  • Java毕设项目:基于SpringBoot的房屋租赁系统的设计与实现(源码+文档,讲解、调试运行,定制等)
  • 有没有什么好办法让孩子坚持调节训练呢?
  • Pyenv管理多个Python版本?不如试试Miniconda-Python3.10专用环境
  • 程序员不怕BUG,怕的是老到没人要
  • 制定内容日历实现长期稳定更新节奏
  • Java毕设项目:基于SpringBoot的“鲜蔬坊”蔬菜销售平台(源码+文档,讲解、调试运行,定制等)
  • 科研团队协作首选:统一Miniconda-Python3.10环境杜绝差异
  • LLM - 生产级 AI Agent 设计手册:从感知、记忆到决策执行的全链路架构解析
  • 小白也能学会的PyTorch安装教程GPU版|Miniconda-Python3.10上手指南
  • Java毕设选题推荐:基于Spring Boot与MySQL的二手车销售管理系统的设计与实现车源管理 - 评估定价 - 交易签约 - 售后跟踪【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 建议收藏!秘书工作可能用到的ai工具大合集来啦!
  • 使用Miniconda-Python3.10镜像构建可复现的AI论文实验环境
  • dbt+DataOps+StarRocks:构建一体化数据治理与智能分析平台实践
  • 斯坦福大学发现:AI系统分工模式的信息论奥秘
  • 在禅道中如何新增测试用例?
  • Markdown转HTML发布技术文章|Miniconda-Python3.10环境实操
  • Conda与Pip混合使用指南|Miniconda-Python3.10环境下PyTorch安装策略
  • SCI检索号怎么看?
  • PyTorch安装教程GPU版:Miniconda-Python3.10镜像一键配置深度学习环境
  • jmeter设置中文页面的办法?
  • AI应用架构师如何巧妙落地增量学习应用实践
  • APS1604M-SQR-ZR产品特点
  • 2025最新云南水土保持方案报告品牌top5榜单公布,服务覆盖昆明/曲靖/文山/保山/昭通等地优质公司专业评测及选择指南,助力项目合规落地新生态 - 全局中转站
  • 设置推荐奖励机制实现老带新裂变增长