Linux内核定时器-模块导出符号表

2023-12-28 11:23:34

Linux内核定时器

  • 定时器的当前时间如何获取?

jiffies:内核时钟节拍数

jiffies是在板子上电这一刻开始计数,只要

板子不断电,这个值一直在增加(64位)。在

驱动代码中直接使用即可。

  • 定时器加1代表走了多长时间?

在内核顶层目录下有.config

CONFIG_HZ=1000

周期?=?1/CONFIG_HZ

周期是1ms;

  • 分配的对象

struct?timer_list?mytimer;

  • 对象的初始化

定时器结构体

struct timer_list 
{
			unsigned long?expires;   //定时的时间
			void (*function)(unsigned long); //定时器的处理函数
			unsigned long?data;      //向定时器处理函数中填写的值
};

定时器初始化

void timer_function(unsigned long?data) //定时器的处理函数
{
			
}
init_timer(&mytimer);  //内核帮你填充你未填充的对象	
mytimer.expries?=?jiffies?+ 1000;  //1s
mytimer.function?=?timer_function;
mytimer.data?= 0;

对象的添加定时器

void add_timer(struct timer_list *timer);
		//同一个定时器只能被添加一次,
		//在你添加定时器的时候定时器就启动了,只会执行一次

		int mod_timer(struct timer_list *timer, unsigned long?expires)
		//再次启动定时器??????????????????????????jiffies+1000
对象的删除

int del_timer(struct timer_list *timer)
//删除定时器

?Int?gpio_get_value(int?gpiono);//通过gpiono获取当权gpio的所处状态

??返回0,低电平???非0:高电平

#include<linux/init.h>
#include<linux/module.h>
#include<linux/printk.h>
#include<linux/timer.h>
#include<linux/gpio.h>
#include<linux/interrupt.h>

#define?GPIONO(m,n)?(m*32+n)
#define?GPIO_B8?GPIONO(1,8)
#define?GPIO_B16?GPIONO(1,16)

int?gpiono[]={GPIO_B8,GPIO_B16};
char *name[]={"interrupt_b8","interrupt_b16"};

int?i;

struct timer_list?mytimer;

//中断处理函数
irqreturn_t?irq_handler(int?irq,void *arg)
{
    //启动定时器
    mod_timer(&mytimer,jiffies+100);
    return?IRQ_HANDLED;
}
//定时处理函数
void timer_handler(unsigned long?data)
{
    //检测引脚状态,如果是低电平,说明产生了中断
    if(gpio_get_value(GPIO_B8)==0)
    {
        printk(KERN_ALERT"+++++++++++++++++++++++++++++++++++\n");//设置为大于终端打印权限,不然只能在demsg中查看
    }
    if(gpio_get_value(GPIO_B16)==0)
    {
        printk(KERN_ALERT"-----------------------------------\n");//设置为大于终端打印权限,不然只能在demsg中查看
    }
}
static int?__init?timer_init(void)
{
    //初始化定时器
    init_timer(&mytimer);//内核自动初始化定时器
????mytimer.expires=?jiffies+100;//定时时长
????mytimer.function=timer_handler;
????mytimer.data=0;
    //添加定时器
    add_timer(&mytimer);
    //注册中断
    for(i=0;i<sizeof(gpiono)/sizeof(int);i++)
    {
        if(request_irq(gpio_to_irq(gpiono[i]),irq_handler,IRQF_TRIGGER_FALLING,name[i],NULL)!=0)
        {
            printk("%s?request_ire?err.\n",name[i]);
            return -EBUSY;
        }
    }
    return 0;
}
static void?__exit?timer_exit(void)
{
    //注销中断
    for(i=0;i<sizeof(gpiono)/sizeof(int);i++)
    {
        free_irq(gpio_to_irq(gpiono[i]),NULL);
    }
}
module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("GPL");

模块导出符号表

思考1应用层两个app程序,app1中拥有一个add函数,app1运行时app2是否可以调用app1中的add函数??不行,因为应用层app运行的空间是私有的(0-3G)没有共享。

思考2两个驱动模块,module1中的函数,module2是否可以调用?可以,他们公用(3-4G)内核空间,只是需要找到函数的地址就可以。好处:减少代码冗余性,代码不会再内存上被重复加载。代码更精简,一些代码可以不用写,直接调用别人写好的函数就可以。

编写驱动代码找到其他驱动中的函数,需要用模块导出符号表将函数导出,被人才可以使用这个函数。他是一个宏函数

在驱动的一个模块中,想使用另外一个模块中的函数/变量,只需要使用EXPORT_SYMBOL_GPL将变量或者函数的地址给导出。使用者就可以用这个地址来调用它了。

EXPORT_SYMBOL_GPL(sym)

sym:变量名或函数名

代码举例1:两个独立的代码驱动模块

代码举例2:提供者为内核已经安装使用的驱动

总结

编译:

1.先编译提供者,编译完成之后会产生一个Module.symvers

2.将Module.symvers拷贝到调用者的目录下

3.编译调用者即可

安装:

先安装提供者

再安装调用者

卸载:

先卸载调用者

再卸载提供者

如果调用者和提供者时两个独立(xx.ko)驱动模块,他们间传递地址的时候,是通过Module.symvers传递的。

如果提供者是内核的模块(uImage),此时调用者和提供者间就不需要Module.symvers文件传递信息

文章来源:https://blog.csdn.net/m0_74937538/article/details/135260377
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。