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/5/20 19:57:59

CAM++企业定制化部署:高并发访问性能优化方案

CAM企业定制化部署&#xff1a;高并发访问性能优化方案 1. 为什么企业需要关注CAM的高并发能力 CAM是一个由科哥开发的说话人识别系统&#xff0c;核心能力是判断两段语音是否来自同一说话人&#xff0c;并能提取192维声纹特征向量。它基于达摩院开源模型speech_campplus_sv_…

作者头像 李华
网站建设 2026/5/30 8:19:00

Z-Image-Turbo_UI界面功能测评,这几点真的太实用了

Z-Image-Turbo_UI界面功能测评&#xff0c;这几点真的太实用了 1. 开箱即用&#xff1a;无需部署&#xff0c;直接上手体验AI图像生成 你有没有试过这样的场景&#xff1a;刚下载完一个AI图像工具&#xff0c;结果卡在环境配置、依赖安装、CUDA版本匹配上&#xff0c;折腾两小…

作者头像 李华
网站建设 2026/6/8 6:31:04

fft npainting lama端口冲突解决:lsof命令查杀7860占用进程

fft npainting lama端口冲突解决&#xff1a;lsof命令查杀7860占用进程 1. 问题背景与使用场景 在部署图像修复系统时&#xff0c;经常会遇到一个让人头疼的问题&#xff1a;启动服务失败&#xff0c;提示端口被占用。特别是当你尝试运行 fft npainting lama 这类基于 WebUI …

作者头像 李华
网站建设 2026/6/5 23:01:55

新手避雷!verl常见报错及解决方案汇总

新手避雷&#xff01;verl常见报错及解决方案汇总 verl作为专为大语言模型后训练设计的强化学习框架&#xff0c;凭借其HybridFlow架构、FSDP2集成和3D-HybridEngine等特性&#xff0c;在实际部署和训练中展现出强大能力。但对刚接触强化学习或分布式训练的新手而言&#xff0…

作者头像 李华
网站建设 2026/6/5 20:54:49

CAM++说话人聚类应用案例:客服录音自动分类实现

CAM说话人聚类应用案例&#xff1a;客服录音自动分类实现 1. 为什么客服团队需要说话人聚类&#xff1f; 你有没有遇到过这样的情况&#xff1a;每天收到上百条客服通话录音&#xff0c;却只能靠人工听、手动记、Excel打标签&#xff1f;销售主管想分析“张三”这个坐席的应答…

作者头像 李华
网站建设 2026/6/8 9:08:25

cv_resnet18适合哪些场景?四大典型应用案例详解

cv_resnet18适合哪些场景&#xff1f;四大典型应用案例详解 ResNet18 是一个轻量级但表现稳健的卷积神经网络&#xff0c;在计算机视觉任务中以“小身材、大能量”著称。而基于它构建的 cv_resnet18_ocr-detection 模型&#xff0c;专为文字检测&#xff08;Text Detection&am…

作者头像 李华