嵌入式设计实验三:Tasket与工作队列


一、实验目的

掌握字符设备驱动程序中Tasklet和工作队列的编写和调用方法。
分析对比Tasklet和工作队列的差异。

二、实验环境

ubuntu 12.04 内核3.2.14

三、实验内容及实验原理

写一个简单的驱动程序,要求:
定义一个Tasklet和一个工作队列,实现打印输出;
定义两个定时器,定时器周期分别为T1和T2;
T1周期到时调度Tasklet,T2周期到时调度工作队列;
在加载驱动模块时注册Tasklet和工作队列
在卸载驱动模块时销毁Tasklet和工作队列

四、实验结果及其分析

1.编译模块(设备驱动程序)

(1)创建模块文件xxx.c

gedit rwbuf.c

// 模块
#include <linux/module.h>
// 内核
#include <linux/kernel.h>
// struct file_operations
#include <linux/fs.h>
// 中断
#include <linux/interrupt.h>
// 调度
#include <linux/sched.h>
// 定时器
#include <linux/timer.h>
#include <linux/time.h>
// 工作队列
#include <linux/workqueue.h>
// 原子操作
#include <asm/atomic.h>

atomic_t tasklet_run_time;
atomic_t wq_run_time;

static unsigned int t_failed_cnt = 0;
static unsigned int w_failed_cnt = 0;

typedef struct timer_data{
    struct timer_list timer;
    unsigned int loops;
}*timer_data_ptr;

// tasklet的定时器
struct timer_data timer_T;
// 工作队列的定时器
struct timer_data timer_W;

// tasklet要做什么
void timer_T_fun(unsigned long arg);
// 工作队列要做什么
void timer_W_fun(unsigned long arg);

// tasklet_struct结构体
static struct tasklet_struct my_tasklet;

static void do_work(struct work_struct *work);
// 工作队列静态创建任务
static DECLARE_WORK(workq, do_work);
// 工作队列结构体
static struct workqueue_struct *wqueue;

// tasklet处理程序
static void tasklet_handler(unsigned long data)
{
    atomic_inc(&tasklet_run_time);
    printk("[tasklet_handler-run_time] %u\n",atomic_read(&tasklet_run_time));
    printk("[tasklet_handler-t_failed_cnt] %u\n",t_failed_cnt);
}

void do_work(struct work_struct *work)
{
    atomic_inc(&wq_run_time);
    printk("[workqueue-run_time] %u\n",atomic_read(&wq_run_time));
    printk("[workqueue-w_failed_cnt] %u\n",w_failed_cnt);
}

// tasklet要做什么
void timer_T_fun(unsigned long arg)
{
    timer_data_ptr data = (timer_data_ptr)arg;
    data->timer.expires+=2*HZ;
    add_timer(&data->timer);
    // 调度
    tasklet_schedule(&my_tasklet);
    data->loops++;
    printk("[timer_T_fun] loops=%u.\n",data->loops);
    
}

// 工作队列要做什么
void timer_W_fun(unsigned long arg)
{
    timer_data_ptr data =(timer_data_ptr)arg;
    data->timer.expires+=3*HZ;
    add_timer(&data->timer);
    // 提交工作给一个工作队列
    queue_work(wqueue,&workq);
    data->loops++;
    printk("[timer_w_fun] loops=%u.\n",data->loops);

}

// module_init()内的初始化函数:返回-1表示错误;返回0表示成功
static int __init isr_init(void)
{

    printk("[isr_init-start-jiffies] %lu\n", jiffies);

    // 初始化定时器
    init_timer(&(timer_T.timer));
    timer_T.timer.expires=jiffies+2*HZ;
    timer_T.timer.data = (unsigned long)(&timer_T);
    timer_T.timer.function=timer_T_fun;
    add_timer(&(timer_T.timer));
    
    // 初始化定时器
    init_timer(&(timer_W.timer));
    timer_W.timer.expires=jiffies+3*HZ;
    timer_W.timer.data = (unsigned long)(&timer_W); 
    timer_W.timer.function=timer_W_fun;
    add_timer(&(timer_W.timer));

    // tasklet初始化
    tasklet_init(&my_tasklet, tasklet_handler, 0);
    // 工作队列初始化,只有单个工作者线程
    wqueue=create_singlethread_workqueue("wq_test");

    printk("[isr_init-success]\n");
    printk("[isr_init-end-jiffies] %lu\n", jiffies);
    return 0;
}

// module_exit()内的退出函数。
static void __exit isr_exit(void)
{
    // 删除tasklet定时器
    del_timer(&(timer_T.timer));
    // 删除workqueue定时器
    del_timer(&(timer_W.timer));
    // 从挂起的队列中去掉我们的tasklet
    tasklet_kill(&my_tasklet);
    // 销毁我们的workqueue
    destroy_workqueue(wqueue);
    printk("[isr_exit-success]\n");
}

// 内核模块入口,相当于main()函数,完成模块初始化
module_init(isr_init);
// 卸载时调用的函数入口,完成模块卸载
module_exit(isr_exit);
// GPL协议证书
MODULE_LICENSE("GPL");

(2)Makefile

gedit Makefile
内容:

obj-m := rwbuf.o
KERNELDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions modules.order  Module.symvers

(3)编译

sudo make

2.插入内核模块(加载设备驱动程序)

先清理一下缓存,不然一会就可能输出一大堆多余东西,影响到我们想要看到的输出东西
sudo dmesg -c
插入内核模块(加载设备驱动程序)
sudo insmod rwbuf.ko

查看是否成功
dmesg
在这里插入图片描述

五、心得体会与建议

感觉做实验的体会还是挺直接的。比如:
在这里插入图片描述

如果按照一般函数的话,就直接是先执行tasklet_handler中的部分,而tasklet是延迟作用的,
在这里插入图片描述

所以执行结果中timer_T_fun中先执行完再去tasklet_handler中的部分。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值