news 2026/2/3 16:16:19

封装驱动 API 接口实验

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
封装驱动 API 接口实验

应用程序app_ioctl.c

#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #define TIME_OPEN _IO('L',0)//定时器打开 #define TIME_CLOSE _IO('L',1)//定时器关闭 #define TIME_SET _IOW('L',2,int)//定时时间设置 int main(int argc,char *argv[]){ int fd; fd = open("/dev/test",O_RDWR,0777);//打开 test 节点 if(fd < 0){ printf("file open error \n"); } ioctl(fd,TIME_SET,1000);//设置定时时间 1 秒 ioctl(fd,TIME_OPEN);//打开定时器 sleep(3); ioctl(fd,TIME_SET,3000);//设置定时时间 3 秒 sleep(7); ioctl(fd,TIME_CLOSE);;//关闭定时器 close(fd); }

驱动程序ioctl.c

#include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/kdev_t.h> #include <linux/uaccess.h> #include <linux/timer.h> #define TIMER_OPEN _IO('L',0) #define TIMER_CLOSE _IO('L',1) #define TIMER_SET _IOW('L',2,int) struct device_test{ dev_t dev_num; //设备号 int major ; //主设备号 int minor ; //次设备号 struct cdev cdev_test; // cdev struct class *class; //类 struct device *device; //设备 int counter; }; static struct device_test dev1; static void fnction_test(struct timer_list *t);//定义 function_test 定时功能函数 DEFINE_TIMER(timer_test,fnction_test);//定义一个定时器 void fnction_test(struct timer_list *t) { printk("this is fnction_test\n"); mod_timer(&timer_test,jiffies_64 + msecs_to_jiffies(dev1.counter));//使用 mod_timer 函数重新设置定时时间 } static int cdev_test_open(struct inode *inode, struct file *file) { file->private_data=&dev1;//设置私有数据 return 0; } static int cdev_test_release(struct inode *inode, struct file *file) { file->private_data=&dev1;//设置私有数据 return 0; } static long cdev_test_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct device_test *test_dev = (struct device_test *)file->private_data;//设置私有数据 switch(cmd){ case TIMER_OPEN: add_timer(&timer_test);//添加一个定时器 break; case TIMER_CLOSE: del_timer(&timer_test);//删除一个定时器 break; case TIMER_SET: test_dev->counter = arg; timer_test.expires = jiffies_64 + msecs_to_jiffies(test_dev->counter);//设置定时时间 break; default: break; } return 0; } /*设备操作函数*/ struct file_operations cdev_test_fops = { .owner = THIS_MODULE, //将 owner 字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块 .open = cdev_test_open, .release = cdev_test_release, .unlocked_ioctl = cdev_test_ioctl, }; static int __init timer_dev_init(void) //驱动入口函数 { /*注册字符设备驱动*/ int ret; /*1 创建设备号*/ ret = alloc_chrdev_region(&dev1.dev_num, 0, 1, "alloc_name"); //动态分配设备号 if(ret < 0) { goto err_chrdev; } printk("alloc_chrdev_region is ok\n"); dev1.major = MAJOR(dev1.dev_num); //获取主设备号 dev1.minor = MINOR(dev1.dev_num); //获取次设备号 printk("major is %d \r\n", dev1.major); //打印主设备号 printk("minor is %d \r\n", dev1.minor); //打印次设备号 /*2 初始化 cdev*/ dev1.cdev_test.owner = THIS_MODULE; cdev_init(&dev1.cdev_test, &cdev_test_fops); /*3 添加一个 cdev,完成字符设备注册到内核*/ ret = cdev_add(&dev1.cdev_test, dev1.dev_num, 1); if(ret<0) { goto err_chr_add; } /*4 创建类*/ dev1. class = class_create(THIS_MODULE, "test"); if(IS_ERR(dev1.class)) { ret=PTR_ERR(dev1.class); goto err_class_create; } /*5 创建设备*/ dev1.device = device_create(dev1.class, NULL, dev1.dev_num, NULL, "test"); if(IS_ERR(dev1.device)) { ret=PTR_ERR(dev1.device); goto err_device_create; } return 0; err_device_create: class_destroy(dev1.class); //删除类 err_class_create: cdev_del(&dev1.cdev_test); //删除 cdev err_chr_add: unregister_chrdev_region(dev1.dev_num, 1); //注销设备号 err_chrdev: return ret; } static void __exit timer_dev_exit(void) //驱动出口函数 { /*注销字符设备*/ unregister_chrdev_region(dev1.dev_num, 1); //注销设备号 cdev_del(&dev1.cdev_test); //删除 cdev device_destroy(dev1.class, dev1.dev_num); //删除设备 class_destroy(dev1.class); //删除类 } module_init(timer_dev_init); module_exit(timer_dev_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("topeet");

Makefile

# 移除ARM64架构和交叉编译器配置,默认使用x86本地编译环境 # export ARCH=arm64 # 注释掉ARM64架构设置 # export CROSS_COMPILE=aarch64-linux-gnu- # 注释掉交叉编译器前缀 obj-m += ioctl.o # 驱动源文件名称,保持不变 # 修改内核目录为x86系统的本地内核源码/头文件目录 # 方案1:使用系统当前运行内核的头文件(推荐,无需手动下载内核源码) KDIR := /lib/modules/$(shell uname -r)/build # 方案2:如果你有本地下载的x86内核源码,替换成对应的路径,例如: # KDIR := /home/yourname/linux-x86-kernel # 根据实际路径修改 PWD ?= $(shell pwd) all: make -C $(KDIR) M=$(PWD) modules # 本地编译x86内核模块 clean: make -C $(KDIR) M=$(PWD) clean # 清理编译产物

封装驱动 API 接口
一般情况下,应用程序都是由专业的应用工程师来进行编写的,在使用ioctl命令时,应
用工程师无需关心ioctl命令的具体实现,所以对于应用程序中的ioctl命令封装是一件必然的
事情。
在工程代码mkdir一个app文件夹
1.封装步骤1
首先来编写整体库文件timerlib.h
#ifndef _TIMELIB_H_ #define _TIMELIB_H_ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #define TIMER_OPEN _IO('L',0) #define TIMER_CLOSE _IO('L',1) #define TIMER_SET _IOW('L',2,int) int dev_open();//定义设备打开函数 int timer_open(int fd);//定义定时器打开函数 int timer_close(int fd);//定义定时器关闭函数 int timer_set(int fd,int arg);//定义设置计时时间函数 #endif
9-11行使用合成宏定义了三个ioctl命令,分别代表定时器打开、定时器关闭、定时时
间设置。
在第12-15行定义了四个功能函数,所代表的功能分别为设备打开、定时器打开、定时器
关闭、定时时间设置。
2.封装步骤2
接下来创建每个功能函数的c文件,编写dev_open()数对应的dev_open.c文件
#include <stdio.h> #include "timerlib.h" int dev_open() { int fd; fd = open("/dev/test",O_RDWR,0777); if(fd < 0){ printf("file open error \n"); } return fd; }
编写定时器打开函数对应的timeropen.c文件
#include <stdio.h> #include "timerlib.h" int timer_open(int fd) { int ret; ret = ioctl(fd,TIMER_OPEN); if(ret < 0){ printf("ioctl open error \n"); return -1; } return ret; }
编写定时器关闭函数对应的timerclose.c文件
#include <stdio.h> #include "timerlib.h" int timer_close(int fd) { int ret; ret = ioctl(fd,TIMER_CLOSE); if(ret < 0){ printf("ioctl close error \n"); return -1; } return ret; }
编写定时时间设置函数对应的timerset.c文件
#include <stdio.h> #include "timerlib.h" int timer_set(int fd,int arg) { int ret; ret = ioctl(fd,TIMER_SET,arg); if(ret < 0){ printf("ioctl error \n"); return -1; } return ret; }
最后编写测试程序ioctl.c文件
#include <stdio.h> #include "timerlib.h" int main(int argc,char *argv[]){ int fd; fd = dev_open(); timer_set(fd,1000); timer_open(fd); sleep(3); timer_set(fd,3000); sleep(7); timer_close(fd); close(fd); }

通过测试程序可以看出封装后的应用程序不再直接调用繁杂的ioctl宏指令,而是直接通
过简单的函数接口来完成设备操作,这样可以提升程序的可读性和可维护性。
首先使用gcc -c dev_open.cgcc -c timer*.c命令将存
放功能函数的c文件编译成.o文件

然 后 使 用ar rcs libtime.a timer*.oar rcs libopen.a
dev_open.o命令将相应的.o文件编译成.a静态库(这里要注意库的名称都以lib开头)
最后使用gcc ioctl.c -L./ -ltime -lopen命令对ioctl.c进行交叉编译

排查

libopen.a为空:执行ar -t libopen.a无输出,说明该静态库未打包任何.o文件,缺少dev_open.o,导致链接时找不到dev_open函数实现;

重新打包libopen.a(加入dev_open.o

# 核心命令:将dev_open.o打包进libopen.a(rcs参数:创建/插入/生成符号索引) ar rcs libopen.a dev_open.o

执行sudo ./ioctl
dmesg
可以看到前面三个打印信息间隔为1秒钟,后面三个打印信息间隔为3秒钟,由此可见使
用封装后的ioctl命令也可以实现控制定时器的功能,说明封装API接口成功,最后使用rmmod
ioctl_timer.ko命令卸载驱动模块

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/31 17:39:53

Llama3-8B语音助手后端:ASR+NLP集成方案

Llama3-8B语音助手后端&#xff1a;ASRNLP集成方案 1. 为什么选择Llama3-8B作为语音助手核心引擎 语音助手的后端能力&#xff0c;本质上是“听懂想清楚说准确”三个环节的闭环。其中&#xff0c;“想清楚”这一步——也就是自然语言理解与生成&#xff08;NLP&#xff09;—…

作者头像 李华
网站建设 2026/2/2 19:45:02

新手福音!PyTorch-2.x-Universal-Dev-v1.0让AI训练变得如此简单

新手福音&#xff01;PyTorch-2.x-Universal-Dev-v1.0让AI训练变得如此简单 1. 为什么说这是新手真正需要的PyTorch环境&#xff1f; 你是不是也经历过这些时刻&#xff1f; 刚装好PyTorch&#xff0c;发现CUDA版本不匹配&#xff0c;GPU用不上&#xff1b; 想跑个图像分类de…

作者头像 李华
网站建设 2026/1/31 16:40:24

unet卡通化支持哪些格式?JPG/PNG/WEBP实测对比

UNet人像卡通化支持哪些格式&#xff1f;JPG/PNG/WEBP实测对比 你是不是也遇到过这样的问题&#xff1a;明明选了一张特别满意的照片&#xff0c;想转成卡通风格发朋友圈或做头像&#xff0c;结果导出后发现画质糊了、颜色发灰&#xff0c;或者文件大得根本发不出去&#xff1…

作者头像 李华
网站建设 2026/2/2 2:41:15

target_modules=all-linear是什么意思?LoRA配置解读

target_modulesall-linear 是什么意思&#xff1f;LoRA 配置解读 在微调大语言模型时&#xff0c;你可能见过类似 --target_modules all-linear 这样的参数。它不像 --lora_rank 或 --learning_rate 那样直观&#xff0c;却直接决定了 LoRA 微调“改哪里”——是只动注意力层&…

作者头像 李华
网站建设 2026/1/31 17:06:48

YOLO26数据集路径错误?data.yaml修改实战指导

YOLO26数据集路径错误&#xff1f;data.yaml修改实战指导 你是不是也遇到过这样的情况&#xff1a;刚启动YOLO26训练镜像&#xff0c;满怀期待地运行train.py&#xff0c;结果终端突然弹出一串红色报错——FileNotFoundError: No such file or directory: datasets/coco128/tr…

作者头像 李华
网站建设 2026/1/30 8:34:25

FSMN VAD部署教程:从零开始搭建语音检测服务

FSMN VAD部署教程&#xff1a;从零开始搭建语音检测服务 1. 什么是FSMN VAD&#xff1f;一句话说清它的价值 你有没有遇到过这样的问题&#xff1a;手头有一段几十分钟的会议录音&#xff0c;但里面夹杂着大量静音、咳嗽、翻纸声&#xff0c;想提取真正说话的部分却要手动拖进…

作者头像 李华