80. Linux内核定时器实验

news2025/7/19 15:09:01

一、Linux内核定时器原理

1.1、内核时间管理
1、Cortex-M内核使用systick作为系统定时器。
2、硬件定时器、软件定时器,原理是依靠系统定时器来驱动。
3、linux内核频率可以配置,图形化界面配置。
4、重点,HZ表示系统节拍率,
在这里插入图片描述

在这里插入图片描述

1.2、节拍率高低的缺陷

1.3、jiffies

1.4、内核定时器
1、软件定时器不像硬件定时器一样,直接给周期值。设置期满以后的时间点。
2、定时处理函数。
3、内核定时器不是周期性的,一次定时时间到了以后就会关闭,除非重新打开。

定时器是一个很常用的功能,需要周期性处理的工作都要用到定时器。 Linux 内核定时器采用系统时钟来实现,并不是我们在裸机篇中讲解的 PIT 等硬件定时器。 Linux 内核定时器使用很简单,只需要提供超时时间(相当于定时值)和定时处理函数即可,当超时时间到了以后设置的定时处理函数就会执行,和我们使用硬件定时器的套路一样,只是使用内核定时器不需要做一大堆的寄存器初始化工作。在使用内核定时器的时候要注意一点,内核定时器并不是周期性运行的,超时以后就会自动关闭,因此如果想要实现周期性定时,那么就需要在定时处理函数中重新开启定时器。 Linux 内核使用 timer_list 结构体表示内核定时器, timer_list 定义在文件include/linux/timer.h 中,定义如下(省略掉条件编译):
在这里插入图片描述

void init_timer(struct timer_list *timer)
函数参数和返回值含义如下: timer:要初始化定时器。返回值: 没有返回值。
2、 add_timer 函数
add_timer 函数用于向 Linux 内核注册定时器,使用 add_timer 函数向内核注册定时器以后,定时器就会开始运行,函数原型如下:
void add_timer(struct timer_list *timer)函数参数和返回值含义如下: timer:要注册的定时器。
返回值: 没有返回值。
3、 del_timer 函数
del_timer 函数用于删除一个定时器,不管定时器有没有被激活,都可以使用此函数删除。在多处理器系统上,定时器可能会在其他的处理器上运行,因此在调用 del_timer 函数删除定时器之前要先等待其他处理器的定时处理器函数退出。 del_timer 函数原型如下:
int del_timer(struct timer_list * timer)函数参数和返回值含义如下: timer:要删除的定时器。
返回值: 0,定时器还没被激活; 1,定时器已经激活。
4、 del_timer_sync 函数
del_timer_sync 函数是 del_timer 函数的同步版,会等待其他处理器使用完定时器再删除, del_timer_sync 不能使用在中断上下文中。 del_timer_sync 函数原型如下所示:
int del_timer_sync(struct timer_list *timer)函数参数和返回值含义如下: timer:要删除的定时器。
返回值: 0,定时器还没被激活; 1,定时器已经激活。
5、 mod_timer 函数
mod_timer 函数用于修改定时值,如果定时器还没有激活的话, mod_timer 函数会激活定时器!函数原型如下:
int mod_timer(struct timer_list *timer, unsigned long expires)函数参数和返回值含义如下: timer:要修改超时时间(定时值)的定时器。expires:修改后的超时时间。
返回值: 0,调用 mod_timer 函数前定时器未被激活; 1,调用 mod_timer 函数前定时器已被激活。
关于内核定时器常用的 API 函数就讲这些,内核定时器一般的使用流程如下所示:
在这里插入图片描述

二、编写试验驱动

1、定义一个定时器,结构体timer_list

2、应用ioctl函数-》unlocked_ioctl和compat_ioctl。

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long)
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
ioctl的命令是自己定义的,但是要符合linux规则。构建命令:
#define _IO(type,nr) /没有参数的命令
#define _IOR(type,nr,size) //该命令是从驱动读取数据
#define _IOW(type,nr,size) //该命令是向驱动写入数据
#define _IOWR(type,nr,size) //双向数据传输
type是幻数,nr是序号,size是大小。

//Makefile

KERNELDIR := /home/mk/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga
CURRENT_PATH := $(shell pwd)

obj-m := timer.o

build: kernel_modules

kernel_modules:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

//timer.c

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
/***************************************************************
文件名     : timer.c
版本      : V1.0
描述      : Linux内核定时器实验
其他      : 无
***************************************************************/
#define TIMER_CNT       1       /* 设备号个数    */
#define TIMER_NAME      "timer" /* 名字       */
#define CLOSE_CMD       (_IO(0XEF, 0x1))    /* 关闭定时器 */
#define OPEN_CMD        (_IO(0XEF, 0x2))    /* 打开定时器 */
#define SETPERIOD_CMD   (_IO(0XEF, 0x3))    /* 设置定时器周期命令 */
#define LEDON           1       /* 开灯 */
#define LEDOFF          0       /* 关灯 */

/* timer设备结构体 */
struct timer_dev{
    dev_t devid;            /* 设备号   */
    struct cdev cdev;       /* cdev     */
    struct class *class;    /* 类        */
    struct device *device;  /* 设备    */
    int major;              /* 主设备号   */
    int minor;              /* 次设备号   */
    struct device_node  *nd; /* 设备节点 */
    int led_gpio;           /* key所使用的GPIO编号        */
    int timeperiod;         /* 定时周期,单位为ms */
    struct timer_list timer;/* 定义一个定时器*/
    spinlock_t lock;        /* 定义自旋锁 */
};

struct timer_dev timerdev;  /* timer设备 */

/*
 * @description : 初始化LED灯IO,open函数打开驱动的时候
 *                初始化LED灯所使用的GPIO引脚。
 * @param       : 无
 * @return      : 无
 */
static int led_init(void)
{
    int ret = 0;

    timerdev.nd = of_find_node_by_path("/gpioled");
    if (timerdev.nd== NULL) {
        return -EINVAL;
    }

    timerdev.led_gpio = of_get_named_gpio(timerdev.nd ,"led-gpio", 0);
    if (timerdev.led_gpio < 0) {
        printk("can't get led\r\n");
        return -EINVAL;
    }
    
    /* 初始化led所使用的IO */
    gpio_request(timerdev.led_gpio, "led");     /* 请求IO     */
    ret = gpio_direction_output(timerdev.led_gpio, 1);
    if(ret < 0) {
        printk("can't set gpio!\r\n");
    }
    return 0;
}

/*
 * @description     : 打开设备
 * @param - inode   : 传递给驱动的inode
 * @param - filp    : 设备文件,file结构体有个叫做private_data的成员变量
 *                    一般在open的时候将private_data指向设备结构体。
 * @return          : 0 成功;其他 失败
 */
static int timer_open(struct inode *inode, struct file *filp)
{
    int ret = 0;
    filp->private_data = &timerdev; /* 设置私有数据 */

    timerdev.timeperiod = 1000;     /* 默认周期为1s */
    ret = led_init();               /* 初始化LED IO */
    if (ret < 0) {
        return ret;
    }

    return 0;
}

/*
 * @description     : ioctl函数,
 * @param - filp    : 要打开的设备文件(文件描述符)
 * @param - cmd     : 应用程序发送过来的命令
 * @param - arg     : 参数
 * @return          : 0 成功;其他 失败
 */
static long timer_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct timer_dev *dev =  (struct timer_dev *)filp->private_data;
    int timerperiod;
    unsigned long flags;
    
    switch (cmd) {
        case CLOSE_CMD:     /* 关闭定时器 */
            del_timer_sync(&dev->timer);
            break;
        case OPEN_CMD:      /* 打开定时器 */
            spin_lock_irqsave(&dev->lock, flags);
            timerperiod = dev->timeperiod;
            spin_unlock_irqrestore(&dev->lock, flags);
            mod_timer(&dev->timer, jiffies + msecs_to_jiffies(timerperiod));
            break;
        case SETPERIOD_CMD: /* 设置定时器周期 */
            spin_lock_irqsave(&dev->lock, flags);
            dev->timeperiod = arg;
            spin_unlock_irqrestore(&dev->lock, flags);
            mod_timer(&dev->timer, jiffies + msecs_to_jiffies(arg));
            break;
        default:
            break;
    }
    return 0;
}

/* 设备操作函数 */
static struct file_operations timer_fops = {
    .owner = THIS_MODULE,
    .open = timer_open,
    .unlocked_ioctl = timer_unlocked_ioctl,
};

/* 定时器回调函数 */
void timer_function(unsigned long arg)
{
    struct timer_dev *dev = (struct timer_dev *)arg;
    static int sta = 1;
    int timerperiod;
    unsigned long flags;

    sta = !sta;     /* 每次都取反,实现LED灯反转 */
    gpio_set_value(dev->led_gpio, sta);
    
    /* 重启定时器 */
    spin_lock_irqsave(&dev->lock, flags);
    timerperiod = dev->timeperiod;
    spin_unlock_irqrestore(&dev->lock, flags);
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod)); 
 }

/*
 * @description : 驱动入口函数
 * @param       : 无
 * @return      : 无
 */
static int __init timer_init(void)
{
    /* 初始化自旋锁 */
    spin_lock_init(&timerdev.lock);

    /* 注册字符设备驱动 */
    /* 1、创建设备号 */
    if (timerdev.major) {       /*  定义了设备号 */
        timerdev.devid = MKDEV(timerdev.major, 0);
        register_chrdev_region(timerdev.devid, TIMER_CNT, TIMER_NAME);
    } else {                        /* 没有定义设备号 */
        alloc_chrdev_region(&timerdev.devid, 0, TIMER_CNT, TIMER_NAME); /* 申请设备号 */
        timerdev.major = MAJOR(timerdev.devid); /* 获取分配号的主设备号 */
        timerdev.minor = MINOR(timerdev.devid); /* 获取分配号的次设备号 */
    }
    
    /* 2、初始化cdev */
    timerdev.cdev.owner = THIS_MODULE;
    cdev_init(&timerdev.cdev, &timer_fops);
    
    /* 3、添加一个cdev */
    cdev_add(&timerdev.cdev, timerdev.devid, TIMER_CNT);

    /* 4、创建类 */
    timerdev.class = class_create(THIS_MODULE, TIMER_NAME);
    if (IS_ERR(timerdev.class)) {
        return PTR_ERR(timerdev.class);
    }

    /* 5、创建设备 */
    timerdev.device = device_create(timerdev.class, NULL, timerdev.devid, NULL, TIMER_NAME);
    if (IS_ERR(timerdev.device)) {
        return PTR_ERR(timerdev.device);
    }
    
    /* 6、初始化timer,设置定时器处理函数,还未设置周期,所有不会激活定时器 */
    init_timer(&timerdev.timer);
    timerdev.timer.function = timer_function;
    timerdev.timer.data = (unsigned long)&timerdev;
    return 0;
}

/*
 * @description : 驱动出口函数
 * @param       : 无
 * @return      : 无
 */
static void __exit timer_exit(void)
{
    
    gpio_set_value(timerdev.led_gpio, 1);   /* 卸载驱动的时候关闭LED */
    del_timer_sync(&timerdev.timer);        /* 删除timer */
#if 0
    del_timer(&timerdev.tiemr);
#endif

    /* 注销字符设备驱动 */
    cdev_del(&timerdev.cdev);/*  删除cdev */
    unregister_chrdev_region(timerdev.devid, TIMER_CNT); /* 注销设备号 */

    device_destroy(timerdev.class, timerdev.devid);
    class_destroy(timerdev.class);
}

module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("mk");

//timerApp.c

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "linux/ioctl.h"
/***************************************************************
文件名     : timerApp.c
版本      : V1.0
描述      : 定时器测试应用程序
其他      : 无
使用方法    :./timertest /dev/timer 打开测试App
***************************************************************/

/* 命令值 */
#define CLOSE_CMD       (_IO(0XEF, 0x1))    /* 关闭定时器 */
#define OPEN_CMD        (_IO(0XEF, 0x2))    /* 打开定时器 */
#define SETPERIOD_CMD   (_IO(0XEF, 0x3))    /* 设置定时器周期命令 */

/*
 * @description     : main主程序
 * @param - argc    : argv数组元素个数
 * @param - argv    : 具体参数
 * @return          : 0 成功;其他 失败
 */
int main(int argc, char *argv[])
{
    int fd, ret;
    char *filename;
    unsigned int cmd;
    unsigned int arg;
    unsigned char str[100];

    if (argc != 2) {
        printf("Error Usage!\r\n");
        return -1;
    }

    filename = argv[1];

    fd = open(filename, O_RDWR);
    if (fd < 0) {
        printf("Can't open file %s\r\n", filename);
        return -1;
    }

    while (1) {
        printf("Input CMD:");
        ret = scanf("%d", &cmd);
        if (ret != 1) {             /* 参数输入错误 */
            gets(str);              /* 防止卡死 */
        }

        if(cmd == 1)                /* 关闭LED灯 */
            cmd = CLOSE_CMD;
        else if(cmd == 2)           /* 打开LED灯 */
            cmd = OPEN_CMD;
        else if(cmd == 3) {
            cmd = SETPERIOD_CMD;    /* 设置周期值 */
            printf("Input Timer Period:");
            ret = scanf("%d", &arg);
            if (ret != 1) {         /* 参数输入错误 */
                gets(str);          /* 防止卡死 */
            }
        }
        ioctl(fd, cmd, arg);        /* 控制定时器的打开和关闭 */   
    }

    close(fd);
}

三、测试

输入如下命令编译出驱动模块文件: make -j32
编译成功以后就会生成一个名为“timer.ko”的驱动模块文件。
输入如下命令编译测试 timerApp.c 这个测试程序:
arm-linux-gnueabihf-gcc timerApp.c -o timerApp编译成功以后就会生成 timerApp 这个应用程序。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2327024.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++类与对象(上):从入门到实践

目录 一、引言 二、面向过程和面向对象初步认识 2.1 面向过程编程 2.2 面向对象编程 三、类的引入 四、类的定义 4.1 定义格式 4.2 定义方式 4.3 成员变量命名规则建议 五、类的访问限定符及封装 5.1 访问限定符 5.2 封装 六、类的作用域 七、类的实例化 7.1 概念…

Lumerical ------ Edge coupler design

Lumerical ------ Edge coupler design 引言正文无 Si Substrate 的仿真步骤有 Si Substrate 的仿真步骤引言 本文,我们将使用官方提供的 Edge coupler 设计教程,但是中间会带有作者本人的设计的感悟。 正文 无 Si Substrate 的仿真步骤 打开 Edge_Coupler_No_Substrate.l…

大语言模型本质上还是自动化,而不是智能化

大语言模型本质上仍然是自动化或高级自动化&#xff0c;而非真正的智能化&#xff0c;原因可以从以下几个方面进行分析&#xff1a;1、自动化与智能化的本质区别自动化&#xff1a;大语言模型通过预训练和微调&#xff0c;基于大量数据和规则生成输出。它的行为是基于输入数据的…

python数据结构——链表、栈、队列

一、思维梳理&#xff1a; 二、双向循环链表&#xff1a; class Node:def __init__(self,data):self.data dataself.next Noneself.prev Noneclass DoubleLink:def __init__(self):self.size 0self.head Nonedef is_empty(self):return self.size 0def add_end(self,dat…

centos操作系统如何更换yum镜像源

CentOS Linux 是一个免费提供的、社区支持的Linux发行版,由CentOS项目社区贡献者开发、分发和维护。2020年CentOS项目宣布将把全部投资转移到CentOS Stream,作为即将发布的 Red Hat Enterprise Linux版本的上游开发平台。因此,CentOS Linux更新和发布将在2021年至2024年期间…

【Linux篇】自主Shell命令行解释器

&#x1f4cc; 个人主页&#xff1a; 孙同学_ &#x1f527; 文章专栏&#xff1a;Liunx &#x1f4a1; 关注我&#xff0c;分享经验&#xff0c;助你少走弯路&#xff01; 文章目录 1. 获取用户名的接口2. 等待用户输入接口3. 将上述代码进行面向对象式的封装4. 命令行解析5.…

我的创作纪念日-一周年

目录 机缘 收获 日常 成就 憧憬 机缘 时光荏苒&#xff0c;转行计算机已经是第5个年头了。从Python入门&#xff0c;到C入土&#xff0c;兜兜转转&#xff0c;发现自己也只是初窥门径&#xff0c;习得皮毛。我从6年前开始潜水CSDN&#xff0c;学习各路大佬的技术经验&…

多线程代码案例 - 1

目录 单例模式 1. 饿汉模式 2. 懒汉模式 单例模式与多线程 问题1 问题2 问题3 完&#xff01; 单例模式 单例模式是一种设计模式。 设计模式&#xff0c;是我们在编写代码时候的一种软性的规定&#xff0c;也就是说&#xff0c;我们遵守设计模式&#xff0c;代码的下限…

开发体育赛事直播系统主播认证功能技术实现方案

该体育直播系统系统由东莞梦幻网络科技开发&#xff0c;使用 ThinkPHP 作为后端&#xff0c;Vue.js 作为 PC/H5 端框架&#xff0c;Java 和 Objective-C 分别用于安卓和 iOS 开发。 1、前端实现 (Vue.js) <template><div class"anchor-certification">…

国产三维CAD「皇冠CAD」在汽车零部件领域建模教程:刹车片

本教程深度融合三维皇冠CAD&#xff08;CrownCAD&#xff09;的MBD&#xff08;Model-Based Definition&#xff09;设计理念&#xff0c;通过参数化建模、智能约束管理、动态装配验证等功能&#xff0c;实现数据驱动设计&#xff0c;精准解决了汽车制动系统中精密制动组件的设…

SpringMvc获取请求数据

基本参数 RequestMapping("save5") ResponseBody public User save5(String name, int age) {User user new User();user.setName(name);user.setAge(age);return user; } 在url中将name与age进行编写&#xff0c;通过框架可以提取url中的name与age&#xff0c;这…

大语言模型开发框架——LangChain

什么是LangChain LangChain是一个开发由语言模型驱动的应用程序的框架&#xff0c;它提供了一套工具、组件和接口&#xff0c;可以简化构建高级语言模型应用程序的过程。利用LangChain可以使应用程序具备两个能力&#xff1a; 上下文感知 将语言模型与上下文&#xff08;提示…

机器学习的一百个概念(7)独热编码

前言 本文隶属于专栏《机器学习的一百个概念》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见[《机器学习的一百个概念》 ima 知识库 知识库广场搜索&…

从实用的角度聊聊Linux下文本编辑器VIM

本文从实用的角度聊聊Vim的常用命令。何为实用&#xff1f;我举个不实用的例子大家就明白了&#xff0c;用vim写代码。;) “vim是从 vi 发展出来的一个文本编辑器。代码补全、编译及错误跳转等方便编程的功能特别丰富&#xff0c;在程序员中被广泛使用&#xff0c;和Emacs并列成…

佳能imageRUNNER 2206N基本参数及管理员密码

基本参数&#xff1a; 产品类型 激光数码复合机 颜色类型 黑白 涵盖功能 复印/打印/扫描 速度类型 低速 最大原稿尺寸 A3 复印/打印方式 激光静电转印方式 感光材料 OPC 显影系统 干式单组分显影 定影…

社交类 APP 设计:打造高用户粘性的界面

在当今数字化时代&#xff0c;社交类APP已成为人们日常生活中不可或缺的一部分。然而&#xff0c;随着市场竞争的加剧&#xff0c;如何通过设计提升用户粘性成为社交类APP成功的关键。本文将从设计的关键要素、用户界面优化、功能创新、个性化体验以及持续优化等方面&#xff0…

数据编排与Dagster:解锁现代数据管理的核心工具

在数据驱动的时代&#xff0c;如何高效管理复杂的数据管道、确保数据质量并实现团队协作&#xff1f;本文深入探讨数据编排的核心概念&#xff0c;解析其与传统编排器的差异&#xff0c;并聚焦开源工具Dagster如何以“资产为中心”的理念革新数据开发流程&#xff0c;助力企业构…

Jmeter的压测使用

Jmeter基础功能回顾 一、创建Jmeter脚本 1、录制新建 &#xff08;1&#xff09;适用群体&#xff1a;初学者 2、手动创建 &#xff08;1&#xff09;需要了解Jmeter的常用组件 元件&#xff1a;多个类似功能组件的容器&#xff08;类似于类&#xff09; 各元件作用 组件…

kubernetes》》k8s》》Deployment》》ClusterIP、LoadBalancer、Ingress 内部访问、外边访问

Nginx部署 K8s 集群内外访问服务的方式 节点 Kubernetes 集群中的服务器&#xff08;指单台&#xff09; 集群 Kubernetes 管理的一组服务器的集合 边界路由器 为局域网和Internet路由数据包的路由器&#xff0c;执行防火墙保护局域网络 集群网络 遵循Kubernetes网络模型实现集…

Transformer 通关秘籍8:词向量如何表示近义词?

上一节已经完成了 token 到词向量的转换。那么&#xff0c;使用转换后的词嵌入向量便可以表示 token 之间的语义了吗&#xff1f;便可以表示两个单词是否是近义词&#xff0c;是否是反义词了吗&#xff1f; 是的。 接下来先通过一个例子&#xff0c;来直观地理解一下词嵌入向…