zoukankan      html  css  js  c++  java
  • 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)
     *               {   
     *                   ... //省略代码
     *               }  
     *               ... //省略代码
     *
     *
     *                                          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
    ", jiffies);
            
                /*msleep(2000);*/
                /*mdelay(2000);*/
                my_mdelay(2000);
                printk("jifffies is %lu
    ", 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
    ", data);
            
                /**
                 * if(timer_pending(&timer))
                 *     printk("timer_main: timer pending
    ");
                 * else
                 *     printk("timer_main: timer NOT pending
    ");
                 */
            
                if(in_interrupt())
                    printk("in interrupt context.
    ");
                if(in_softirq())
                    printk("in softirq context.
    ");
                if(in_irq())
                    printk("in irq context.
    ");
            
                /**
                 * mdelay(3000);
                 * printk("timer fn end
    ");
                 */
                /*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
    ");
                else
                    printk("timer NOT pending
    ");
            
                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
    ", t->pid);
                while(1)
                {
                    if(kthread_should_stop())
                        break;
            
                    /*printk("thread running.
    ");*/
                    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
    ", 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
    ", cnt++);
                    //在进程上下文执行,可以sleep
                    msleep(1000);
                }
            }
            
            static irqreturn_t irq_handler(int irq, void *arg)
            {
                /*printk("key1 down.
    ");*/
                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.
    ");
            
                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!
    ");
                    ret = -ENOMEM;
                    goto err0;
                }
            
                ret = request_irq(IRQ_EINT(26), irq_handler, 
                                  IRQF_TRIGGER_FALLING, "KEY1", NULL);
                if(ret)
                {
                    printk("request_irq failed!
    ");
                    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.
    ", &p->lock);
            }
            
            static irqreturn_t irq_handler(int irq, void *arg)
            {
                test_t *p = arg;
                printk("in irq
    ");
            
                /*critical("irq
    ", &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] = '';
            
                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
    ", 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");
  • 相关阅读:
    leetcode 309. Best Time to Buy and Sell Stock with Cooldown
    leetcode 714. Best Time to Buy and Sell Stock with Transaction Fee
    leetcode 32. Longest Valid Parentheses
    leetcode 224. Basic Calculator
    leetcode 540. Single Element in a Sorted Array
    leetcode 109. Convert Sorted List to Binary Search Tree
    leetcode 3. Longest Substring Without Repeating Characters
    leetcode 84. Largest Rectangle in Histogram
    leetcode 338. Counting Bits
    git教程之回到过去,版本对比
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/4336007.html
Copyright © 2011-2022 走看看