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

简介: /*********************************************************************************** * * jiffies,timer,k...
/***********************************************************************************
 *                    
 *                     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");

 

目录
相关文章
|
5月前
STM32Cubmx FreeRTOS Timer
STM32Cubmx FreeRTOS Timer
51 10
|
调度 开发者
【Freertos基础入门】2个Freertos的Delay函数
【Freertos基础入门】2个Freertos的Delay函数
846 1
中断-softirq-tasklet-work queue(下)
中断-softirq-tasklet-work queue
80 0
|
7月前
|
芯片
ARM hint instruction-WFI(Wait For Interrupt)指令的一些笔记
ARM hint instruction-WFI(Wait For Interrupt)指令的一些笔记
414 0
|
7月前
|
算法 调度
FreeRTOS入门教程(任务优先级,Tick)
FreeRTOS入门教程(任务优先级,Tick)
579 0
|
传感器 移动开发 Linux
RT-Thread UART设备驱动框架初体验(中断方式接收带\r\n的数据)
RT-Thread UART设备驱动框架初体验(中断方式接收带\r\n的数据)
226 0
|
消息中间件 算法 安全
RTOS实时操作系统中RT-Thread、FreeRTOS和uCOS 选择哪一个学习比较好?
RTOS实时操作系统中RT-Thread、FreeRTOS和uCOS 选择哪一个学习比较好?
|
Linux 调度
中断-softirq-tasklet-work queue(上)
中断-softirq-tasklet-work queue
94 0
|
调度
按键演示休眠与唤醒场景--基于IMX6ULL
按键演示休眠与唤醒场景--基于IMX6ULL
151 0
按键演示休眠与唤醒场景--基于IMX6ULL