jiffies操作、内核计时器、tasklet、workqueue 相关函数 >>Linux设备驱动程序

我爱学习,爱代码,代码让我更加专注,虽然已经不是早上了,有点分心,不能再分心了
必须为自己选择的道路负责起来;加油!

[0x100] 内容概述

  1. 内核定时器
  2. 软中断机制

[0x200] 与延迟有关的内核应用

  • HZ :通常定义于<asm/param.h>文件中,标识每秒产生多少次时钟中断,使用CONFIG_HZ来获取数值;
  • jiffies :时钟中断递增计数器,通常为无符号64位的数值;

[0x210] jiffies 时钟中断计数器

[0x211] 获取计数值

#include <linux/jiffies.h>
#if (BITS_PER_LONG < 64)
/*implement kernel_dir/kernel/time/jiffies.c */
u64 get_jiffies_64(void)
{
        unsigned long seq;
        u64 ret;

        do {
               /*使用seqlock_t 进行读锁定*/
                seq = read_seqbegin(&xtime_lock);
                /*对于小端存储的32位系统的jiffes 通常是低32位,大端则是高32位*/
                ret = jiffies_64;
        } while (read_seqretry(&xtime_lock, seq));
        return ret;
}
#else
static inline u64 get_jiffies_64(void)
{           /*64位系统没有这个问题,原子访问操作*/    
        return (u64)jiffies;
}       
#endif  

[0x212] 比较计数值

#include <linux/jiffies.h>
/*如果a 晚于b 为真 否则为假*/
#define time_after(a,b)         \
        (typecheck(unsigned long, a) && \
         typecheck(unsigned long, b) && \
         ((long)(b) - (long)(a) < 0))
/*如果a 早于b 为真 否则为假*/
#define time_before(a,b)        time_after(b,a)
/*如果a 晚于或者等于b 为真 否则为假*/
#define time_after_eq(a,b)      \
        (typecheck(unsigned long, a) && \
         typecheck(unsigned long, b) && \
         ((long)(a) - (long)(b) >= 0))
/*如果a 早于或者等于b 为真 否则为假*/
#define time_before_eq(a,b)     time_after_eq(b,a)
/*如果a 存在于[b,c]闭集合中为真,否则为假*/
#define time_in_range(a,b,c) \
        (time_after_eq(a,b) && \
         time_before_eq(a,c))
/*如果a 存在于[b,c)集合中为真,否则为假*/         
#define time_in_range_open(a,b,c) \
        (time_after_eq(a,b) && \
         time_before(a,c))

[0x213] 计数值的转换

  1. struct timeval :使用秒和毫秒值标识时间;
  2. struct timespec :使用秒和纳秒值标识时间;
  3. 1s = 103ms = 10 6 μs = 10 9 ns
#include <linux/time.h>
struct timespec {
        __kernel_time_t tv_sec;                 /*  秒[s]*/
        long            tv_nsec;                /* 纳秒[ns]*/
};
struct timeval {
        __kernel_time_t         tv_sec;          /* 秒[s]*/
        __kernel_suseconds_t    tv_usec;        /* 微秒 [μs]*/
};
#include <linux/jiffies.h>

extern unsigned long 
timespec_to_jiffies(const struct timespec *value);
extern void 
jiffies_to_timespec(const unsigned long jiffies,struct timespec *value);
extern unsigned long 
timeval_to_jiffies(const struct timeval *value);
extern void 
jiffies_to_timeval(const unsigned long jiffies,struct timeval *value);

[0x220] 内核定时器[struct timer_list]

[0x221] 定时器相关数据结构

#include <linux/timer.h>
struct timer_list {
        struct list_head entry;         
        unsigned long expires;                  /*需要传入jiffies计数值*/         
        struct tvec_base *base;              
        void (*function)(unsigned long data);   /*定时器timeout后的执行函数*/
        unsigned long data;                    /*需要向定时器执行函数传递的参数 */        
        int slack;
#ifdef CONFIG_TIMER_STATS
        int start_pid;
        void *start_site;
        char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
        struct lockdep_map lockdep_map;
#endif  
};   

[0x222] 内核定时器的函数接口

#include <linux/timer.h>
/*implement kernel-dir/kernel/timer.c */
 /*1.定义定时器处理函数*/
void (*function)(unsigned long data);   /*定时器timeout后的执行函数*/
 /*2.使用如下宏 填充struct timer_list 结构,expires 需要填入一个 jiffies偏移值,data需要传递的参数*/   
 struct timer_list TIMER_INITIALIZER( void (*function)(unsigned long data),expires,data);     
 /*3.激活一次定时器*/
void add_timer(struct timer_list *timer)
/*4.重新启用定时器:通常需要在一个计时器调用周期结束之前 */ 
int mod_timer(struct timer_list *timer, unsigned long expires);
 /*5.销毁定时器*/ 
 int del_timer(struct timer_list *timer);
/*当定时器在所有CPU均执行完毕后,注销定时器,不能使用在中断上下文中*/
 int del_timer_sync(struct timer_list *timer)

[0x230] 原子软中断–tasklet

软件中断:打开硬件中断的同时,执行某些异步任务的一种内核机制,常用于执行相对耗时的操作的;
特征 : 只运行于调用CPU,链表结构的软中断形式,必须原子运行于中断上下文,在2.6 内核前有数量限制;

[0x231] 相关数据结构

#include <linux/interrupt.h>
struct tasklet_struct
{
        struct tasklet_struct *next;       /*链表结构*/
        unsigned long state;               /*对外不可见 数据 执行状态*/
        enum{
        TASKLET_STATE_SCHED,    /* 放弃CPU执行状态*/
        TASKLET_STATE_RUN       /* 正在运行状态 只在SMP的前提下有效*/
        };
        atomic_t count;                    /*禁用计数*/
        void (*func)(unsigned long);       /*带有一个执行函数指针*/
        unsigned long data;                /*执行函数的需要的传递的参数,需要*/
};

[0x232] 结构初始化与销毁

#include <linux/interrupt.h>
//初始化结构
/*implement kernel-dir/kernel/softirq.c*/
void tasklet_init(struct tasklet_struct *t,void(*func)(unsigned long data),unsigned long data);
/*宏接口 定义并清空禁用计数 原子变量清空*/
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
/*宏接口 定义并添加禁用计数 禁用任务*/
#define DECLARE_TASKLET_DISABLED(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }

[0x233] 调用执行

#include <linux/interrupt.h>
/*implement kernel-dir/kernel/softirq.c*/
/*调用tasklet 执行一次*/
static inline void tasklet_schedule(struct tasklet_struct *t)
/*高优先级调用tasklet 必须具备低优先级tasklet*/
void __tasklet_hi_schedule(struct tasklet_struct *t)

[0x234] 禁用与启用

#include <linux/interrupt.h>
//强制禁用tasklet 不等待执行完成
void tasklet_disable_nosync(struct tasklet_struct *t);
//阻塞等待tasklet 执行结束,后禁用指定的tasklet
void tasklet_disable(struct tasklet_struct *t);
//启用之前禁用的tasklet 必须存在禁用,
static inline void tasklet_enable(struct tasklet_struct *t)

[0x240] 进程软中断–workqueue

特征 :运行于可以休眠的内核进程上下文,手工更改可以多处理器调度,可指定延迟执行间隔;

[0x241] 相关数据结构

#include <linux/workqueue.h>
/*指定工作队列的属性 类型*/
struct workqueue_struct {
        unsigned int            flags;          /* W: WQ_* flags */
        union {
                struct cpu_workqueue_struct __percpu    *pcpu;
                struct cpu_workqueue_struct             *single;
                unsigned long                           v;
        } cpu_wq;                               /* I: cwq's */
        struct list_head        list;           /* W: list of all workqueues */

        struct mutex            flush_mutex;    /* protects wq flushing */
        int                     work_color;     /* F: current work color */
        int                     flush_color;    /* F: current flush color */
        atomic_t                nr_cwqs_to_flush; /* flush in progress */
        struct wq_flusher       *first_flusher; /* F: first flusher */
        struct list_head        flusher_queue;  /* F: flush waiters */
        struct list_head        flusher_overflow; /* F: flush overflow list */

        mayday_mask_t           mayday_mask;    /* cpus requesting rescue */
        struct worker           *rescuer;       /* I: rescue worker */

        int                     nr_drainers;    /* W: drain in progress */
        int                     saved_max_active; /* W: saved cwq max_active */
#ifdef CONFIG_LOCKDEP
        struct lockdep_map      lockdep_map;
#endif
        char                    name[];         /* I: workqueue name */
};
/*实际工作队列的任务项*/
struct work_struct {
        atomic_long_t data;
        struct list_head entry;
        work_func_t func;
#ifdef CONFIG_LOCKDEP   
        struct lockdep_map lockdep_map;
#endif                  
};
/*具有定时器的工作队列的任务项*/
struct delayed_work {
        struct work_struct work;
        struct timer_list timer;
};

[0x241] 初始化队列属性与工作任务项

#include <linux/workqueue.h>
/*1.分配结构空间并指定执行工作队列方式 */
/*多CPU多个工作队列,传递工作队列名称 返回一个 struct workqueue_struct 结构体指针*/
#define create_workqueue(name)                                  \
        alloc_workqueue((name), WQ_MEM_RECLAIM, 1)
/*仅有一个工作队列,创建一个单线程的工作队列*/        
#define create_singlethread_workqueue(name)                     \
        alloc_workqueue((name), WQ_UNBOUND | WQ_MEM_RECLAIM, 1)
        
/*2.构建一个任务项struct work_struct ,struct work_struct 结构指针和执行函数指针*/
/*运行中首次使用struct work_struct 传递 struct work_struct * 和 执行函数指针*/
#define INIT_WORK(_work, _func)                                 \
        do {                                                    \
                __INIT_WORK((_work), (_func), 0);               \
        } while (0)
/*修改执行函数的宏*/
#define PREPARE_WORK(_work, _func)                              \
        do {                                                    \
                (_work)->func = (_func);                        \
        } while (0)
/*初始化延时的工作任务项*/
#define INIT_DELAYED_WORK(_work, _func)

[0x242] 提交工作任务项

#include <linux/workqueue.h>
/*implement kernel-dir/kernel/workqueue.c*/
/*提交工作任务到工作队列 如果成功返回0 失败返回非零*/
int queue_work(struct workqueue_struct *wq, struct work_struct *work)
/*提交延时工作任务,注意这样的必须是 INIT_DELAY_WORK 初始化的struct delayed_work*/
int queue_delayed_work(struct workqueue_struct *wq,struct delayed_work *dwork, unsigned long delay)

[0x243] 调用执行工作队列

#include <linux/workqueue.h>
/*implement kernel-dir/kernel/workqueue.c*/
/*调用非延时 任务队列项*/
int schedule_work(struct work_struct *work);
/*调用延时 任务队列项*/
int schedule_delayed_work(struct delayed_work *work, unsigned long delay);

[0x243] 销毁

/*销毁工作任务项*/
bool cancel_work_sync(struct work_struct *work);
static inline bool cancel_delayed_work(struct delayed_work *work);
/*保证销毁后不再任何地方运行,应该刷新对应的工作队列*/
void flush_workqueue(struct workqueue_struct *wq);
/*销毁工作队列*/
void destroy_workqueue(struct workqueue_struct *wq);

猜你喜欢

转载自blog.csdn.net/god1992/article/details/85260521