学习ldd3--计时、延迟操作(第七章)

作者:张伟AreS
/******************************************************************************/
学习此之前先需学习《学习ldd3--proc文件系统(第七章与第四章)》
实例:代码:ldd3的jit.c   (D:\学习\个人学习笔记及网络经典文章\学习LDD3笔记\ldd3_examples\misc-modules\jit.c)
时钟中断:
  由系统的定时硬件以周期性的时间间隔产生,这个间隔(即频率)由内核根据HZ来确定,
HZ是一个与体系结构无关的常数,可配置(50-1200),在X86平台,默认是1000(1sec 1000次)
每当时钟中断发生时,全局变量jiffies(unsigned long)就会加1.因此jiffies记录了自linux启动后时钟发生中断此时。
驱动程序常利用jiffies来计算不同事件间的时间间隔
 
  源码:jit.c jiq.c  (D:\学习\个人学习笔记及网络经典文章\学习LDD3笔记\ldd3_examples\misc-modules中)
 
  查看/proc/currentime,它对应的read_proc函数是jit_currentime
 
int jit_currentime(char *buf, char **start, off_t offset,int len, int *eof, void *data)
{
 struct timeval tv1;//这两个结构体都定义在linux/time.h中
 struct timespec tv2;
 unsigned long j1;
 u64 j2;
 /* get them four */
 j1 = jiffies;
 j2 = get_jiffies_64();//获得64位的jiffies的值
 do_gettimeofday(&tv1);//调用do_gettimeofday函数,把当前jiffies值转换为timeval类型保存在tv1中
 tv2 = current_kernel_time();//调用current_kernel_time函数取得当前jiffies值对应的timespec(秒和纳秒)类型,保存在tv2中
 /* 将4种形式的时间值保存在buf指向的内存页中*/
 len=0;
 /*sprintf函数将数值保存在buf中*/
 len += sprintf(buf,"0x%08lx 0x%016Lx %10i.%06i\n" "%40i.%09i\n",j1, j2,(int) tv1.tv_sec, (int) tv1.tv_usec,(int) tv2.tv_sec, (int) tv2.tv_nsec);
 *start = buf;
 
 return len;
}
  /*内核中struct timval与struct timespec结构体的定义*/
  struct timeval { 
    time_t      tv_sec;     /* seconds 秒*/ 
    suseconds_t tv_usec;    /* microseconds毫秒 */ 
}; 
 
  struct timespec { 
   time_t  tv_sec;     /* seconds 秒*/ 
   long    tv_nsec;    /* nanoseconds纳秒 */ 
}; 
/***************************************/
延迟操作
接下来我们看jit_fn函数,/proc/jitbusy、/proc/jitsched、/proc/jitqueue、/proc/jitschedto
四个文件的read_proc函数都是jit_fn,所不同的是,读这四个文件时,会分别传递给jit_fn四个不同的
参数JIT_BUSY、JIT_SCHED、JIT_QUEUE、JIT_SCHEDTO
/* use these as data pointers, to implement four files in one function */
/*定义了JIT_BUSY、JIT_SCHED、JIT_QUEUE、JIT_SCHEDTO四个枚举值,分别对应0,1,2,3*/
enum jit_files {
 JIT_BUSY,
 JIT_SCHED,
 JIT_QUEUE,
 JIT_SCHEDTO
};

/*
 * This function prints one line of data, after sleeping one second.
 * It can sleep in different ways, according to the data pointer
 */
int jit_fn(char *buf, char **start, off_t offset,int len, int *eof, void *data)
{
 unsigned long j0, j1; /* jiffies */
 /*定义并初始化了等待队列头wait*/
 wait_queue_head_t wait;
 init_waitqueue_head (&wait);
 j0 = jiffies;    /*将j0设置为当前jiffies值*/
 j1 = j0 + delay;    /*delay默认值定义为HZ,HZ为1000,j1即未来1秒*/
/*长延迟执行*/
 switch((long)data) {
  /*1.忙等待。如果传递给jit_fn的参数是JIT_BUSY,也就是说用户在读/proc/jitbusy文件,
  *此时采用忙等待的方式延迟delay长度的时钟滴答。time_before(jiffies, j1)宏在jiffies小于j1的情况下为真。
  *cpu_relax函数不做任何事情。
  */
  case JIT_BUSY:
   while (time_before(jiffies, j1)) //time_before()宏表示在jiffies     cpu_relax();//如果我们没有将内核配置为抢占型的,那么这个循环将在延迟期间独占处理器,
                //在j1所代表的时间到达之前,系统看起来就像死掉了一样。而如果我们将内核配置为可抢占型的,则问题不会那么严重,
                //这是因为除非代码拥有一个锁,否则处理器的时间还可以用作他用。但是在抢占式系统中,忙等待仍然有些浪费。
                    //更糟糕的是,如果在进入while循环之前,禁止了中断(jiffies靠内核时钟中断不断更新),jiffies值就得不到更新,
                    //那么while循环的判定条件就永远为真了
       break;
  
  /*2.让出处理器。如果传递给jit_fn的参数是JIT_SCHED,也就是说用户在读/proc/jitsched文件,
  *此时如果time_before(jiffies, j1)宏为真,则调用schedule函数让出处理器。
     */
  case JIT_SCHED:
   while (time_before(jiffies, j1)) {
    schedule();   //当前进程虽然放弃了CPU而不做任何事,但依然在运行队列中
   }
   break;
  
  /*3.超时。如果传递给jit_fn的参数是JIT_QUEUE,也就是说用户在读/proc/jitqueue文件,
  *则调用wait_event_interruptible_timeout(wait, 0, delay)函数在等待队列wait上休眠,
  *但是会在指定的等待时间到期时返回。在到期之前,如果进程收到中断信号也会退出休眠返回。
    */
    case JIT_QUEUE:
   wait_event_interruptible_timeout(wait, 0, delay);
   break;
   
   /*4.也是超时。如果传递给jit_fn的参数是JIT_SCHEDTO,也就是说用户在读/proc/schedto文件
   *则调用set_current_state将进程状态设置为TASK_INTERRUPTIBLE
   *然后调用schedule_timeout (delay),schedule_timeout函数让出处理器,并等待delay指定的时间。
   *实际上wait_event_interruptible_timeout函数在内部就是利用了schedule_timeout函数。
      *使用schedule_timeout方式延迟,性能与使用wait_event_interruptible_timeout相似,但是可以避免声明和使用多余的等待队列
      */
    case JIT_SCHEDTO:
   set_current_state(TASK_INTERRUPTIBLE);
   schedule_timeout (delay);
   break;
 }
 j1 = jiffies; /* actual value after we delayed */
 len = sprintf(buf, "%9li %9li\n", j0, j1);
 *start = buf;
 return len;
}

/************************************/ 
短延迟:
   void ndelay(unsigned long nsecs);//纳秒级延迟
   void udelay(unsigned long nsecs);//微秒级延迟
   void mdelay(unsigned long nsecs);//毫秒级延迟
  实现原理是忙等待,类似软件中的延迟函数:
  void delay(unsigned int time)
    {
       while(time--);
   
    }
 毫秒时延以及更大的秒时延已经比较大,内核中不应直接使用medelay()函数,将无谓的耗费CPU资源,
 对于毫秒级以上时延,内核提供下述函数:
   void msleep(unsigned int millisecs);
     unsigned long msleep_interruptible(unsigned int millisecs);
     void ssleep(unsigned int second);
 上述函数使得调用它们的进程睡眠参数指定的时间,前两个函数不能被打断,第三个函数可以被打断。     
  

你可能感兴趣的:(嵌入式)