Chinaunix首页 | 论坛 | 博客
  • 博客访问: 57114
  • 博文数量: 7
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 90
  • 用 户 组: 普通用户
  • 注册时间: 2015-05-10 20:54
个人简介

多学习,多分享!

文章分类
文章存档

2018年(7)

我的朋友

分类: LINUX

2018-09-03 23:22:42

本文为作者原创,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者: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

点击(此处)折叠或打开

  1. static void sleep_handler (int sig)
  2. {
  3.   return;
  4. }

  5. unsigned int __sleep (unsigned int seconds)
  6. {
  7.   unsigned int remaining, slept;
  8.   time_t before, after;
  9.   sigset_t set, oset;
  10.   struct sigaction act, oact;
  11.   int save = errno;

  12.   if (seconds == 0)    0秒则不做任何处理,直接返回
  13.     return 0;
  14.    
  15.   if (sigemptyset (&set) < 0 ||
  16.       sigaddset (&set, SIGALRM) < 0 ||
  17.       sigprocmask (SIG_BLOCK, &set, &oset))
  18.     return seconds;

  19.   act.sa_handler = sleep_handler;
  20.   act.sa_flags = 0;
  21.   act.sa_mask = oset;    /* execute handler with original mask */
  22.   if (sigaction (SIGALRM, &act, &oact) < 0) 注册新的SIGALRM信号处理函数,并将老的保存在oact中
  23.     return seconds;

  24.   before = time ((time_t *) NULL);
  25.   remaining = alarm (seconds);设置超时时间,超时后执行SIGALRM信号处理函数

  26.   if (remaining > 0 && remaining < seconds)
  27.     {
  28.       (void) sigaction (SIGALRM, &oact, (struct sigaction *) NULL);
  29.       alarm (remaining);    /* Restore sooner alarm. */
  30.       sigsuspend (&oset);    /* Wait for it to go off. */
  31.       after = time ((time_t *) NULL);
  32.     }
  33.   else
  34.     {
  35.       /* Atomically restore the old signal mask
  36.      (which had better not block SIGALRM),
  37.      and wait for a signal to arrive. */
  38.     这里会阻塞,直到信号到达,也即等到时间超时。
  39.       sigsuspend (&oset);

  40.       after = time ((time_t *) NULL);

  41.       /* Restore the old signal action state. */恢复为老的信号处理函数
  42.       (void) sigaction (SIGALRM, &oact, (struct sigaction *) NULL);
  43.     }

  44.   /* Notice how long we actually slept. */
  45.   slept = after - before; 当前进程被阻塞的时间间隔,正常情况下等于sleep指定的秒数

  46.   (void) alarm (remaining > slept ? remaining - slept : 0);

  47.   /* Restore the original signal mask. */恢复信号mask
  48.   (void) sigprocmask (SIG_SETMASK, &oset, (sigset_t *) NULL);

  49.   /* Restore the `errno' value we started with.
  50.      Some of the calls we made might have failed, but we didn't care. */
  51.   __set_errno (save);恢复errno

  52.   return slept > seconds ? 0 : seconds - slept;
  53. }
  54. weak_alias (__sleep, sleep) __sleepsleep的弱类型别称,如果sleep未定义,则使用__sleep
     sleep需要阻塞,而这里的阻塞功能是通过sigsuspend实现。下面分析该函数。

    函数sigsuspend是系统调用,我们直接跟进内核态:

点击(此处)折叠或打开

  1. /*
  2.  * atomically swap in the new signal mask, and wait for a signal.
  3.  */
  4. asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask)
  5. {
  6.     mask &= _BLOCKABLE;
  7.     spin_lock_irq(&current->sighand->siglock);
  8.     current->saved_sigmask = current->blocked;
  9.     siginitset(&current->blocked, mask);
  10.     recalc_sigpending();
  11.     spin_unlock_irq(&current->sighand->siglock);

  12.     current->state = TASK_INTERRUPTIBLE;可中断休眠,收到信号后会被唤醒
  13.     schedule();    让出CPU,并从CPU运行队列移除,等待被信号唤醒
  14.     set_restore_sigmask();
  15.     return -ERESTARTNOHAND;
  16. }

    前面文章《用户态定时器介绍》介绍了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

点击(此处)折叠或打开

  1. unsigned int __sleep (unsigned int seconds)
  2. {
  3.   const unsigned int max
  4.     = (unsigned int) (((unsigned long int) (~((time_t) 0))) >> 1);
  5.   struct timespec ts;
  6.   sigset_t set, oset;
  7.   unsigned int result;

  8.   /* This is not necessary but some buggy programs depend on this. */
  9.   if (__builtin_expect (seconds == 0, 0))
  10.     {
  11. #ifdef CANCELLATION_P
  12.       CANCELLATION_P (THREAD_SELF);
  13. #endif
  14.       return 0;
  15.     }

  16.   ts.tv_sec = 0;
  17.   ts.tv_nsec = 0;
  18.  again:
  19.   if (sizeof (ts.tv_sec) <= sizeof (seconds))
  20.     {
  21.       /* Since SECONDS is unsigned assigning the value to .tv_sec can
  22.      overflow it. In this case we have to wait in steps. */
  23.       ts.tv_sec += MIN (seconds, max);
  24.       seconds -= (unsigned int) ts.tv_sec;
  25.     }
  26.   else
  27.     {
  28.       ts.tv_sec = (time_t) seconds;
  29.       seconds = 0;
  30.     }
  31.   __sigemptyset (&set);
  32.   __sigaddset (&set, SIGCHLD);
  33.   if (__sigprocmask (SIG_BLOCK, &set, &oset))
  34.     return -1;

  35.   /* If SIGCHLD is already blocked, we don't have to do anything. */
  36.   if (!__sigismember (&oset, SIGCHLD))
  37.     {
  38.       int saved_errno;
  39.       struct sigaction oact;

  40.       __sigemptyset (&set);
  41.       __sigaddset (&set, SIGCHLD);

  42.       /* We get the signal handler for SIGCHLD. */
  43.       if (__sigaction (SIGCHLD, (struct sigaction *) NULL, &oact) < 0)
  44.     {
  45.      saved_errno = errno;
  46.      /* Restore the original signal mask. */
  47.      (void) __sigprocmask (SIG_SETMASK, &oset, (sigset_t *) NULL);
  48.      __set_errno (saved_errno);
  49.      return -1;
  50.     }

  51.       if (oact.sa_handler == SIG_IGN)
  52.     {

  53.      /* We should leave SIGCHLD blocked. */
  54.      while (1)
  55.      {
  56.         这里是关键的,sleep主要功能由该函数实现(nanosleep系统调用,休眠)
  57.      result = __nanosleep (&ts, &ts);

  58.      if (result != 0 || seconds == 0)
  59.         break;

  60.      if (sizeof (ts.tv_sec) <= sizeof (seconds))
  61.         {
  62.          ts.tv_sec = MIN (seconds, max);
  63.          seconds -= (unsigned int) ts.tv_nsec;
  64.         }
  65.      }

  66.      saved_errno = errno;
  67.      /* Restore the original signal mask. */
  68.      (void) __sigprocmask (SIG_SETMASK, &oset, (sigset_t *) NULL);
  69.      __set_errno (saved_errno);

  70.      goto out;
  71.     }

  72.       /* We should unblock SIGCHLD. Restore the original signal mask. */
  73.       (void) __sigprocmask (SIG_SETMASK, &oset, (sigset_t *) NULL);
  74.     }
  75.     这里是关键的,sleep主要功能由该函数实现(nanosleep系统调用,休眠)
  76.   result = __nanosleep (&ts, &ts);
  77.   if (result == 0 && seconds != 0)
  78.     goto again;

  79.  out:
  80.   if (result != 0)
  81.     /* Round remaining time. */
  82.     result = seconds + (unsigned int) ts.tv_sec + (ts.tv_nsec >= 500000000L);

  83.   return result;
  84. }
  85. weak_alias (__sleep, sleep)
    下面再跟一下nanosleep,该函数是系统调用,跟进内核:

点击(此处)折叠或打开

  1. SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp,
  2.         struct timespec __user *, rmtp)
  3. {
  4.     struct timespec tu;

  5.     if (copy_from_user(&tu, rqtp, sizeof(tu)))
  6.         return -EFAULT;

  7.     if (!timespec_valid(&tu))
  8.         return -EINVAL;

  9.     return hrtimer_nanosleep(&tu, rmtp, HRTIMER_MODE_REL, CLOCK_MONOTONIC);
  10. }
    再跟踪hrtimer_nanosleep:

点击(此处)折叠或打开

  1. long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp,
  2.          const enum hrtimer_mode mode, const clockid_t clockid)
  3. {
  4.     struct restart_block *restart;
  5.     struct hrtimer_sleeper t;
  6.     int ret = 0;
  7.     unsigned long slack;

  8.     slack = current->timer_slack_ns;
  9.     if (rt_task(current))
  10.         slack = 0;

  11.     hrtimer_init_on_stack(&t.timer, clockid, mode);
  12.     hrtimer_set_expires_range_ns(&t.timer, timespec_to_ktime(*rqtp), slack);
  13.     if (do_nanosleep(&t, mode))    返回1代表执行成功,后续会返回用户态
  14.         goto out;

  15.     /* Absolute timers do not update the rmtp value and restart: */
  16.     if (mode == HRTIMER_MODE_ABS) {
  17.         ret = -ERESTARTNOHAND;
  18.         goto out;
  19.     }

  20.     if (rmtp) {
  21.         ret = update_rmtp(&t.timer, rmtp);
  22.         if (ret <= 0)
  23.             goto out;
  24.     }

  25.     restart = &current_thread_info()->restart_block;
  26.     restart->fn = hrtimer_nanosleep_restart;
  27.     restart->nanosleep.index = t.timer.base->index;
  28.     restart->nanosleep.rmtp = rmtp;
  29.     restart->nanosleep.expires = hrtimer_get_expires_tv64(&t.timer);

  30.     ret = -ERESTART_RESTARTBLOCK;若执行失败则再重新执行一次
  31. out:
  32.     destroy_hrtimer_on_stack(&t.timer);
  33.     return ret;
  34. }
    再跟踪do_nanosleep

点击(此处)折叠或打开

  1. static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode)
  2. {
  3.     hrtimer_init_sleeper(t, current);初始化函数,后面分析

  4.     do {
  5.         set_current_state(TASK_INTERRUPTIBLE);     可被信号唤醒
  6.         hrtimer_start_expires(&t->timer, mode);    启动高精度定时器

  7.         if (!hrtimer_active(&t->timer))
  8.             t->task = NULL;

  9.         if (likely(t->task))
  10.             schedule();    休眠,并等待被定时器或信号唤醒

  11.         hrtimer_cancel(&t->timer);
  12.         mode = HRTIMER_MODE_ABS;

  13.     } while (t->task && !signal_pending(current));
  14.     若超时时间到,则t->task被设置为NULL,退出循环;若收到信号也会退出循环。

  15.     __set_current_state(TASK_RUNNING);
  16.     是否超若是,说明进程已休眠了指定的时间,可以安全返回到用户态;否则(被信号唤醒),函数hrtimer_nanosleep会做特殊处理。所谓的特殊处理就是通过返回ERESTART_RESTARTBLOCK,使得nanosleep系统调用(sys_nanosleep)在返回用户态前会被重新启动,直到执行成功。
  17.     return t->task == NULL;
  18. }
    初始化函数

点击(此处)折叠或打开


  1. void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *task)
  2. {
  3.     sl->timer.function = hrtimer_wakeup;该函数在定时器到期时负责唤醒进程
  4.     sl->task = task;                    被唤醒的进程
  5. }
    下面再分析一下高精度定时器是如何运行的:

点击(此处)折叠或打开

  1. xx_timer_interrupt    时钟中断(硬中断)响应函数(底层驱动,CPU相关
  2.         -> tick_handle_periodic
  3.             -> tick_periodic
  4.                 -> update_process_times
  5.                        -> run_local_timers
  6.                         -> hrtimer_run_queues Called from hardirq context every jiffy
  7.                             -> __run_hrtimer
  8.                                 -> hrtimer_wakeup
  9.                         ->raise_softirq(TIMER_SOFTIRQ); 该函数触发(非执行)普通定时器
    继续跟踪hrtimer_wakeup:

点击(此处)折叠或打开

  1. static enum hrtimer_restart hrtimer_wakeup(struct hrtimer *timer)
  2. {
  3.     struct hrtimer_sleeper *t =
  4.         container_of(timer, struct hrtimer_sleeper, timer);
  5.     struct task_struct *task = t->task;

  6.     t->task = NULL;设置为NULL
  7.     if (task)
  8.         wake_up_process(task);唤醒因执行sleep而休眠的进程

  9.     return HRTIMER_NORESTART;
  10. }

2.2.2 总结

    该版本sleep通过nanosleep系统调用陷入内核并休眠,指定时间到达时,再返回用户态,从而达到sleep的目的。
阅读(3434) | 评论(0) | 转发(0) |
0

上一篇:用户态定时器介绍

下一篇:没有了

给主人留下些什么吧!~~