Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2305278
  • 博文数量: 318
  • 博客积分: 8752
  • 博客等级: 中将
  • 技术积分: 4944
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-23 07:56
文章分类

全部博文(318)

文章存档

2019年(1)

2017年(2)

2016年(12)

2015年(2)

2014年(1)

2013年(17)

2012年(22)

2011年(9)

2010年(37)

2009年(33)

2008年(44)

2007年(43)

2006年(95)

分类: LINUX

2010-08-17 15:18:48

内核代码(尤其是驱动程序)除了使用定时器或下半部机制以外还需要其他方法来推迟执行任务。这种推迟通常发生在等待硬件完成某些工作时,而且等待的时间往往非常短。有许多延迟方法(所有的延迟方法都应该在进城上下文中使用):
o. 忙等待:仅仅在想要延迟的时间是节拍的整数倍或者精确率要求不高时才可以使用。
o. 短延迟:比时钟节拍还短的延迟,并且要求延迟时间很精确。
           void udelay(unsigned long usecs); //利用忙等待将任务延迟到指定的微秒数后运行
           void mdelay(unsigned long msecs); //利用忙等待将任务延迟到指定的毫秒数后运行
o. schedule_timeout()
           延迟执行的任务睡眠到指定的延迟时间耗尽后再重新运行。不能保证睡眠时间正好等于指定的延迟时间。调用之前需要把任务设置为TASK_INTERRUPTIBLE或者TASK_UNINTERRUPTIBLE状态,否则任务不会睡眠
o. 设置超时时间,在等待队列上睡眠
 
我凭着网上的资料和自己的理解来分析一下udelay的实现。
理论部分:udelay函数通过BogoMIPS实现。Bogo的意思是Bogus,MIPS的意思是每秒百万条指令(million of instructions per second)
BogoMIPS纪录处理器在给定时间内忙循环执行的次数(该值存放在loops_per_jiffy),所以udelay函数仅仅需要根据指定时间在1秒所占的比例,就可以知道执行多少次循环能达到要求的推迟时间。
代码分析:(内核版本2.6.20)
1. loops_per_jiffy在内核初始化的时候,在calibrate_delay函数内被计算(calibrate.c)

 

/*
 * This is the number of bits of precision for the loops_per_jiffy. Each
 * bit takes on average 1.5/HZ seconds. This (like the original) is a little
 * better than 1%
 */

/* 计算的精度,这个值设定的越大,loops_per_jiffy的值越精确 */
#define LPS_PREC 8


void __devinit calibrate_delay(void)
{
    unsigned long ticks, loopbit;
    int lps_precision = LPS_PREC;

    /* 不知道preset_lpj是什么,查了一下,说是可以在引导程序里面设置的 */
    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 */

        loops_per_jiffy = (1<<12);

        printk(KERN_DEBUG "Calibrating delay loop... ");

        /* 第一次循环,每次都将loops_per_jiffy扩大2倍 */
        while ((loops_per_jiffy <<= 1) != 0) {

            /* 下面这段是循环直到一个新的节拍的开始 */
            /* wait for "start of" clock tick */
            ticks = jiffies;
            while (ticks == jiffies)
                /* nothing */;
            /* Go .. */

            /* 纪录新的节拍开始的tick值 */
            ticks = jiffies;

            /* __delay下面说明,是执行loops_per_jiffy次指令 */
            __delay(loops_per_jiffy);

            /* 执行完了看看当前的ticks是否>0(意味着超过了1个节拍) */

            /* 超过一个节拍就跳出循环 */
            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是满足这样子的条件,*/

         /* 即执行它超过一个节拍,但是loop_per_jiffy/2则不到一个节拍 */
         /* 具体一个节拍需要多少次loops_per_jiffy, */

         /* 则一定在[ loops_per_jiffy/2, loops_per_jiffy ]区间内。*/

         /* 这次循环是更加精确的寻找 */

        loops_per_jiffy >>= 1;
        loopbit = loops_per_jiffy;

        /* 从loops_per_jiffy / 2开始探测 */
        /* 当前探测值 = loops_per_jiffy | loopbit */

        /* lps_precision决定了循环的次数,从而影响了结果的精度 */
        while (lps_precision-- && (loopbit >>= 1)) {
            loops_per_jiffy |= loopbit;
            ticks = jiffies;
            while (ticks == jiffies)
                /* nothing */;
            ticks = jiffies;
            __delay(loops_per_jiffy);

            /* 大于一个tick,说明loops_per_jiffy取值过大 */

            /* 恢复原来的loops_per_jiffy,且loopbit会在下个循环 */

            /* 里将值变小*/
            if (jiffies != ticks)    /* longer than 1 tick */
                loops_per_jiffy &= ~loopbit;
        }

        /* Round the value and print it */

        /* loops_per_jiffy表示的是每个tick内执行的指令条数 */
        /* 打印的结果应该是1/500000秒执行的指令条数,这只是打印结果 */

        /* 并不影响loops_per_jiffy的值 */
          
  printk("%lu.%02lu BogoMIPS (lpj=%lu)\n",
            loops_per_jiffy/(500000/HZ),
            (loops_per_jiffy/(5000/HZ)) % 100,
            loops_per_jiffy);
    }
}

 BogoMIPS = loops_per_jiffs * 1秒内的jiffy数 * 延迟循环消耗的指令数(以百万为单位)

   =( loops_per_jiffs * HZ * 2)/ 1000000)

   = loops_per_jiffs / (1000000/HZ/2)

   = loops_per_jiffs / (500000/HZ)

2.下面说一下delay的实现(arch/arm/lib/delay.S)

.LC0:           .word   loops_per_jiffy
.LC1:           .word   (2199023*HZ)>>11

/*
 * r0  <= 2000
 * lpj <= 0x01ffffff (max. 3355 bogomips)
 * HZ  <= 1000
 */

ENTRY(__udelay)
                ldr     r2, .LC1
                mul     r0, r2, r0
ENTRY(__const_udelay)                           @ 0 <= r0 <= 0x7fffff06
                ldr     r2, .LC0
                ldr     r2, [r2]                @ max = 0x01ffffff
                mov     r0, r0, lsr #14         @ max = 0x0001ffff
                mov     r2, r2, lsr #10         @ max = 0x00007fff
                mul     r0, r2, r0              @ max = 2^32-1
                movs    r0, r0, lsr #6
                moveq   pc, lr

/*
 * loops = r0 * HZ * loops_per_jiffy / 1000000
 *
 * Oh, if only we had a cycle counter...
 */

@ Delay routine
ENTRY(__delay)
                subs    r0, r0, #1
                bhi     __delay  //延迟循环消耗的指令数为 2
                mov     pc, lr

阅读(1360) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-12-30 10:27:30

能够解释一下 loops_per_jiffy |= loopbit;为什么用或,不是加呢?假设loops_per_jiff = 0xff00,那么loopbit就是他的一半,0x7f80,或了以后的结果是0xff80。假设这个值的delay大于一个tick,那么就要恢复原来值,但是这里用and操作,那么就是0x8000 搞不明白。。。。