中断的打印和定时中断

定时中断

定时中断是计算机系统中的一种事件触发机制,它允许系统按照预定的时间间隔执行特定的任务,定时中断通常由硬件定时器或系统时钟生成,以确保系统在不同的时间点执行特定的操作。用于演示如何使用高分辨率定时器(hrtimer)实现定时中断。
定时中断的用途为:时间管理,调度和多任务处理,设备管理,性能监控,定时任务。

下面是一段定时中断的代码
这段代码演示了如何创建和使用高分辨率定时器来实现定时中断处理。当模块加载时,它初始化一个定时器,设置了1秒的触发间隔,然后定时器每1秒触发一次中断处理函数,该函数在触发时输出一条信息。在模块卸载时,定时器被停止。可以根据需求修改出发间隔和中断处理逻辑。

下面是代码的分析

代码包含了一些必要的Linux内核头文件,以便访问内核的函数和数据结构,

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>

模块信息:在模块代码的顶部,有一些宏和字符串,用于指定模块的许可证、作者、描述和版本等信息。这些信息在加载模块时会在系统日志中显示。

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Linux Kernel High-Resolution Timer Interrupt Example");
MODULE_VERSION("0.1");

定义一些全局变量 my_timer 和 interval,用于管理高分辨率定时器和定时器触发的间隔。

static struct hrtimer my_timer;
static ktime_t interval;

定时器中断处理函数
timer_callback 是一个定时器中断处理函数。当定时器到期时,该函数将被调用。会打印一条信息,然后通过 hrtimer_forward 函数重新设置定时器以在未来再次触发。

static enum hrtimer_restart timer_callback(struct hrtimer *timer)
{
    printk(KERN_INFO "Timer interrupt occurred\n");
    // 在这里添加您的处理逻辑

    // 重新设置定时器以在未来触发
    hrtimer_forward(timer, timer->_softexpires, interval);
    return HRTIMER_RESTART;
}

模块初始化函数:timer_init 是模块的初始化函数。它在模块加载时被调用。在此函数中,定时器被初始化,其触发时间被设置为1秒,并使用 hrtimer_start 启动定时器。


static int __init timer_init(void)
{
    // 初始化定时器
    ktime_t now = ktime_get();
    interval = ktime_set(1, 0); // 设置为1秒

    hrtimer_init(&my_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    my_timer.function = timer_callback;

    // 启动定时器
    hrtimer_start(&my_timer, now + interval, HRTIMER_MODE_REL);

    printk(KERN_INFO "High-Resolution Timer interrupt module loaded\n");
    return 0;
}

模块退出函数:timer_exit 是模块的退出函数。它在模块卸载时被调用。在此函数中,定时器被取消(停止)以确保在卸载模块时不再触发定时中断

static void __exit timer_exit(void)
{
    // 停止定时器
    hrtimer_cancel(&my_timer);

    printk(KERN_INFO "High-Resolution Timer interrupt module unloaded\n");
}

模块初始化和退出宏:最后,使用 module_init 和 module_exit 宏将初始化和退出函数与模块关联起来。这些宏告诉内核在加载和卸载模块时应该调用哪些函数

module_init(timer_init);
module_exit(timer_exit);

Makefile文件如下

obj-m += dingshi.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

完整代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Linux Kernel High-Resolution Timer Interrupt Example");
MODULE_VERSION("0.1");

static struct hrtimer my_timer;
static ktime_t interval;

// 定时器中断处理函数
static enum hrtimer_restart timer_callback(struct hrtimer *timer)
{
    printk(KERN_INFO "Timer interrupt occurred\n");
    // 在这里添加您的处理逻辑

    // 重新设置定时器以在未来触发
    hrtimer_forward(timer, timer->_softexpires, interval);
    return HRTIMER_RESTART;
}

static int __init timer_init(void)
{
    // 初始化定时器
    ktime_t now = ktime_get();
    interval = ktime_set(1, 0); // 设置为1秒

    hrtimer_init(&my_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    my_timer.function = timer_callback;

    // 启动定时器
    hrtimer_start(&my_timer, now + interval, HRTIMER_MODE_REL);

    printk(KERN_INFO "High-Resolution Timer interrupt module loaded\n");
    return 0;
}

static void __exit timer_exit(void)
{
    // 停止定时器
    hrtimer_cancel(&my_timer);

    printk(KERN_INFO "High-Resolution Timer interrupt module unloaded\n");
}

module_init(timer_init);
module_exit(timer_exit);

运行后中断的情况
在这里插入图片描述

中断的打印

中断上半部与下半部
上半部:上半部就是中断处理函数,那些处理过程比较快,不会占用很长时间的处理就可以放在上半部完成。
下半部:如果中断处理过程比较耗时,那么就将这些比较耗时的代码提出来,交给下半部去执行,这样中断处理函数就会快进快出。

使用:
①、如果要处理的内容不希望被其他中断打断,那么可以放到上半部。
②、如果要处理的任务对时间敏感,可以放到上半部。
③、如果要处理的任务与硬件有关,可以放到上半部
④、除了上述三点以外的其他任务,优先考虑放到下半部。

中断打印上半部分

演示了如何注册中断处理程序,并在中断发生时执行相应的操作。模块参数 irq 和 devname 允许用户在加载模块时指定中断号和设备名称。这个模块的主要功能是在中断事件发生时打印一些信息。

下面是代码的分析
头文件包含:代码包含了一些必要的Linux内核头文件,以便访问内核的函数和数据结构

# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/interrupt.h>

代码声明了两个模块参数,irq 和 devname,用于在模块加载时接收用户指定的参数

static int irq;			//irq号		
static char * devname;		//设备名称	

这两个是用来让我们在命令行传入参数

module_param(irq,int,0644);
module_param(devname,charp,0644);    //这里charp相当于char*,是字符指针

定义了一个结构体 myirq,它包含一个整数 devid,该结构体主要用于共享 IRQ(中断请求)

struct myirq
{
    int devid;     //这个主要用在共享irq中
};

struct myirq mydev={1119};

中断处理函数,当中断事件发生时,该函数会被调用。它接收两个参数,一个是 IRQ 号,另一个是一个指向 myirq 结构体的指针。在函数内部,它打印了一些信息,包括接收到的 IRQ 号和 devid。然后,它返回 IRQ_HANDLED 表示中断已经处理。

static irqreturn_t myirq_handler(int irq,void * dev)
{
    struct myirq mydev;
    static int count=1;
    mydev = *(struct myirq*)dev;		
    printk("key: %d..\n",count);
    printk("devid:%d ISR is working..\n",mydev.devid);
    printk("ISR is leaving......\n");
    count++;
    return IRQ_HANDLED;
}

内核模块初始化函数,在模块加载时,它会被调用。该函数注册了一个中断处理程序,使用 request_irq 函数。如果注册成功,它会打印一条成功消息,否则会打印失败消息。

static int __init myirq_init(void)    //最重要的工作是注册中断线,并将自己写的中断服务例程注册进去,用request_irq完成
{
    printk("Module is working...\n");
    if(request_irq(irq,myirq_handler,IRQF_SHARED,devname,&mydev)!=0)
    {
        printk("%s request IRQ:%d failed..\n",devname,irq);
        return -1;
    }
    printk("%s request IRQ:%d success...\n",devname,irq);
    return 0;
}

内核模块退出函数,在模块卸载时,它会被调用。该函数使用 free_irq 函数注销之前注册的中断处理程序,并打印一条消息表示注销成功。

static void __exit myirq_exit(void)
{
    printk("Module is leaving...\n");
    free_irq(irq,&mydev);            //注销函数
    printk("Free the irq:%d..\n",irq);
}

模块信息

MODULE_LICENSE("GPL");
module_init(myirq_init);
module_exit(myirq_exit);

完整代码

# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/interrupt.h>

static int irq;			//irq号		
static char * devname;		//设备名称	
			
//这两个是用来让我们在命令行传入参数
module_param(irq,int,0644);
module_param(devname,charp,0644);    //这里charp相当于char*,是字符指针
			
struct myirq
{
    int devid;     //这个主要用在共享irq中
};

struct myirq mydev={1119};
		
//中断处理函数
static irqreturn_t myirq_handler(int irq,void * dev)
{
    struct myirq mydev;
    static int count=1;
    mydev = *(struct myirq*)dev;		
    printk("key: %d..\n",count);
    printk("devid:%d ISR is working..\n",mydev.devid);
    printk("ISR is leaving......\n");
    count++;
    return IRQ_HANDLED;
}


//内核模块初始化函数
static int __init myirq_init(void)    //最重要的工作是注册中断线,并将自己写的中断服务例程注册进去,用request_irq完成
{
    printk("Module is working...\n");
    if(request_irq(irq,myirq_handler,IRQF_SHARED,devname,&mydev)!=0)
    {
        printk("%s request IRQ:%d failed..\n",devname,irq);
        return -1;
    }
    printk("%s request IRQ:%d success...\n",devname,irq);
    return 0;
}

//内核模块退出函数
static void __exit myirq_exit(void)
{
    printk("Module is leaving...\n");
    free_irq(irq,&mydev);            //注销函数
    printk("Free the irq:%d..\n",irq);
}

MODULE_LICENSE("GPL");
module_init(myirq_init);
module_exit(myirq_exit);

插入模块和编译后运行后,查看日志信息可以看到每进入一次中断就会显示一些信息。
在这里插运行后入图片描述

中断打印下半部分代码

演示了中断处理程序和下半部分处理(tasklet)的使用。当中断事件发生时,中断处理程序会在中断上下文中快速响应,然后将处理工作推迟到下半部分处理,以提高系统的响应性。这是一种有效处理中断事件的方法。模块参数 irq 和 devname 允许用户在加载模块时指定中断号和设备名称。

头文件包含:代码包含了一些必要的Linux内核头文件,以便访问内核的函数和数据结构

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>

模块参数:声明两个模块参数,irq 和 devname,用于在模块加载时接收用户指定的参数。module_param 宏用于定义这些参数。

static int irq;
static char * devname;
module_param(irq,int,0644);
module_param(devname,charp,0644);

定义了一个结构体 myirq,它包含一个整数 devid,该结构体主要用于共享 IRQ(中断请求)。

struct myirq
{
        int devid;
};
 
struct myirq mydev={1119};

定义一个名为 mytasklet 的 tasklet_struct 结构体,它用于实现下半部分处理。

static struct tasklet_struct mytasklet;

mytasklet_handler 是下半部分处理函数。当中断事件发生时,中断处理程序会将工作推迟到下半部分处理,以避免在中断上下文中执行较长或耗时的操作。

static void mytasklet_handler(unsigned long data)  //下半部分处理函数
{
        printk("I am mytasklet_handler");
}

myirq_handler 是中断处理函数。当中断事件发生时,该函数会被调用。它打印一些信息,然后初始化并调度 mytasklet,以便执行下半部分处理。

static irqreturn_t myirq_handler(int irq,void * dev)
{
//      struct myirq mydev;
//      static int count=1;
        static int count=0;
//      mydev = *(struct myirq*)dev;
//      printk("key: %d..\n",count);
//      printk("devid:%d ISR is working..\n",mydev.devid);
//      printk("ISR is leaving......\n");
        printk("count:%d\n",count+1);
        printk("I am myirq_handler\n");
        printk("The most of the interrupt work will be done by folling tasks\n");
        //将下半部分函数进行注册。最主要的是要将下半部分处理函数挂载上去
        tasklet_init(&mytasklet,mytasklet_handler,0);  
        tasklet_schedule(&mytasklet);   //调度
        count++;
        return IRQ_HANDLED;
}

myirq_init 是模块的初始化函数。在模块加载时,它会被调用。该函数注册了一个中断处理程序,使用 request_irq 函数。如果注册成功,它会打印一条成功消息,否则会打印失败消息。

static int __init myirq_init(void)
{
        printk("Module is working...\n");
        if(request_irq(irq,myirq_handler,IRQF_SHARED,devname,&mydev)!=0)
        {
                printk("%s request IRQ: %d failed...\n",devname,irq);
                return -1;
        }
        printk("%s request IRQ:%d success...\n",devname,irq);
        return 0;
}

myirq_exit 是模块的退出函数。在模块卸载时,它会被调用。该函数使用 free_irq 函数注销之前注册的中断处理程序,并使用 tasklet_kill 函数注销 mytasklet。

static void __exit myirq_exit(void)
{
        printk("Module is leaving...\n");
        free_irq(irq,&mydev);
        tasklet_kill(&mytasklet);      //注销掉tasklet
        printk("Free the irq: %d..\n",irq);
}

模块信息

MODULE_LICENSE("GPL");
module_init(myirq_init);
module_exit(myirq_exit);

插入模块和编译后运行后,查看日志信息可以看到每进入一次中断就会显示一些信息。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值