/*********************************************************************************** * * 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) * { * ... //省略代码 * } * ... //省略代码 * * * 2015-3-13 阴 深圳 尚观 Var 曾剑锋 **********************************************************************************/ \\\\\\\\--*目录*--//////// | 一. error与非法地址: | 二. jiffies接口: | 三. timer接口: | 四. kthread接口: | 五. workqueue接口: | 六. tasklet接口: \\\\\\\\\\\\////////////// 一. error与非法地址: 1. error与非法地址的关系: 1. ENOMEM(错误码)是一个很小的正整数,大于0,小于4k; 2. -ENOMEM是一个很大的负数,小于0,大于-4k; 3. (void *)(-ENOMEM)是一个很大的正整数,大于4G-4K,小于4G; 4. 系统将大于4G-4k,小于4G的地址定义为非法地址; 2. 把错误码ENOMEM转换成对应的非法地址,返回非法地址: ERR_PTR(-ENOMEM); 3. 把非法地址err转换为对应的错误码,返回值为错误码对应的值: PTR_ERR(err); 4. 检查err是否是非法地址: IS_ERR(err); 二. jiffies接口: 1. 时钟滴答计数值: jiffies; 2. 查看时钟滴答频率配置,内核根目录下执行命令: cat .config | grep CONFIG_HZ 3. 毫秒值转换成时钟滴答: msecs_to_jiffies(); 4. 时钟滴答转换成毫秒值: jiffies_to_msecs(); 5. 判断给出的时间a是否大于jiffies: time_is_after_jiffies(a) a > jiffies 7. 判断给出的时间a是否小于jiffies: time_is_before_jiffies(a) a < jiffies 6. 判断给出的时间a是否大于等于jiffies: time_is_after_eq_jiffies(a) a >= jiffies 8. 判断给出的时间a是否小于等于jiffies: time_is_before_eq_jiffies(a) a <= jiffies 9. 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(2000); printk("jifffies is %lu\n", jiffies); return 0; } ... 三. timer接口: 1. 定时器定义: struct timer_list timer; 2. 设置定时器函数: setup_timer(&timer, fn, data); 3. 定时,并激活定时器: mod_timer(&timer, expires); 4. 取消定时器: del_timer_sync(&timer); 5. 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, 11223344); //设置时间,并激活定时器 mod_timer(&timer, jiffies + 3 * HZ); mod_timer(&timer, jiffies + 1 * HZ); if(timer_pending(&timer)) printk("timer pending\n"); else printk("timer NOT pending\n"); return 0; } void __exit test_exit(void) { /*del_timer(&timer);*/ del_timer_sync(&timer); } ... 四. kthread接口: 1. 创建一个内核线程,创建后状态是stop的,需要wake_up_process(t)唤醒: struct task_struct *t = kthread_create(thread_main, NULL, "my_kthread%d", 0); 参数说明: 1. thread_main: 线程创建后执行的函数 2. NULL: 传递给thread_main的参数 3. "my_kthread%d": 内核线程名字格式化字符串 2. 唤醒内核线程t: wake_up_process(t) 3. 通知线程退出,不是强制结束进程退出,而是分成以下两步: 1. 而且阻塞直到线程退出,返回线程函数的返回值: kthread_stop(t) 2. 检查当前线程是否收到退出的通知: kthread_should_stop() 4. 创建并唤醒一个内核线程: t = kthread_run(thread_main, NULL, "my_kthread%d", 0); 5. kthread接口实例Demo: ... struct task_struct *t; int thread_main(void *data) { printk("pid = %d\n", t->pid); while(1) { if(kthread_should_stop()) break; /*printk("thread running.\n");*/ msleep(3000); } return 123; } 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", 0); if(IS_ERR(t)) return PTR_ERR(t); return 0; } void __exit test_exit(void) { int ret; ret = kthread_stop(t); printk("ret = %d\n", ret); } ... 五. workqueue接口: 1. 两种创建工作队列方式: 1. struct workqueue_struct *wq = create_workqueue("my_wq"); 2. struct workqueue_struct *wq = create_singlethread_workqueue("my_wq"); 2. 两种工作任务: 1. 普通工作任务: struct work_struct t; INIT_WORK(&t, fn); 2. 延时工作: struct delayed_work t; INIT_DELAYED_WORK(&t[i], work_main); 3. 两种提交普通工作的队列方式,返回值如果是0,表示任务已经在工作队列上了,还没有处理: 1. 将任务t放到wq工作队列上处理: queue_work(wq, &t); 2. 将任务t放到系统工作队列(系统已经建立的工作队列)上处理: schedule_work(&t); 4. 提交延时工作队列的方式: queue_delayed_work(wq, &t[i++], 3 * HZ)); 5. 让工作队列尽快执行完: flush_workqueue(wq); 6. 销毁工作队列: destroy_workqueue(wq); 7. 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 = 0; i < 3; i++) { printk("work, count = %d\n", cnt++); //在进程上下文执行,可以sleep msleep(1000); } } static irqreturn_t irq_handler(int irq, void *arg) { /*printk("key1 down.\n");*/ static int i; if(i == NUM) i = 0; /*if(!schedule_work(&t[i++]))*/ /*if(!queue_work(wq, &t[i++]))*/ if(!queue_delayed_work(wq, &t[i++], 3 * HZ)) printk("work is already in the queue.\n"); return IRQ_HANDLED; } int __init test_init(void) { int ret, i; //初始化工作结构 for(i = 0; 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(26), 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(26), NULL); /*flush_workqueue(wq);*/ destroy_workqueue(wq); } ... 六. tasklet接口: 1. 定义并初始化: struct tasklet_struct t; tasklet_init(&t, fn, data); 2. 提交任务, 提交的工作在软中断上下文执行: tasklet_schedule(t); 3. 对应加锁,解锁方法: spin_lock_bh(); spin_unlock_bh(); 4. 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 0; } static int test_close(struct inode *inode, struct file *file) { /*test_t *p = file->private_data;*/ return 0; } int critical(const char *s, spinlock_t *lock) { int i; unsigned long flag; static int cnt = 0; /*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 = 0; i < 3; i++) { printk("count = %d, %s", cnt++, s); mdelay(1000); } spin_unlock_bh(lock); /*spin_unlock_irqrestore(lock, flag);*/ /*spin_unlock_irq(lock);*/ /*local_irq_restore(flag);*/ /*local_irq_enable();*/ /*spin_unlock(lock);*/ return 0; } 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 + 1]; 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 = 0, }; 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 > 0) { test.major = ret; printk("major = %d\n", test.major); ret = 0; } ret = request_irq(IRQ_EINT(26), 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(26), &test); unregister_chrdev(test.major, DEV_NAME); } module_init(test_init); module_exit(test_exit); MODULE_LICENSE("GPL");