- Measure time Difference
- The kernel tracks the time stream with timer interrupts
- Clock interrupts are generated at periodic intervals by the system's timing hardware, which is set by the kernel according to the Hz value, which is defined by default at 1000 on the common x86 PC platform.
- <linux/param.h>
- <linux/timex.h>
- Jiffies_64
- unsigned long jiffies
- Use jiffies counter
- <linux/jiffies.h>
- 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);
- usually only needs to contain <linux/sched.h>
- diff = (long) T2-(long) t1
- msec = diff * 1000/hz
- <linux/times.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);
- u64 get_jiffies_64 (void);
- <linux/types.h>
- proc/interrupts
- Processor-specific Registers
- The most famous counter register is the TSC
- <asm/msr.h>
- RDTSC (Low32, High32);
- RDTSCL (LOW32);
- RDTSCL1 (VAR64);
- <linux/timex.h>
- cycles_t get_cycles (void);
- Define RDTSCL (dest) __asm__ __volatile__ ("Mfs0%0,$9; NOP ":" =r "(dest))
- Get current time
- The kernel provides functions that convert wall clock time to jiffies values
- <linux/time.h>
- unsigned long mktime (unsigned int year, unsigned int mon, unsigned int day, unsigned int hour, unsigned int min, unsigned int sec);
- void Do_gettimeofday (struct timeval *tv);
- struct Timespec current_kernel_time (void);
- Deferred execution
- long delay
- busy waiting
- while (Time_before (Jiffies, J1)) Cpu_relax ();
- yield processor
- voluntarily frees CPU when no CPU is required
- <linux/sched.h>
- while (Time_before ( Jiffies, J1)) schedule ();
- Timeout
- <linux/wait.h>
- long wait_event_timeout (wait_queue_head_t Q, Condition c, long timeout);
- Long Wait_event_interruptible_timeout (wait_queue_head_t Q, condition C, long timeout);
The
- timeout value represents the Jiffies value to wait, not an absolute time value
- If the timeout expires, two functions return 0, and if the process wakes up by another event, the remaining deferred implementation is returned
- <linux/sched.h>
- signed Long Schedule_timeout (signed long timeout);
- set_current_state (task_interruptible);
- schedule_timeout (delay);
- Short delay
- <linux/delay.h>
- void Ndelay (unsigned long nsecs);
- void Udelay (unsigned long usecs);
- void Mdelay (unsigned long msecs);
- These three delay functions are all busy wait functions
- unsigned long msleep_interruptible (unsigned int millisecs);
- void Ssleep (unsigned int seconds);
- Kernel timers
- A kernel timer is a data structure that tells the kernel to use user-defined parameters to execute a user-defined function at a user-defined point in time
- Kernel timers are often run as a result of a "software outage"
- If you are outside the process context, you must follow these rules
- Do not allow access to user space
- The current pointer is meaningless in atomic mode and is not available.
- Cannot perform hibernation or dispatch
- <asm/hardirq.h>
- In_interrupt ()
- In_atomic ()
- Tasks can register themselves to rerun at a later time
- Timers can be a potential source of race even on a single-processor system
- Timer API
- <linux/timer.h>
- struct timer_list
- unsigned long expires;< /li>
- Void (*function) (unsigned long);
- unsigned long data;
- void Init_timer (struct timer_list *time);
- struct timer_list timer_initializer (_function, _expires, _data);
- void Add_timer (struct timer_list *timer);
- int Del_timer (struct timer_list *timer);
The
- expires field represents the Jiffies value of the expected timer execution
- int Mod_timer (struct timer_list *timer, unsigned long expires);
- int Del_timer_sync (struct timer_list *timer);
- int timer_pending (const struct timer_list *timer);
- Implementation of the kernel timer
- The implementation of the kernel timer should meet the following requirements and assumptions
- The management of timers must be as lightweight as possible
- The design must be very flexible when the active timer increases greatly.
- Most timers expire in up to a few seconds or minutes, and few long-term delay timers
- The timer should run on the same CPU that registered it
- Whenever the kernel code registers a timer, its operation is eventually performed by Internal_add_timer (defined in kernel/timer.c)
- Cascading tables work The following way
- If the timer expires in the next 0~255 Jiffiew, the timer will be added to one of the 256 lists (depending on the low 8-bit value of the Expires field)
- If the timer expires in the farther future (but before 16,384 jiffies), then the timer will be added to one of the 64 linked lists (depending on the 9~14 bit of the Expires field)
- For further future timers, the same techniques are used for 15~20, 21~26, and 27~31 bits
- If the Expires field of the timer represents a further future, the delay 0xfffffff is used for hashing, and the timer expires in the past time is dispatched at the next timer tick
- When __run_times is fired, it executes all the pending timers on the current timer tick
- Tasklet
- This mechanism is used extensively in interrupt management
- Always runs during an outage and always runs on the same CPU that dispatches it, receiving a unsigned long parameter
- Cannot require Tasklet to execute at a given time
- <linux/interrupt.h>
- struct TASKLET_STRUCT
- void (*func) (unsigned long);
- unsigned long data;
- void Tasklet_init (struct tasklet_struct *t, void (*func) (unsigned long), unsigned long data);
- Declare_tasklet (Name, func, data);
- Declare_tasklet_disabled (Name, func, data);
- Interesting features
- A tasklet can be disabled or re-enabled at a later time, and Tasklet will be executed only if the number of times and the number of prohibitions are enabled.
- Similar to timers, Tasklet can register itself
- Tasklet can be dispatched to perform at the usual priority or high priority level
- If the system load is not heavy, Tasklet will be executed immediately, but never later than the next timer tick
- A tasklet can be concurrent with other tasklet, but it is strictly serial for itself
- void tasklet_disable (struct tasklet_struct *t);
- void Tasklet_disable_nosync (struct tasklet_struct *t);
- void tasklet_enable (struct tasklet_struct *t);
- void Tasklet_schedule (struct tasklet_struct *t);
- void Tasklet_hi_schedule (struct tasklet_struct *t);
- void Tasklet_kill (struct tasklet_struct *t);
- The implementation of Tasklet in KERNEL/SOFTIRQ.C
- Work queue
- Difference from Tasklet
- Tasklet runs in the context of software interrupts, so all Tasklet code must be atomic. Instead, work queue functions run in the context of a particular kernel process, so they have better flexibility. In particular, the work queue function can hibernate
- Tasklet always runs on the same processor that was initially committed, but this is the default for work queues
- The kernel code can request the execution of a work queue function to delay a given time interval
- Work queue functions can have longer delays and do not have to be atomized
- <linux/workqueue.h>
- struct workqueue_struct
- struct workqueue_struct *create_workqueue (const char *name);
- struct workqueue_struct *create_singlethread_workqueue (const char *name);
- struct Work_struct
- 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);
- The return value of the above two functions is nonzero means that the given work_struct structure is already waiting in the queue
- int cancel_delayed_work (struct work_struct *work);
- The entry is canceled before execution starts, and a non-0 value is returned
- void Flush_workqueue (struct workqueue_struct *queue);
- void Destroy_workqueue (struct workqueue_struct *queue);
- Shared queues
- int schedule_work (struct work_struct *work);
- void Flush_scheduled_work (void)
Summary: 1. Linux is based on clock interrupt tracking, system time flow.
2. Timers, tasks must be followed in the atomic context, the timer can specify the future scheduling time, the task cannot specify the time of execution.
3. The work queue, the scheduled function can hibernate.
"Linux Device Drivers" The seventh chapter time, delay and delay operation--note