Samsung_tiny4412(驱动笔记08)----jiffies,timer,kthread,workqueue,tasklet

时间:2023-03-09 22:09:52
Samsung_tiny4412(驱动笔记08)----jiffies,timer,kthread,workqueue,tasklet
/***********************************************************************************
*
* jiffies,timer,kthread,workqueue,tasklet
*
* 声明:
* 1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会
* 不对齐,从而影响阅读.
* 2. 本文中有些源代码没有全部帖出来,主要是因为篇幅太大的原因;
* 3. 基于2中的原因,本文借鉴了python中的缩进代码风格进行代码的体现:
* 1. 有些代码中的"..."代表省略了不影响阅读的代码;
* 2. 如下代码缩进代表在一个函数内部的代码,至于在什么函数里,不影响阅读:
* ... //省略代码
* struct test_s {
* };
* ... //省略代码
*
* //进入临界区之前加锁 }
* spin_lock(&p->lock); |
* | |
* /* 有效代码 */ |-->|采用缩进,代表在一个函数内
* | |的代码
* //出临界区之后解锁 |
* spin_unlock(&p->lock); }
*
* ... //省略代码
* int __init test_init(void)
* {
* ... //省略代码
* }
* ... //省略代码
*
*
* -- 阴 深圳 尚观 Var 曾剑锋
**********************************************************************************/ \\\\\\\\--*目录*--////////
| 一. error与非法地址:
| 二. jiffies接口:
| 三. timer接口:
| 四. kthread接口:
| 五. workqueue接口:
| 六. tasklet接口:
\\\\\\\\\\\\////////////// 一. error与非法地址:
. error与非法地址的关系:
. ENOMEM(错误码)是一个很小的正整数,大于0,小于4k;
. -ENOMEM是一个很大的负数,小于0,大于-4k;
. (void *)(-ENOMEM)是一个很大的正整数,大于4G-4K,小于4G;
. 系统将大于4G-4k,小于4G的地址定义为非法地址;
. 把错误码ENOMEM转换成对应的非法地址,返回非法地址: ERR_PTR(-ENOMEM);
. 把非法地址err转换为对应的错误码,返回值为错误码对应的值: PTR_ERR(err);
. 检查err是否是非法地址: IS_ERR(err); 二. jiffies接口:
. 时钟滴答计数值: jiffies;
. 查看时钟滴答频率配置,内核根目录下执行命令: cat .config | grep CONFIG_HZ
. 毫秒值转换成时钟滴答: msecs_to_jiffies();
. 时钟滴答转换成毫秒值: jiffies_to_msecs();
. 判断给出的时间a是否大于jiffies: time_is_after_jiffies(a) a > jiffies
. 判断给出的时间a是否小于jiffies: time_is_before_jiffies(a) a < jiffies
. 判断给出的时间a是否大于等于jiffies: time_is_after_eq_jiffies(a) a >= jiffies
. 判断给出的时间a是否小于等于jiffies: time_is_before_eq_jiffies(a) a <= jiffies
. jiffies接口实例Demo:
...
void my_mdelay(int msec)
{
unsigned long expire = jiffies + msecs_to_jiffies(msec);
while(time_is_after_jiffies(expire))
;
} int __init test_init(void)
{
printk("jifffies is %lu\n", jiffies); /*msleep(2000);*/
/*mdelay(2000);*/
my_mdelay();
printk("jifffies is %lu\n", jiffies); return ;
}
... 三. timer接口:
. 定时器定义: struct timer_list timer;
. 设置定时器函数: setup_timer(&timer, fn, data);
. 定时,并激活定时器: mod_timer(&timer, expires);
. 取消定时器: del_timer_sync(&timer);
. timer接口实例Demo:
...
struct timer_list timer;
void timer_main(unsigned long data)
{
printk("timer expire! data = %lu\n", data); /**
* if(timer_pending(&timer))
* printk("timer_main: timer pending\n");
* else
* printk("timer_main: timer NOT pending\n");
*/ if(in_interrupt())
printk("in interrupt context.\n");
if(in_softirq())
printk("in softirq context.\n");
if(in_irq())
printk("in irq context.\n"); /**
* mdelay(3000);
* printk("timer fn end\n");
*/
/*mod_timer(&timer, jiffies + HZ);*/
} int __init test_init(void)
{
setup_timer(&timer, timer_main, ); //设置时间,并激活定时器
mod_timer(&timer, jiffies + * HZ);
mod_timer(&timer, jiffies + * HZ); if(timer_pending(&timer))
printk("timer pending\n");
else
printk("timer NOT pending\n"); return ;
} void __exit test_exit(void)
{
/*del_timer(&timer);*/
del_timer_sync(&timer);
}
... 四. kthread接口:
. 创建一个内核线程,创建后状态是stop的,需要wake_up_process(t)唤醒:
struct task_struct *t = kthread_create(thread_main, NULL, "my_kthread%d", );
参数说明:
. thread_main: 线程创建后执行的函数
. NULL: 传递给thread_main的参数
. "my_kthread%d": 内核线程名字格式化字符串
. 唤醒内核线程t: wake_up_process(t)
. 通知线程退出,不是强制结束进程退出,而是分成以下两步:
. 而且阻塞直到线程退出,返回线程函数的返回值: kthread_stop(t)
. 检查当前线程是否收到退出的通知: kthread_should_stop()
. 创建并唤醒一个内核线程:
t = kthread_run(thread_main, NULL, "my_kthread%d", );
. kthread接口实例Demo:
...
struct task_struct *t;
int thread_main(void *data)
{
printk("pid = %d\n", t->pid);
while()
{
if(kthread_should_stop())
break; /*printk("thread running.\n");*/
msleep();
} return ;
} int __init test_init(void)
{
/**
* t = kthread_create(thread_main, NULL, "my_thread%d", 0);
* if(IS_ERR(t))
* return PTR_ERR(t);
*
* wake_up_process(t);
*/
t = kthread_run(thread_main, NULL, "my_thread%d", );
if(IS_ERR(t))
return PTR_ERR(t); return ;
} void __exit test_exit(void)
{
int ret;
ret = kthread_stop(t);
printk("ret = %d\n", ret);
}
... 五. workqueue接口:
. 两种创建工作队列方式:
. struct workqueue_struct *wq = create_workqueue("my_wq");
. struct workqueue_struct *wq = create_singlethread_workqueue("my_wq");
. 两种工作任务:
. 普通工作任务: struct work_struct t; INIT_WORK(&t, fn);
. 延时工作: struct delayed_work t; INIT_DELAYED_WORK(&t[i], work_main);
. 两种提交普通工作的队列方式,返回值如果是0,表示任务已经在工作队列上了,还没有处理:
. 将任务t放到wq工作队列上处理: queue_work(wq, &t);
. 将任务t放到系统工作队列(系统已经建立的工作队列)上处理: schedule_work(&t);
. 提交延时工作队列的方式: queue_delayed_work(wq, &t[i++], * HZ));
. 让工作队列尽快执行完: flush_workqueue(wq);
. 销毁工作队列: destroy_workqueue(wq);
. workqueue接口实例Demo:
...
#define NUM 2
//定义延时工作
struct delayed_work t[NUM];
struct workqueue_struct *wq; void work_main(struct work_struct *work)
{
int i;
static int cnt; for(i = ; i < ; i++)
{
printk("work, count = %d\n", cnt++);
//在进程上下文执行,可以sleep
msleep();
}
} static irqreturn_t irq_handler(int irq, void *arg)
{
/*printk("key1 down.\n");*/
static int i; if(i == NUM)
i = ; /*if(!schedule_work(&t[i++]))*/
/*if(!queue_work(wq, &t[i++]))*/
if(!queue_delayed_work(wq, &t[i++], * HZ))
printk("work is already in the queue.\n"); return IRQ_HANDLED;
} int __init test_init(void)
{
int ret, i; //初始化工作结构
for(i = ; i < NUM; i++)
/* INIT_WORK(&t[i], work_main); */
INIT_DELAYED_WORK(&t[i], work_main); //创建工作队列
/*wq = create_workqueue("my_workqueue");*/
//只创建一个工作者线程
wq = create_singlethread_workqueue("my_workqueue");
if(!wq)
{
printk("workqueue create failed!\n");
ret = -ENOMEM;
goto err0;
} ret = request_irq(IRQ_EINT(), irq_handler,
IRQF_TRIGGER_FALLING, "KEY1", NULL);
if(ret)
{
printk("request_irq failed!\n");
goto err1;
} return ret; err1:
destroy_workqueue(wq);
err0:
return ret;
} void __exit test_exit(void)
{
free_irq(IRQ_EINT(), NULL);
/*flush_workqueue(wq);*/
destroy_workqueue(wq);
}
... 六. tasklet接口:
. 定义并初始化: struct tasklet_struct t; tasklet_init(&t, fn, data);
. 提交任务, 提交的工作在软中断上下文执行: tasklet_schedule(t);
. 对应加锁,解锁方法:
spin_lock_bh();
spin_unlock_bh();
. tasklet接口实例Demo:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/delay.h> #define DEV_NAME "test" struct test_s {
struct file_operations fops;
struct tasklet_struct task;
spinlock_t lock;
int major;
};
typedef struct test_s test_t; static int test_open(struct inode *inode, struct file *file)
{
test_t *p;
p = container_of(file->f_op, test_t, fops); file->private_data = p; return ;
} static int test_close(struct inode *inode, struct file *file)
{
/*test_t *p = file->private_data;*/ return ;
} int critical(const char *s, spinlock_t *lock)
{
int i;
unsigned long flag;
static int cnt = ; /*spin_lock(lock);*/
/*local_irq_disable();*/
/*local_irq_save(flag);*/
/*spin_lock_irq(lock);*/
/*spin_lock_irqsave(lock, flag);*/
spin_lock_bh(lock); //bottom half 关闭软中断,加锁 for(i = ; i < ; i++)
{
printk("count = %d, %s", cnt++, s);
mdelay();
} spin_unlock_bh(lock);
/*spin_unlock_irqrestore(lock, flag);*/
/*spin_unlock_irq(lock);*/
/*local_irq_restore(flag);*/
/*local_irq_enable();*/
/*spin_unlock(lock);*/ return ;
} void task_main(unsigned long data)
{
test_t *p = (test_t *)data;
critical("softirq.\n", &p->lock);
} static irqreturn_t irq_handler(int irq, void *arg)
{
test_t *p = arg;
printk("in irq\n"); /*critical("irq\n", &p->lock);*/
tasklet_schedule(&p->task); return IRQ_HANDLED;
} static ssize_t test_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
int ret;
char kbuf[count + ];
test_t *p = file->private_data; ret = copy_from_user(kbuf, buf, count);
if(ret)
return -EFAULT;
kbuf[count] = '\0'; if(critical(kbuf, &p->lock))
return -EAGAIN; return count;
} struct test_s test = {
.fops = {
.owner = THIS_MODULE,
.open = test_open,
.release = test_close,
.write = test_write,
},
.major = ,
}; int __init test_init(void)
{
int ret; tasklet_init(&test.task, task_main, (unsigned long)&test);
spin_lock_init(&test.lock); ret = register_chrdev(test.major,
DEV_NAME, &test.fops);
if(ret > )
{
test.major = ret;
printk("major = %d\n", test.major);
ret = ;
} ret = request_irq(IRQ_EINT(), irq_handler,
IRQF_TRIGGER_FALLING, "key1", &test);
if(ret)
unregister_chrdev(test.major, DEV_NAME); return ret;
} void __exit test_exit(void)
{
free_irq(IRQ_EINT(), &test);
unregister_chrdev(test.major, DEV_NAME);
} module_init(test_init);
module_exit(test_exit); MODULE_LICENSE("GPL");