• 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");
  • 相关阅读:
    集群
    Zabbix分布式监控系统
    构建读写分离的数据库集群
    MySQL主从配置
    常用MySQL操作
    Prometheus Node_exporter 之 Network Netstat ICMP
    Prometheus Node_exporter 之 Network Netstat UDP
    Prometheus Node_exporter 之 Network Netstat TCP Linux MIPs
    Prometheus Node_exporter 之 Network Netstat TCP
    Prometheus Node_exporter 之 Network Netstat
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/4336007.html
Copyright © 2020-2023  润新知