ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
## 7.7. 快速参考 本章介绍了下面的符号. ### 7.7.1. 时间管理 ~~~ #include <linux/param.h> HZ ~~~ HZ 符号指定了每秒产生的时钟嘀哒的数目. ~~~ #include <linux/jiffies.h> volatile unsigned long jiffies; u64 jiffies_64; ~~~ jiffies_64 变量每个时钟嘀哒时被递增; 因此, 它是每秒递增 HZ 次. 内核代码几乎常常引用 jiffies, 它在 64-位平台和 jiffies_64 相同并且在 32-位平台是它低有效的一半. ~~~ int time_after(unsigned long a, unsigned long b); int time_before(unsigned long a, unsigned long b); int time_after_eq(unsigned long a, unsigned long b); int time_before_eq(unsigned long a, unsigned long b); ~~~ 这些布尔表达式以一种安全的方式比较 jiffies, 没有万一计数器溢出的问题和不需要存取 jiffies_64. ~~~ u64 get_jiffies_64(void); ~~~ 获取 jiffies_64 而没有竞争条件. ~~~ #include <linux/time.h> unsigned long timespec_to_jiffies(struct timespec *value); void jiffies_to_timespec(unsigned long jiffies, struct timespec *value); unsigned long timeval_to_jiffies(struct timeval *value); void jiffies_to_timeval(unsigned long jiffies, struct timeval *value); ~~~ 在 jiffies 和其他表示之间转换时间表示. ~~~ #include <asm/msr.h> rdtsc(low32,high32); rdtscl(low32); rdtscll(var32); ~~~ x86-特定的宏定义来读取时戳计数器. 它们作为 2 半 32-位来读取, 只读低一半, 或者全部读到一个 long long 变量. ~~~ #include <linux/timex.h> cycles_t get_cycles(void); ~~~ 以平台独立的方式返回时戳计数器. 如果 CPU 没提供时戳特性, 返回 0. ~~~ #include <linux/time.h> unsigned long mktime(year, mon, day, h, m, s); ~~~ 返回自 Epoch 以来的秒数, 基于 6 个 unsigned int 参数. ~~~ void do_gettimeofday(struct timeval *tv); ~~~ 返回当前时间, 作为自 Epoch 以来的秒数和微秒数, 用硬件能提供的最好的精度. 在大部分的平台这个解决方法是一个微秒或者更好, 尽管一些平台只提供 jiffies 精度. ~~~ struct timespec current_kernel_time(void); ~~~ 返回当前时间, 以一个 jiffy 的精度. ### 7.7.2. 延迟 ~~~ #include <linux/wait.h> long wait_event_interruptible_timeout(wait_queue_head_t *q, condition, signed long timeout); ~~~ 使当前进程在等待队列进入睡眠, 安装一个以 jiffies 表达的超时值. 使用 schedule_timeout( 下面) 给不可中断睡眠. ~~~ #include <linux/sched.h> signed long schedule_timeout(signed long timeout); ~~~ 调用调度器, 在确保当前进程在超时到的时候被唤醒后. 调用者首先必须调用 set_curret_state 来使自己进入一个可中断的或者不可中断的睡眠状态. ~~~ #include <linux/delay.h> void ndelay(unsigned long nsecs); void udelay(unsigned long usecs); void mdelay(unsigned long msecs); ~~~ 引入一个整数纳秒, 微秒和毫秒的延迟. 获得的延迟至少是请求的值, 但是可能更多. 每个函数的参数必须不超过一个平台特定的限制(常常是几千). ~~~ void msleep(unsigned int millisecs); unsigned long msleep_interruptible(unsigned int millisecs); void ssleep(unsigned int seconds); ~~~ 使进程进入睡眠给定的毫秒数(或者秒, 如果使 ssleep). ### 7.7.3. 内核定时器 ~~~ #include <asm/hardirq.h> int in_interrupt(void); int in_atomic(void); ~~~ 返回一个布尔值告知是否调用代码在中断上下文或者原子上下文执行. 中断上下文是在一个进程上下文之外, 或者在硬件或者软件中断处理中. 原子上下文是当你不能调度一个中断上下文或者一个持有一个自旋锁的进程的上下文. ~~~ #include <linux/timer.h> void init_timer(struct timer_list * timer); struct timer_list TIMER_INITIALIZER(_function, _expires, _data); ~~~ 这个函数和静态的定时器结构的声明是初始化一个 timer_list 数据结构的 2 个方法. ~~~ void add_timer(struct timer_list * timer); ~~~ 注册定时器结构来在当前 CPU 上运行. ~~~ int mod_timer(struct timer_list *timer, unsigned long expires); ~~~ 改变一个已经被调度的定时器结构的超时时间. 它也能作为一个 add_timer 的替代. ~~~ int timer_pending(struct timer_list * timer); ~~~ 宏定义, 返回一个布尔值说明是否这个定时器结构已经被注册运行. ~~~ void del_timer(struct timer_list * timer); void del_timer_sync(struct timer_list * timer); ~~~ 从激活的定时器链表中去除一个定时器. 后者保证这定时器当前没有在另一个 CPU 上运行. ### 7.7.4. Tasklets 机制 ~~~ #include <linux/interrupt.h> DECLARE_TASKLET(name, func, data); DECLARE_TASKLET_DISABLED(name, func, data); void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data); ~~~ 前 2 个宏定义声明一个 tasklet 结构, 而 tasklet_init 函数初始化一个已经通过分配或其他方式获得的 tasklet 结构. 第 2 个 DECLARE 宏标识这个 tasklet 为禁止的. ~~~ void tasklet_disable(struct tasklet_struct *t); void tasklet_disable_nosync(struct tasklet_struct *t); void tasklet_enable(struct tasklet_struct *t); ~~~ 禁止和使能一个 tasklet. 每个禁止必须配对一个使能( 你可以禁止这个 tasklet 即便它已经被禁止). 函数 tasklet_disable 等待 tasklet 终止如果它在另一个 CPU 上运行. 这个非同步版本不采用这个额外的步骤. ~~~ void tasklet_schedule(struct tasklet_struct *t); void tasklet_hi_schedule(struct tasklet_struct *t); ~~~ 调度一个 tasklet 运行, 或者作为一个"正常" tasklet 或者一个高优先级的. 当软中断被执行, 高优先级 tasklets 被首先处理, 而正常 tasklet 最后执行. ~~~ void tasklet_kill(struct tasklet_struct *t); ~~~ 从激活的链表中去掉 tasklet, 如果它被调度执行. 如同 tasklet_disable, 这个函数可能在 SMP 系统中阻塞等待 tasklet 终止, 如果它当前在另一个 CPU 上运行. ### 7.7.5. 工作队列 ~~~ #include <linux/workqueue.h> struct workqueue_struct; struct work_struct; ~~~ 这些结构分别表示一个工作队列和一个工作入口. ~~~ struct workqueue_struct *create_workqueue(const char *name); struct workqueue_struct *create_singlethread_workqueue(const char *name); void destroy_workqueue(struct workqueue_struct *queue); ~~~ 创建和销毁工作队列的函数. 一个对 create_workqueue 的调用创建一个有一个工作者线程在系统中每个处理器上的队列; 相反, create_singlethread_workqueue 创建一个有一个单个工作者进程的工作队列. ~~~ DECLARE_WORK(name, void (*function)(void *), void *data); INIT_WORK(struct work_struct *work, void (*function)(void *), void *data); PREPARE_WORK(struct work_struct *work, void (*function)(void *), void *data); ~~~ 声明和初始化工作队列入口的宏. int queue_work(struct workqueue_struct *queue, struct work_struct *work);int queue_delayed_work(struct workqueue_struct *queue, struct work_struct *work, unsigned long delay); 从一个工作队列对工作进行排队执行的函数. ~~~ int cancel_delayed_work(struct work_struct *work); void flush_workqueue(struct workqueue_struct *queue); ~~~ 使用 cancel_delayed_work 来从一个工作队列中去除入口; flush_workqueue 确保没有工作队列入口在系统中任何地方运行. ~~~ int schedule_work(struct work_struct *work); int schedule_delayed_work(struct work_struct *work, unsigned long delay); void flush_scheduled_work(void); ~~~ 使用共享队列的函数.