您的位置 首页 国产IC

你知道Linux内核使命推迟会有什么影响?

你知道Linux内核任务延迟会有什么影响?-内核代码(尤其是驱动程序)除了使用定时器或下半部机制以外还需要其他方法来推迟执行任务。这种推迟通常发生在等待硬件完成某些工作时,而且等待时间非常短。

  内核代码(尤其是驱动程序)除了运用守时器或下半部机制以外还需求其他办法来推延履行使命。这种推延一般产生在等候硬件完结某些作业时,而且等候时刻十分短。

  内核供给了许多推迟办法处理各种推迟要求。不同的办法有不同的处理特色,有些是在推迟使命时挂起处理器,避免处理器履行任何实际作业;另一些不会挂起处理器,所以也不能确保被推迟的代码能够在指定的推迟时刻运转。

  忙等候

  最简略的推迟办法(尽管也是最不抱负的办法)是忙等带。该办法只是在想要推迟的时刻是节拍的整数倍,或许准确率要求不太高时才干够运用。

  忙循环完结起来很简略,在循环中不断旋转直到期望的时钟节拍数耗尽,比方:

  unsigned long delay = jiffies + 10;

  while(time_before(jiffies,delaly))

  ;

  更好的办法应该是在代码等候时,运转内核从头调度履行其他使命:

  unsigned long delay = jiffies + 2 * HZ;

  while(TIme_before(jiffies,delay))

  cond_resched();

  在中

  int __sched cond_resched(void)

  {

  if (need_resched() && !(preempt_count() & PREEMPT_ACTIVE) &

  system_state == SYSTEM_RUNNING) {

  __cond_resched();

  return 1;

  }

  return 0;

  }

  staTIc void __cond_resched(void)

  {

  #ifdef CONFIG_DEBUG_SPINLOCK_SLEEP

  __might_sleep(__FILE__, __LINE__);

  #endif

  /*

  * The BKS might be reacquired before we have dropped

  * PREEMPT_ACTIVE, which could trigger a second

  * cond_resched() call.

  */

  do {

  add_preempt_count(PREEMPT_ACTIVE);

  schedule();

  sub_preempt_count(PREEMPT_ACTIVE);

  } while (need_resched());

  }

  cond_resched()函数将调度一个新程序投入运转,可是它只要在设置完need_resched标志后,才干收效。也便是说,该办法有用的条件是体系中存在更重要的使命需求履行。留意,因为该办法需求调用调度程序,所以它不能在中止上下文中运用,只能在进程上下文中运用。实际上,一切推迟办法在进程上下文中运用,因为中止处理程序都应该尽或许快地履行。别的,推迟履行不论在哪种状况下都不应该在持有时或制止中止时产生。

  C编译器一般只将变量装载一次,一般状况下不能确保循环中的jiffies变量在每次循环中被读取时都从头被载入。可是要求jiffies在每次循环时都有必要从头装载,因为在后台jiffies值会随时钟中止的产生而不断添加。为了处理这个问题,jiffies变量被符号为volatile。

  关键字volatile指示编译器在每次拜访变量时都从头从主内存中取得,而不是经过寄存器中变量的别号来拜访,然后确保前面的循环能够按预期的办法履行。

  短推迟

  有时候,内核代码(一般也是驱动程序)不光需求很时刻短的推迟(比时钟节拍还短)而且还要求推迟的时刻很准确。这种状况多产生在和硬件同步时,也便是说需求时刻短等候某个动作的完结,等候时刻往往小于1毫秒,所以不能运用像前面比如中那种依据jiffies的推迟办法。

  内核供给了两个能够处理为妙和毫秒等级的推迟的函数udelay()和mdelay()。前一个函数运用忙循环来将使命推迟到指定的奇妙数后运转,后者推迟指定的毫秒数:

  在中

  /* 0x10c7 is 2**32 / 1000000 (rounded up) */

  #define udelay(n) (__builtin_constant_p(n) ? /

  ((n) 》 20000 ? __bad_udelay() : __const_udelay((n) * 0x10c7ul)) : /

  __udelay(n))

  /* 0x5 is 2**32 / 1000000000 (rounded up) */

  #define ndelay(n) (__builtin_constant_p(n) ? /

  ((n) 》 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : /

  __ndelay(n))

  在中

  /*

  * Copyright (C) 1993 Linus Torvalds

  *

  * Delay routines, using a pre-computed “loops_per_jiffy” value.

  */

  extern unsigned long loops_per_jiffy;

  #include

  /*

  * Using udelay() for intervals greater than a few milliseconds can

  * risk overflow for high loops_per_jiffy (high bogomips) machines. The

  * mdelay() provides a wrapper to prevent this. For delays greater

  * than MAX_UDELAY_MS milliseconds, the wrapper is used. Architecture

  * specific values can be defined in asm-???/delay.h as an override.

  * The 2nd mdelay() definition ensures GCC will optimize away the

  * while loop for the common cases where n 《= MAX_UDELAY_MS — Paul G.

  */

  #ifndef MAX_UDELAY_MS

  #define MAX_UDELAY_MS 5

  #endif

  #ifndef mdelay

  #define mdelay(n) (/

  (__builtin_constant_p(n) && (n)《=MAX_UDELAY_MS) ? udelay((n)*1000) : /

  ({unsigned long __ms=(n); while (__ms–) udelay(1000);}))

  #endif

  #ifndef ndelay

  #define ndelay(x) udelay(((x)+999)/1000)

  #endif

  udelay()函数依托履行数次到达推迟作用,而mdelay()函数经过udelay()函数完结。因为内核知道处理器在一秒内能履行多少次循环,所以udelay()函数紧迫需求依据指定的推迟时刻在1秒中占的份额就能决议需求进行多少次循环就能到达要求的推延时刻。

  不要运用udelay()函数处理超越1毫秒的推迟,推迟超越1毫秒运用mdelay()更安全。

  千万要留意不要在持有锁时或制止中止时运用忙等候,因为这时忙等候会时体系响应速度和功能大打折扣。

  内核怎么知道处理器在一秒内能履行多少循环呢?

  BogoMIPS值记载的是处理器在给守时刻内忙循环履行的次数。其实便是记载处理器在闲暇时速度有多快。它主要被udelay()和mdelay()函数运用。该值存放在变量loops_per_jiffies中,能够从文件/proc/cpuinfo中读到它。推迟循环函数运用loops_per_jiffies值来核算为供给准确推迟而需求进行多少次循环。内核在启动时运用函数calibrate_delay()函数核算loops_per_jiffies值,该函数在文件init/main.c中运用到。

  在中

  /*

  * This should be approx 2 Bo*oMips to start (note initial shift), and will

  * still work even if initially too large, it will just take slightly longer

  */

  unsigned long loops_per_jiffy = (1《《12);

  在中

  void __devinit calibrate_delay(void)

  {

  unsigned long ticks, loopbit;

  int lps_precision = LPS_PREC;

  if (preset_lpj) {

  loops_per_jiffy = preset_lpj;

  printk(“Calibrating delay loop (skipped)。.. ”

  “%lu.%02lu BogoMIPS preset/n”,

  loops_per_jiffy/(500000/HZ),

  (loops_per_jiffy/(5000/HZ)) % 100);

  } else if ((loops_per_jiffy = calibrate_delay_direct()) != 0) {

  printk(“Calibrating delay using timer specific routine.。 ”);

  printk(“%lu.%02lu BogoMIPS (lpj=%lu)/n”,

  loops_per_jiffy/(500000/HZ),

  (loops_per_jiffy/(5000/HZ)) % 100,

  loops_per_jiffy);

  } else {

  loops_per_jiffy = (1《《12);

  printk(KERN_DEBUG “Calibrating delay loop.。. ”);

  while ((loops_per_jiffy 《《= 1) != 0) {

  /* wait for “start of” clock tick */

  ticks = jiffies;

  while (ticks == jiffies)

  /* nothing */;

  /* Go 。. */

  ticks = jiffies;

  __delay(loops_per_jiffy);

  ticks = jiffies – ticks;

  if (ticks)

  break;

  }

  /*

  * Do a binary approximation to get loops_per_jiffy set to

  * equal one clock (up to lps_precision bits)

  */

  loops_per_jiffy 》》= 1;

  loopbit = loops_per_jiffy;

  while (lps_precision– && (loopbit 》》= 1)) {

  loops_per_jiffy |= loopbit;

  ticks = jiffies;

  while (ticks == jiffies)

  /* nothing */;

  ticks = jiffies;

  __delay(loops_per_jiffy);

  if (jiffies != ticks) /* longer than 1 tick */

  loops_per_jiffy &= ~loopbit;

  }

  /* Round the value and print it */

  printk(“%lu.%02lu BogoMIPS (lpj=%lu)/n”,

  loops_per_jiffy/(500000/HZ),

  (loops_per_jiffy/(5000/HZ)) % 100,

  loops_per_jiffy);

  }

  }

  schedule_timeout()

  更抱负的推迟办法时运用schedule_timeout()函数。该办法会让需求推迟履行的使命睡觉到指定的推迟时刻耗尽后再从头运转。但该办法也不能确保睡觉时刻正好等于指定的推迟时刻,只能尽量使睡觉时刻挨近指定的推迟时刻。当指守时刻到期后,内核唤醒被推迟的使命并将其从头放回运转行列:

  set_current_state(TASK_INTERRUPTABLE);

  schedule_timeout(s*HZ);//参数是推迟的相对时刻,单位为jiffies

  留意,在调用schedule_timeout()函数前,有必要将使命状况设置成TASK_INTERRUPTABLE和TASK_UNINTERRUPTABLE状况之一,不然使命不会睡觉。

  留意,因为schedule_timeout()函数需求调用调度程序,所以调用它的代码有必要确保能够睡觉。调用代码有必要处于进程上下文中,而且不能持有锁。

  在(Timer.c(kernel))中

  /**

  * schedule_timeout – sleep until timeout

  * @timeout: timeout value in jiffies

  *

  * Make the current task sleep until @timeout jiffies have

  * elapsed. The routine will return immediately unless

  * the current task state has been set (see set_current_state())。

  *

  * You can set the task state as follows –

  *

  * %TASK_UNINTERRUPTIBLE – at least @timeout jiffies are guaranteed to

  * pass before the routine returns. The routine will return 0

  *

  * %TASK_INTERRUPTIBLE – the routine may return early if a signal is

  * delivered to the current task. In this case the remaining time

  * in jiffies will be returned, or 0 if the timer expired in time

  *

  * The current task state is guaranteed to be TASK_RUNNING when this

  * routine returns.

  *

  * Specifying a @timeout value of %MAX_SCHEDULE_TIMEOUT will schedule

  * the CPU away without a bound on the timeout. In this case the return

  * value will be %MAX_SCHEDULE_TIMEOUT.

  *

  * In all cases the return value is guaranteed to be non-negative.

  */

  fastcall signed long __sched schedule_timeout(signed long timeout)

  {

  struct timer_list timer;

  unsigned long expire;

  switch (timeout)

  {

  case MAX_SCHEDULE_TIMEOUT: //用于查看使命是否无限期的睡觉,假如这样的话,函数不会为它设置守时器

  /*

  * These two special cases are useful to be comfortable

  * in the caller. Nothing more. We could take

  * MAX_SCHEDULE_TIMEOUT from one of the negative value

  * but I‘ d like to return a valid offset (》=0) to allow

  * the caller to do everything it want with the retval.

  */

  schedule();

  goto out;

  default:

  /*

  * Another bit of PARANOID. Note that the retval will be

  * 0 since no piece of kernel is supposed to do a check

  * for a negative retval of schedule_timeout() (since it

  * should never happens anyway)。 You just have the printk()

  * that will tell you if something is gone wrong and where.

  */

  if (timeout 《 0) {

  printk(KERN_ERR “schedule_timeout: wrong timeout ”

  “value %lx/n”, timeout);

  dump_stack();

  current-》state = TASK_RUNNING;

  goto out;

  }

  }

  expire = timeout + jiffies;

  setup_timer(&timer, process_timeout, (unsigned long)current); /*创立守时器,设置超时时刻,设置超时函数 */

  __mod_timer(&timer, expire); /*激活守时器 */

  schedule(); /*挑选其他使命运转 */

  del_singleshot_timer_sync(&timer); /*毁掉守时器*/

  timeout = expire – jiffies;

  out:

  return timeout 《 0 ? 0 : timeout;

  }

  在中

  static inline void setup_timer(struct timer_list * timer,

  void (*function)(unsigned long),

  unsigned long data)

  {

  timer-》function = function;

  timer-》data = data;

  init_timer(timer);

  }

  在中

  //当守时器超时,该函数被调用

  static void process_timeout(unsigned long __data)

  {

  wake_up_process((struct task_struct *)__data);

  }

  schedule_timeout()函数是内核守时器的一个简略运用。

  设置超时时刻,在等候行列上睡觉

  进程上下文中的代码为了等候特定事情产生,能够将自己放入等候行列,然后调用调度程序去履行新使命。一旦事情产生后,内核调用wake_up()函数唤醒在睡觉行列上的使命使其从头投入运转。

  有时,等候行列上的某个使命或许既在等候一个特定的事情到来,又在等候一个特定的时刻到期,在这种状况下,代码能够简略地运用schedule_timeout()函数替代schedule()函数,当期望指守时刻到期,使命就会被唤醒。代码需求查看被唤醒的原因,或许是被事情唤醒,或许是因为推迟时刻到期,也或许是因为接纳到了信号;然后履行相应的操作。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/bandaoti/ic/89744.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部