news 2026/6/9 16:23:39

封装驱动 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/6/6 8:59:19

YOLO26训练周期设置:epochs参数选择与过拟合规避指南

YOLO26训练周期设置&#xff1a;epochs参数选择与过拟合规避指南 YOLO26作为最新一代目标检测架构&#xff0c;在精度、速度与部署灵活性上实现了显著突破。但许多用户在实际训练中发现&#xff1a;明明数据质量不错、硬件资源充足&#xff0c;模型却迟迟无法收敛&#xff0c;…

作者头像 李华
网站建设 2026/6/6 13:31:24

一句话打开抖音关注博主,Open-AutoGLM真实案例展示

一句话打开抖音关注博主&#xff0c;Open-AutoGLM真实案例展示 你有没有试过&#xff1a;在手机上一边刷抖音&#xff0c;一边想“要是能直接说一句‘关注这个博主’就自动完成&#xff0c;该多省事&#xff1f;” 现在&#xff0c;这不是设想——而是真实可运行的自动化流程。…

作者头像 李华
网站建设 2026/6/7 12:21:21

FSMN-VAD中文语音适配:专为普通话优化

FSMN-VAD中文语音适配&#xff1a;专为普通话优化 你是否遇到过这样的问题&#xff1a;一段30分钟的会议录音&#xff0c;真正说话的内容可能只有8分钟&#xff0c;其余全是翻页声、咳嗽、空调嗡鸣和长时间停顿&#xff1f;如果直接把整段音频喂给ASR系统&#xff0c;不仅推理…

作者头像 李华
网站建设 2026/6/7 12:28:48

YOLOv10预测超简单:一行命令实现图像检测

YOLOv10预测超简单&#xff1a;一行命令实现图像检测 你有没有试过——刚打开终端&#xff0c;还没写一行训练代码&#xff0c;就卡在了“怎么让模型跑起来”这一步&#xff1f;下载权重慢、环境报错多、配置文件改来改去还是提示ModuleNotFoundError……目标检测本该是“输入…

作者头像 李华
网站建设 2026/6/7 11:16:05

说话人识别实战:CAM++镜像让声纹比对变得超简单

说话人识别实战&#xff1a;CAM镜像让声纹比对变得超简单 1. 为什么声纹比对不再需要写代码和调模型 你有没有遇到过这样的场景&#xff1a; 安保系统要确认来电者是不是本人&#xff0c;却得等工程师跑一趟部署模型&#xff1b;客服质检想批量比对坐席语音是否为同一人&…

作者头像 李华
网站建设 2026/6/9 10:17:41

ESP32引脚图系统学习:I2C与其他信号复用分析

以下是对您提供的博文《ESP32引脚图系统学习&#xff1a;IC与其他信号复用分析》进行 深度润色与专业重构后的终稿 。本次优化严格遵循您的全部要求&#xff1a; ✅ 彻底去除AI痕迹&#xff0c;语言自然、有经验感、带教学温度 ✅ 摒弃所有模板化标题&#xff08;如“引言”…

作者头像 李华