应用程序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 # 清理编译产物#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#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; }#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; }#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; }#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; }#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); }排查
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