本文为作者原创,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:misteryoung
博客:http://blog.chinaunix.net/uid/20706239
=======================================================================
1 前言
本文sleep的函数依然基于glibc的实现来分析,相对于前面的《ARM版glibc库介绍》,本文除了做用户态的glibc库分析外,还以系统调用为线索跟进内核,看看其内核部分的具体实现。
说明:本文glibc/glibc-ports版本为2.14,Linux版本为2.6.32.11,并基于ARM平台进行分析。
2 sleep函数分析
从glibc的源码可以看到,sleep共包含两种实现方式:
1)glibc-2.14/sysdeps/posix/sleep.c
2)glibc-2.14/sysdeps/unix/sysv/linux/sleep.c
其中,第一种方式是基于SIGALRM信号实现的;而第二种方式是基于
nanosleep系统调用实现的。下面就这两种方式分别介绍。
2.1 信号机制
2.1.1 分析
glibc-2.14/sysdeps/posix/sleep.c
-
static void sleep_handler (int sig)
-
{
-
return;
-
}
-
-
unsigned int __sleep (unsigned int seconds)
-
{
-
unsigned int remaining, slept;
-
time_t before, after;
-
sigset_t set, oset;
-
struct sigaction act, oact;
-
int save = errno;
-
-
if (seconds == 0) 0秒则不做任何处理,直接返回
-
return 0;
-
-
if (sigemptyset (&set) < 0 ||
-
sigaddset (&set, SIGALRM) < 0 ||
-
sigprocmask (SIG_BLOCK, &set, &oset))
-
return seconds;
-
-
act.sa_handler = sleep_handler;
-
act.sa_flags = 0;
-
act.sa_mask = oset; /* execute handler with original mask */
-
if (sigaction (SIGALRM, &act, &oact) < 0) 注册新的SIGALRM信号处理函数,并将老的保存在oact中
-
return seconds;
-
-
before = time ((time_t *) NULL);
-
remaining = alarm (seconds);设置超时时间,超时后执行SIGALRM信号处理函数
-
-
if (remaining > 0 && remaining < seconds)
-
{
-
(void) sigaction (SIGALRM, &oact, (struct sigaction *) NULL);
-
alarm (remaining); /* Restore sooner alarm. */
-
sigsuspend (&oset); /* Wait for it to go off. */
-
after = time ((time_t *) NULL);
-
}
-
else
-
{
-
/* Atomically restore the old signal mask
-
(which had better not block SIGALRM),
-
and wait for a signal to arrive. */
-
这里会阻塞,直到信号到达,也即等到时间超时。
-
sigsuspend (&oset);
-
-
after = time ((time_t *) NULL);
-
-
/* Restore the old signal action state. */恢复为老的信号处理函数
-
(void) sigaction (SIGALRM, &oact, (struct sigaction *) NULL);
-
}
-
-
/* Notice how long we actually slept. */
-
slept = after - before; 当前进程被阻塞的时间间隔,正常情况下等于sleep指定的秒数
-
-
(void) alarm (remaining > slept ? remaining - slept : 0);
-
-
/* Restore the original signal mask. */恢复信号mask
-
(void) sigprocmask (SIG_SETMASK, &oset, (sigset_t *) NULL);
-
-
/* Restore the `errno' value we started with.
-
Some of the calls we made might have failed, but we didn't care. */
-
__set_errno (save);恢复errno
-
-
return slept > seconds ? 0 : seconds - slept;
-
}
-
weak_alias (__sleep, sleep) __sleep是sleep的弱类型别称,如果sleep未定义,则使用__sleep
sleep需要阻塞,而这里的阻塞功能是通过sigsuspend实现。下面分析该函数。
函数sigsuspend是系统调用,我们直接跟进内核态:
-
/*
-
* atomically swap in the new signal mask, and wait for a signal.
-
*/
-
asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask)
-
{
-
mask &= _BLOCKABLE;
-
spin_lock_irq(¤t->sighand->siglock);
-
current->saved_sigmask = current->blocked;
-
siginitset(¤t->blocked, mask);
-
recalc_sigpending();
-
spin_unlock_irq(¤t->sighand->siglock);
-
-
current->state = TASK_INTERRUPTIBLE;可中断休眠,收到信号后会被唤醒
-
schedule(); 让出CPU,并从CPU运行队列移除,等待被信号唤醒
-
set_restore_sigmask();
-
return -ERESTARTNOHAND;
-
}
前面文章《用户态定时器介绍》介绍了alarm函数的内核实现:超时后,内核定时器处理函数it_real_fn会给当前进程发送SIGALRM信号。
而根据上面的分析可知,当前进程阻塞在内核态sys_sigsuspend函数的schedule处。在收到SIGALRM信号后,进程被唤醒。此时sigsuspend从内核态返回用户态,这样sigsuspend就实现了阻塞的作用。
2.1.2 总结
该版本sleep通过alarm + sigsuspend实现阻塞指定时间的功能。其中,后者负责阻塞当前进程,前者负责指定时间过后唤醒当前进程(停止阻塞)。
2.2 定时器机制
2.2.1 分析
glibc-2.14/sysdeps/unix/sysv/linux/sleep.c
-
unsigned int __sleep (unsigned int seconds)
-
{
-
const unsigned int max
-
= (unsigned int) (((unsigned long int) (~((time_t) 0))) >> 1);
-
struct timespec ts;
-
sigset_t set, oset;
-
unsigned int result;
-
-
/* This is not necessary but some buggy programs depend on this. */
-
if (__builtin_expect (seconds == 0, 0))
-
{
-
#ifdef CANCELLATION_P
-
CANCELLATION_P (THREAD_SELF);
-
#endif
-
return 0;
-
}
-
-
ts.tv_sec = 0;
-
ts.tv_nsec = 0;
-
again:
-
if (sizeof (ts.tv_sec) <= sizeof (seconds))
-
{
-
/* Since SECONDS is unsigned assigning the value to .tv_sec can
-
overflow it. In this case we have to wait in steps. */
-
ts.tv_sec += MIN (seconds, max);
-
seconds -= (unsigned int) ts.tv_sec;
-
}
-
else
-
{
-
ts.tv_sec = (time_t) seconds;
-
seconds = 0;
-
}
-
__sigemptyset (&set);
-
__sigaddset (&set, SIGCHLD);
-
if (__sigprocmask (SIG_BLOCK, &set, &oset))
-
return -1;
-
-
/* If SIGCHLD is already blocked, we don't have to do anything. */
-
if (!__sigismember (&oset, SIGCHLD))
-
{
-
int saved_errno;
-
struct sigaction oact;
-
-
__sigemptyset (&set);
-
__sigaddset (&set, SIGCHLD);
-
-
/* We get the signal handler for SIGCHLD. */
-
if (__sigaction (SIGCHLD, (struct sigaction *) NULL, &oact) < 0)
-
{
-
saved_errno = errno;
-
/* Restore the original signal mask. */
-
(void) __sigprocmask (SIG_SETMASK, &oset, (sigset_t *) NULL);
-
__set_errno (saved_errno);
-
return -1;
-
}
-
-
if (oact.sa_handler == SIG_IGN)
-
{
-
-
/* We should leave SIGCHLD blocked. */
-
while (1)
-
{
-
这里是关键的,sleep主要功能由该函数实现(nanosleep系统调用,休眠)
-
result = __nanosleep (&ts, &ts);
-
-
if (result != 0 || seconds == 0)
-
break;
-
-
if (sizeof (ts.tv_sec) <= sizeof (seconds))
-
{
-
ts.tv_sec = MIN (seconds, max);
-
seconds -= (unsigned int) ts.tv_nsec;
-
}
-
}
-
-
saved_errno = errno;
-
/* Restore the original signal mask. */
-
(void) __sigprocmask (SIG_SETMASK, &oset, (sigset_t *) NULL);
-
__set_errno (saved_errno);
-
-
goto out;
-
}
-
-
/* We should unblock SIGCHLD. Restore the original signal mask. */
-
(void) __sigprocmask (SIG_SETMASK, &oset, (sigset_t *) NULL);
-
}
-
这里是关键的,sleep主要功能由该函数实现(nanosleep系统调用,休眠)
-
result = __nanosleep (&ts, &ts);
-
if (result == 0 && seconds != 0)
-
goto again;
-
-
out:
-
if (result != 0)
-
/* Round remaining time. */
-
result = seconds + (unsigned int) ts.tv_sec + (ts.tv_nsec >= 500000000L);
-
-
return result;
-
}
-
weak_alias (__sleep, sleep)
下面再跟一下nanosleep,该函数是系统调用,跟进内核:
-
SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp,
-
struct timespec __user *, rmtp)
-
{
-
struct timespec tu;
-
-
if (copy_from_user(&tu, rqtp, sizeof(tu)))
-
return -EFAULT;
-
-
if (!timespec_valid(&tu))
-
return -EINVAL;
-
-
return hrtimer_nanosleep(&tu, rmtp, HRTIMER_MODE_REL, CLOCK_MONOTONIC);
-
}
再跟踪hrtimer_nanosleep:
-
long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp,
-
const enum hrtimer_mode mode, const clockid_t clockid)
-
{
-
struct restart_block *restart;
-
struct hrtimer_sleeper t;
-
int ret = 0;
-
unsigned long slack;
-
-
slack = current->timer_slack_ns;
-
if (rt_task(current))
-
slack = 0;
-
-
hrtimer_init_on_stack(&t.timer, clockid, mode);
-
hrtimer_set_expires_range_ns(&t.timer, timespec_to_ktime(*rqtp), slack);
-
if (do_nanosleep(&t, mode)) 返回1代表执行成功,后续会返回用户态
-
goto out;
-
-
/* Absolute timers do not update the rmtp value and restart: */
-
if (mode == HRTIMER_MODE_ABS) {
-
ret = -ERESTARTNOHAND;
-
goto out;
-
}
-
-
if (rmtp) {
-
ret = update_rmtp(&t.timer, rmtp);
-
if (ret <= 0)
-
goto out;
-
}
-
-
restart = ¤t_thread_info()->restart_block;
-
restart->fn = hrtimer_nanosleep_restart;
-
restart->nanosleep.index = t.timer.base->index;
-
restart->nanosleep.rmtp = rmtp;
-
restart->nanosleep.expires = hrtimer_get_expires_tv64(&t.timer);
-
-
ret = -ERESTART_RESTARTBLOCK;若执行失败则再重新执行一次
-
out:
-
destroy_hrtimer_on_stack(&t.timer);
-
return ret;
-
}
再跟踪
do_nanosleep:
-
static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode)
-
{
-
hrtimer_init_sleeper(t, current);初始化函数,后面分析
-
-
do {
-
set_current_state(TASK_INTERRUPTIBLE); 可被信号唤醒
-
hrtimer_start_expires(&t->timer, mode); 启动高精度定时器
-
-
if (!hrtimer_active(&t->timer))
-
t->task = NULL;
-
-
if (likely(t->task))
-
schedule(); 休眠,并等待被定时器或信号唤醒
-
-
hrtimer_cancel(&t->timer);
-
mode = HRTIMER_MODE_ABS;
-
-
} while (t->task && !signal_pending(current));
-
若超时时间到,则t->task被设置为NULL,退出循环;若收到信号也会退出循环。
-
-
__set_current_state(TASK_RUNNING);
-
是否超时?若是,说明进程已休眠了指定的时间,可以安全返回到用户态;否则(被信号唤醒),函数hrtimer_nanosleep会做特殊处理。所谓的特殊处理就是通过返回ERESTART_RESTARTBLOCK,使得nanosleep系统调用(sys_nanosleep)在返回用户态前会被重新启动,直到执行成功。
-
return t->task == NULL;
-
}
初始化函数:
-
-
void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *task)
-
{
-
sl->timer.function = hrtimer_wakeup;该函数在定时器到期时负责唤醒进程
-
sl->task = task; 被唤醒的进程
-
}
下面再分析一下高精度定时器是如何运行的:
-
xx_timer_interrupt 时钟中断(硬中断)响应函数(底层驱动,CPU相关)
-
-> tick_handle_periodic
-
-> tick_periodic
-
-> update_process_times
-
-> run_local_timers
-
-> hrtimer_run_queues Called from hardirq context every jiffy
-
-> __run_hrtimer
-
-> hrtimer_wakeup
-
->raise_softirq(TIMER_SOFTIRQ); 该函数触发(非执行)普通定时器
继续跟踪hrtimer_wakeup:
-
static enum hrtimer_restart hrtimer_wakeup(struct hrtimer *timer)
-
{
-
struct hrtimer_sleeper *t =
-
container_of(timer, struct hrtimer_sleeper, timer);
-
struct task_struct *task = t->task;
-
-
t->task = NULL;设置为NULL
-
if (task)
-
wake_up_process(task);唤醒因执行sleep而休眠的进程
-
-
return HRTIMER_NORESTART;
-
}
2.2.2 总结
该版本sleep通过
nanosleep系统调用陷入内核并休眠,指定时间到达时,再返回用户态,从而达到sleep的目的。
阅读(3697) | 评论(0) | 转发(0) |