Time Management

1. two components of  Time management:

a. the ability to measure time.

b. the abililty to control when and how your driver code gets executed. Include delaying(代码中添加延时功能) and defering(延期执行函数)

Delaying:  which involves pausing execution of your code for a specific amount of time, or until a specific condition is met.

Defering:  which involves scheduling execution of a function at some point in the future.

There are a number of strategies available for delaying or defering execution.

a. Long delay: more than one jiffies.

b. short delay: less than one jiffies, for dealing with hardware latencies, etc.

c. don't care about time, just want to defer execution to a more convenient  time.

--- the best option depends on the nature of the delay needed.

2. Long Delays

Long delays are considered as delays which extend beyond a jiffy.

2.1 timeouts:

The optimal way to implement fixed delays of this nature is to leverage a kernel facility called timeouts

--- timeouts delay execution for a specified number of jiffies.

--- Kernel can schedule other work on the CPU while your driver code waits for the timeout to expire.

schedule_timeout_interruptible(HZ);  // delay for one second, HZ jiffies means one second.
schedule_timeout_uninterruptible();  --- sleep for the specified number of jiffies. Once sleep, the process cannot be interrupted.

                                                                      --- you should consider using interruptible (or killable at least) version instead.

signed long __sched schedule_timeout_uninterruptible(signed long timeout)
{
	__set_current_state(TASK_UNINTERRUPTIBLE);
	return schedule_timeout(timeout);
}
EXPORT_SYMBOL(schedule_timeout_uninterruptible);
schedule_timeout_killabe();   --- sleep for a specified number of jiffies, this can be interrupted by fatal signals only.

signed long __sched schedule_timeout_killable(signed long timeout)
{
	__set_current_state(TASK_KILLABLE);
	return schedule_timeout(timeout);
}
EXPORT_SYMBOL(schedule_timeout_killable);
shecdule_timeout_interruptible();  ---sleep for a specified number of jiffies, this friendly version can be interrupted by any signals.

/*
 * We can use __set_current_state() here because schedule_timeout() calls
 * schedule() unconditionally.
 */
signed long __sched schedule_timeout_interruptible(signed long timeout)
{
	__set_current_state(TASK_INTERRUPTIBLE);
	return schedule_timeout(timeout);
}
EXPORT_SYMBOL(schedule_timeout_interruptible);
2.2 Wait queue
But what if you need to wait until a specific condition is met, rather than a specific amount of time.

wait queues are useful for implementing conditional delays which are anticipated to be long.

wait queue enables a process to sleep until someone else "wake up" the wait queue.

--- the process will go back to sleep if the specified condition is not met.

wait queue define:

struct __wait_queue {
	unsigned int flags;
#define WQ_FLAG_EXCLUSIVE	0x01
	void *private;
	wait_queue_func_t func;
	struct list_head task_list;
};
typedef struct __wait_queue wait_queue_t;
declaration and initialisation a wait queue:

#define DECLARE_WAITQUEUE(name, tsk)					\
	wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)
#define __WAITQUEUE_INITIALIZER(name, tsk) {				\
	.private	= tsk,						\
	.func		= default_wake_function,			\
	.task_list	= { NULL, NULL } }
wait queues present the following interfaces:

wait_event(wq, condition); --- wait until a condition is true, once asleep the process cannot be interrupted,

                                                --- you should consider using interruptible(or killable at least) version instead.

/**
 * wait_event - sleep until a condition gets true
 * @wq: the waitqueue to wait on
 * @condition: a C expression for the event to wait for
 *
 * The process is put to sleep (TASK_UNINTERRUPTIBLE) until the
 * @condition evaluates to true. The @condition is checked each time
 * the waitqueue @wq is woken up.
 *
 * wake_up() has to be called after changing any variable that could
 * change the result of the wait condition.
 */
#define wait_event(wq, condition) 					\
do {									\
	if (condition)	 						\
		break;							\
	__wait_event(wq, condition);					\
} while (0)
#define __wait_event(wq, condition) 					\
do {									\
	DEFINE_WAIT(__wait);						\
									\
	for (;;) {							\
		prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);	\
		if (condition)						\
			break;						\
		schedule();						\
	}								\
	finish_wait(&wq, &__wait);					\
} while (0)
wait_event_timeout(wq, condition, timeout); --- similar to wait_event(), but will eventually time out after a specified number of jiffies.

/**
 * wait_event_timeout - sleep until a condition gets true or a timeout elapses
 * @wq: the waitqueue to wait on
 * @condition: a C expression for the event to wait for
 * @timeout: timeout, in jiffies
 *
 * The process is put to sleep (TASK_UNINTERRUPTIBLE) until the
 * @condition evaluates to true. The @condition is checked each time
 * the waitqueue @wq is woken up.
 *
 * wake_up() has to be called after changing any variable that could
 * change the result of the wait condition.
 *
 * The function returns 0 if the @timeout elapsed, and the remaining
 * jiffies if the condition evaluated to true before the timeout elapsed.
 */
#define wait_event_timeout(wq, condition, timeout)			\
({									\
	long __ret = timeout;						\
	if (!(condition)) 						\
		__wait_event_timeout(wq, condition, __ret);		\
	__ret;								\
})
#define __wait_event_timeout(wq, condition, ret)			\
do {									\
	DEFINE_WAIT(__wait);						\
									\
	for (;;) {							\
		prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);	\
		if (condition)						\
			break;						\
		ret = schedule_timeout(ret);				\
		if (!ret)						\
			break;						\
	}								\
	finish_wait(&wq, &__wait);					\
} while (0)
wait_event_killable(wq, condition); --- wait until condition is true. this can be interrupted by fatal signals only.

wait_event_interruptible(wq, condition); --- wait until condition is true. This friendly version can be interrupted by any signals.

wait_event_interruptible_timeout(wq, condition,timeout);  --- similar to wait_event_interruptible(), but will be time out after a specified number of jiffies.

3. short delay:
short delays are considered to be delays which do not extend beyond one jiffies.

--- because sub-jiffy precision is required, timeouts are not suitable for these cases.

--- short delays can be further subdivided based on this requirements

a. very short delay, where a relatively high level of precision is desired, and sleeping is not tolerated.

b. short delays in the order of milliseconds, where high precision is not required.

3.1 very short delay

these type of delays can be accomplished by using one of the following functions:

mdelay(d);  --- delay for d mlliseconds.

udelay(d);   --- delay for d microseconds.

ndelay(d); --- delay for d nanoseconds. Note that most hardware does not deliver nanosecond resolution.
Note that mdelay(), udelay(), ndelay() are implemented using a technique called busy waiting.

Busy waiting essentially involves repetitively(重复的,不停的) checking a condition until it is satisfied, without relinquish the processor. This has the following advantages:

--- relatively high precision is achieved when delays are in the order of microseconds or greater.

--- funcitons can be used anywhere in your driver.

Busy waiting has negative effects however:

--- the CPU is unable to perform other useful work for the duration of the wait, therefore busy wait should be used sparingly(保守的,节约的,较少的).

3.2 msleep()

Due to the disadvantages associated with busy waiting, sometimes we should avoid it. For this reason, it's preferable to use msleep().

msleep() ---blocks the caller for at least the specified number of milliseconds, but maybe longer. 

/**
 * msleep - sleep safely even with waitqueue interruptions
 * @msecs: Time in milliseconds to sleep for
 */
void msleep(unsigned int msecs)
{
	unsigned long timeout = msecs_to_jiffies(msecs) + 1;

	while (timeout)
		timeout = schedule_timeout_uninterruptible(timeout);
}

EXPORT_SYMBOL(msleep);
you know block means sleep, cpu will go to process other process. that's the difference with delay().

3.3 cpu_relax()

If you need a short delay based on a condition, rather than a fixed amount of time, you should use cpu_relax().

--- in this case, you will have little choice but to busy wait.  That is when you simply cannot go to sleep(in a interrupt handler, for example), and when you anticipate the wait times to be very short.

cpu_relax() is an architecture-specific function useful for implementing busy waits in a way that can lower CPU power consumption or yield to a hyperthreaded twin processor.

4. Defering execution

Defering execution essentially means asking the kernel to call one of your functions at a later time.

three constructs are available to defer execution, depending on your need.

a. kernel timers --- which request the kernel to call your function at a specific time. The specific function must be atomic.(that is, it cannot sleep)

b. Tasklet --- which request the kernel to call your function at a later time. The specific function must be atomic.(that is, it cannot sleep)

c. work queue --- which request the kernel to call your function at a later time. The specific function does not need to be atomic.

4.1 kernel timers

kernel timers are constructs which schedule the asynchronous execution of a function at a specific time.

useful in situations such as:

--- polling hardware which cannot generate interrupts.

code in kernel timer function is similar to an interrupt handler.

--- no sleeping or scheduling.

--- no access to user-space. In fact, kernel timer functions are run in interrupt context, with no associated process.

kernel timers present the following interfaces:

struct timer_list --- timer data structure,

struct timer_list {
	/*
	 * All fields that change during normal runtime grouped to the
	 * same cacheline
	 */
	struct list_head entry;
	unsigned long expires;       // absolutly time (in jiffies) to call function.
	struct tvec_base *base;

	void (*function)(unsigned long);   //pointer to the function to call.
	unsigned long data;         //private data parameter to pass to function.

	int slack;

#ifdef CONFIG_TIMER_STATS
	void *start_site;
	char start_comm[16];
	int start_pid;
#endif
#ifdef CONFIG_LOCKDEP
	struct lockdep_map lockdep_map;
#endif
};
init_timer(timer) ; --- Used to initialize a dynamically allocated timer

#define init_timer(timer)						\
	do {								\
		static struct lock_class_key __key;			\
		init_timer_key((timer), #timer, &__key);		\
	} while (0)
/**
 * init_timer_key - initialize a timer
 * @timer: the timer to be initialized
 * @name: name of the timer
 * @key: lockdep class key of the fake lock used for tracking timer
 *       sync lock dependencies
 *
 * init_timer_key() must be done to a timer prior calling *any* of the
 * other timer functions.
 */
void init_timer_key(struct timer_list *timer,
		    const char *name,
		    struct lock_class_key *key)
{
	debug_init(timer);
	__init_timer(timer, name, key);
}
EXPORT_SYMBOL(init_timer_key);
static void __init_timer(struct timer_list *timer,
			 const char *name,
			 struct lock_class_key *key)
{
	timer->entry.next = NULL;
	timer->base = __raw_get_cpu_var(tvec_bases);
	timer->slack = -1;
#ifdef CONFIG_TIMER_STATS
	timer->start_site = NULL;
	timer->start_pid = -1;
	memset(timer->start_comm, 0, TASK_COMM_LEN);
#endif
	lockdep_init_map(&timer->lockdep_map, name, key, 0);
}
TIMER_INITIALIZER(_function, _expires, _data); --- Used to initialize a statically allocated timer
#define TIMER_INITIALIZER(_function, _expires, _data) {		\
		.entry = { .prev = TIMER_ENTRY_STATIC },	\
		.function = (_function),			\
		.expires = (_expires),				\
		.data = (_data),				\
		.base = &boot_tvec_bases,			\
		__TIMER_LOCKDEP_MAP_INITIALIZER(		\
			__FILE__ ":" __stringify(__LINE__))	\
	}
add_timer(struct timer_list *timer); --- activate a timer.

/**
 * add_timer - start a timer
 * @timer: the timer to be added
 *
 * The kernel will do a ->function(->data) callback from the
 * timer interrupt at the ->expires point in the future. The
 * current time is 'jiffies'.
 *
 * The timer's ->expires, ->function (and if the handler uses it, ->data)
 * fields must be set prior calling this function.
 *
 * Timers with an ->expires field in the past will be executed in the next
 * timer tick.
 */
void add_timer(struct timer_list *timer)
{
	BUG_ON(timer_pending(timer));
	mod_timer(timer, timer->expires);
}
EXPORT_SYMBOL(add_timer);
int mod_timer(struct timer_list *timer, unsigned long expires); --- Modify expires value for a pending timer.

/**
 * mod_timer - modify a timer's timeout
 * @timer: the timer to be modified
 * @expires: new timeout in jiffies
 *
 * mod_timer() is a more efficient way to update the expire field of an
 * active timer (if the timer is inactive it will be activated)
 *
 * mod_timer(timer, expires) is equivalent to:
 *
 *     del_timer(timer); timer->expires = expires; add_timer(timer);
 *
 * Note that if there are multiple unserialized concurrent users of the
 * same timer, then mod_timer() is the only safe way to modify the timeout,
 * since add_timer() cannot modify an already running timer.
 *
 * The function returns whether it has modified a pending timer or not.
 * (ie. mod_timer() of an inactive timer returns 0, mod_timer() of an
 * active timer returns 1.)
 */
int mod_timer(struct timer_list *timer, unsigned long expires)
{
	/*
	 * This is a common optimization triggered by the
	 * networking code - if the timer is re-modified
	 * to be the same thing then just return:
	 */
	if (timer_pending(timer) && timer->expires == expires)
		return 1;

	expires = apply_slack(timer, expires);

	return __mod_timer(timer, expires, false, TIMER_NOT_PINNED);
}
EXPORT_SYMBOL(mod_timer);
int del_timer(struct timer_list *timer); --- remove a pending timer.

/**
 * del_timer - deactive a timer.
 * @timer: the timer to be deactivated
 *
 * del_timer() deactivates a timer - this works on both active and inactive
 * timers.
 *
 * The function returns whether it has deactivated a pending timer or not.
 * (ie. del_timer() of an inactive timer returns 0, del_timer() of an
 * active timer returns 1.)
 */
int del_timer(struct timer_list *timer)
{
	struct tvec_base *base;
	unsigned long flags;
	int ret = 0;

	timer_stats_timer_clear_start_info(timer);
	if (timer_pending(timer)) {
		base = lock_timer_base(timer, &flags);
		if (timer_pending(timer)) {
			detach_timer(timer, 1);
			if (timer->expires == base->next_timer &&
			    !tbase_get_deferrable(timer->base))
				base->next_timer = base->timer_jiffies;
			ret = 1;
		}
		spin_unlock_irqrestore(&base->lock, flags);
	}

	return ret;
}
EXPORT_SYMBOL(del_timer);
int del_timer_sync(struct timer_list *timer); --- similar to del_timer(), but guarantees that your function is not running when it returns.

/**
 * del_timer_sync - deactivate a timer and wait for the handler to finish.
 * @timer: the timer to be deactivated
 *
 * This function only differs from del_timer() on SMP: besides deactivating
 * the timer it also makes sure the handler has finished executing on other
 * CPUs.
 *
 * Synchronization rules: Callers must prevent restarting of the timer,
 * otherwise this function is meaningless. It must not be called from
 * interrupt contexts. The caller must not hold locks which would prevent
 * completion of the timer's handler. The timer's handler must not call
 * add_timer_on(). Upon exit the timer is not queued and the handler is
 * not running on any CPU.
 *
 * The function returns whether it has deactivated a pending timer or not.
 */
int del_timer_sync(struct timer_list *timer)
{
#ifdef CONFIG_LOCKDEP
	unsigned long flags;

	local_irq_save(flags);
	lock_map_acquire(&timer->lockdep_map);
	lock_map_release(&timer->lockdep_map);
	local_irq_restore(flags);
#endif

	for (;;) {
		int ret = try_to_del_timer_sync(timer);
		if (ret >= 0)
			return ret;
		cpu_relax();
	}
}
EXPORT_SYMBOL(del_timer_sync);
4.2 tasklet

4.3 work queue

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值