文章目录
我爱学习,爱代码,代码让我更加专注,虽然已经不是早上了,有点分心,不能再分心了
必须为自己选择的道路负责起来;加油!
[0x100] 内容概述
- 内核定时器
- 软中断机制
[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] 计数值的转换
- struct timeval :使用秒和毫秒值标识时间;
- struct timespec :使用秒和纳秒值标识时间;
- 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);