news 2026/4/23 11:48:52

封装驱动 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/4/23 11:47:59

Speech Seaco Paraformer支持热词吗?关键词增强识别实战

Speech Seaco Paraformer支持热词吗&#xff1f;关键词增强识别实战 1. 热词不是“锦上添花”&#xff0c;而是识别准确率的“关键开关” 你有没有遇到过这样的情况&#xff1a; 会议录音里反复提到“Paraformer”“Seaco”“FunASR”&#xff0c;结果识别出来全是“怕拉佛玛…

作者头像 李华
网站建设 2026/4/23 11:47:14

GPT-OSS-20B部署问题汇总:显存不足解决方案大全

GPT-OSS-20B部署问题汇总&#xff1a;显存不足解决方案大全 1. 为什么GPT-OSS-20B总在报“CUDA out of memory”&#xff1f; 你刚拉起镜像&#xff0c;点开网页界面&#xff0c;输入一句“你好”&#xff0c;还没等响应&#xff0c;控制台就刷出一长串红色报错——最常见、最…

作者头像 李华
网站建设 2026/4/18 5:56:09

YOLOv11遥感图像应用:土地分类检测实战

YOLOv11遥感图像应用&#xff1a;土地分类检测实战 你是否试过用YOLO模型处理卫星图或航拍影像&#xff1f;不是那种街景、车辆、行人检测&#xff0c;而是真正面向广域地表——农田、林地、水体、建筑、裸地……这些类别在遥感图像里边界模糊、纹理相似、尺度多变&#xff0c…

作者头像 李华
网站建设 2026/4/16 10:45:34

Live Avatar电影级画质:cinematic style实现路径

Live Avatar电影级画质&#xff1a;cinematic style实现路径 1. 什么是Live Avatar——不止是数字人&#xff0c;而是电影级表达引擎 Live Avatar不是又一个简单的“说话头”模型。它是阿里联合国内顶尖高校共同开源的实时数字人生成系统&#xff0c;核心目标很明确&#xff…

作者头像 李华
网站建设 2026/4/18 5:33:23

通义千问3-14B部署教程:NVIDIA驱动配置避坑手册

通义千问3-14B部署教程&#xff1a;NVIDIA驱动配置避坑手册 1. 为什么是Qwen3-14B&#xff1f;单卡跑出30B级效果的现实选择 你是不是也遇到过这些情况&#xff1a; 想试大模型&#xff0c;但手头只有一张RTX 4090&#xff0c;装完Qwen2-72B直接OOM&#xff1b;下载了Qwen3-…

作者头像 李华
网站建设 2026/4/10 23:27:32

NewBie-image-Exp0.1省时部署:预下载权重免等待实战案例

NewBie-image-Exp0.1省时部署&#xff1a;预下载权重免等待实战案例 你是不是也经历过这样的时刻&#xff1a;兴冲冲想跑一个动漫生成模型&#xff0c;结果卡在环境配置上两小时&#xff0c;pip install 报错、CUDA 版本不匹配、权重下载到99%断连……更别提还要手动修源码里的…

作者头像 李华