Chinaunix首页 | 论坛 | 博客
  • 博客访问: 971182
  • 博文数量: 214
  • 博客积分: 10173
  • 博客等级: 上将
  • 技术积分: 1867
  • 用 户 组: 普通用户
  • 注册时间: 2007-06-18 13:48
文章分类

全部博文(214)

文章存档

2012年(1)

2010年(13)

2009年(5)

2008年(98)

2007年(97)

分类: LINUX

2007-11-18 23:44:47

Linux中断解析(实例二)

实例二——从RTC设备学习中断

系统实时钟

每台PC机都有一个实时钟(Real Time Clock)设备。在你关闭计算机电源的时候,由它维持系统的日期和时间信息。

此外,它还可以用来产生周期信号,频率变化范围从2Hz8192Hz——当然,频率必须是2的倍数。这样该设备就能被当作一个定时器使用,比如我们把频率设定为4Hz,那么设备启动后,系统实时钟每秒就会向CPU发送4次定时信号——通过8号中断提交给系统(标准PC机的IRQ 8是如此设定的)。由于系统实时钟是可编程控制的,你也可以把它设成一个警报器,在某个特定的时刻拉响警报——向系统发送IRQ 8中断信号。由此看来,IRQ 8与生活中的闹铃差不多:中断信号代表着报警器或定时器的发作。

Linux操作系统的实现里,上述中断信号可以通过/dev/rtc(主设备号10,从设备号135,只读字符设备)设备获得。对该设备执行读(read)操作,会得到unsigned long型的返回值,最低的一个字节表明中断的类型(更新完毕update-done,定时到达alarm-rang,周期信号periodic);其余字节包含上次读操作以来中断到来的次数。如果系统支持/proc文件系统,/proc/driver/rtc中也能反映相同的状态信息。

该设备只能由每个进程独占,也就是说,在一个进程打开(open)设备后,在它没有释放前,不允许其它进程再打开它。这样,用户的程序就可以通过对/dev/rtc执行read()select()系统调用来监控这个中断——用户进程会被阻塞,直到系统接收到下一个中断信号。对于一些高速数据采集程序来说,这个功能非常有用,程序无需死守着反复查询,耗尽所有的CPU资源;只要做好设定,以一定频率进行查询就可以了。

#include

#include

#include

#include

#include

#include

#include

#include

 

int main(void)

{

  int i, fd, retval, irqcount = 0;

  unsigned long tmp, data;

  struct rtc_time rtc_tm;

 

  // 打开RTC设备

  fd = open ("/dev/rtc", O_RDONLY);

 

  if (fd ==  -1) {

    perror("/dev/rtc");

    exit(errno);

  }

 

  fprintf(stderr, "\n\t\t\tEnjoy TV while boiling water.\n\n");

 

  // 首先是一个报警器的例子,设定10分钟后"响铃"   

  // 获取RTC中保存的当前日期时间信息

  /* Read the RTC time/date */

  retval = ioctl(fd, RTC_RD_TIME, &rtc_tm);

  if (retval == -1) {

    perror("ioctl");

    exit(errno);

  }

 

  fprintf(stderr, "\n\nCurrent RTC date/time is %d-%d-%d,%02d:

%02d:%02d.\n",   

      rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,

      rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);

 

  // 设定时间的时候要避免溢出

  rtc_tm.tm_min += 10;

  if (rtc_tm.tm_sec >= 60) {

    rtc_tm.tm_sec %= 60;

    rtc_tm.tm_min++;

  }

  if  (rtc_tm.tm_min == 60) {

    rtc_tm.tm_min = 0;

    rtc_tm.tm_hour++;

  }

  if  (rtc_tm.tm_hour == 24)

    rtc_tm.tm_hour = 0;

 

  // 实际的设定工作

  retval = ioctl(fd, RTC_ALM_SET, &rtc_tm);

  if (retval == -1) {

    perror("ioctl");

    exit(errno);

  }

 

  // 检查一下,看看是否设定成功

  /* Read the current alarm settings */

  retval = ioctl(fd, RTC_ALM_READ, &rtc_tm);

  if (retval == -1) {

    perror("ioctl");

    exit(errno);

  }

 

  fprintf(stderr, "Alarm time now set to %02d:%02d:%02d.\n",

      rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);

 

  // 光设定还不成,还要启用alarm类型的中断才行

  /* Enable alarm interrupts */

  retval = ioctl(fd, RTC_AIE_ON, 0);

  if (retval == -1) {

    perror("ioctl");

    exit(errno);

  }

 

  // 现在程序可以耐心的休眠了,10分钟后中断到来的时候它就会被唤醒

  /* This blocks until the alarm ring causes an interrupt */

  retval = read(fd, &data, sizeof(unsigned long));

  if (retval == -1) {

    perror("read");

    exit(errno);

  }

  irqcount++;

  fprintf(stderr, " okay. Alarm rang.\n");

}

这个例子稍微显得有点复杂,用到了openioctlread等诸多系统调用,初看起来让人眼花缭乱。其实如果简化一下的话,过程还是“烧开水”:设定定时器、等待定时器超时、执行相应的操作(“关煤气灶”)。

读者可能不理解的是:这个例子完全没有表现出中断带来的好处啊,在等待10分钟的超时过程中,程序依然什么都不能做,只能休眠啊?

读者需要注意自己的视角,我们所说的中断能够提升并发处理能力,提升的是CPU的并发处理能力。在这里,上面的程序可以被看作是烧开水,在烧开水前,闹铃已经被上好,10分钟后CPU会被中断(闹铃声)惊动,过来执行后续的关煤气工作。也就是说,CPU才是这里唯一具有处理能力的主体,我们在程序中主动利用中断机制来节省CPU的耗费,提高CPU的并发处理能力。这有什么好处呢?试想如果我们还需要CPU烤面包,CPU就有能力完成相应的工作,其它的工作也一样。这其实是在多任务操作系统环境下程序生存的道德基础——“我为人人,人人为我”。

好了,这段程序其实是我们进入Linux中断机制的引子,现在我们就进入Linux中断世界。

更详细的内容和其它一些注意事项请参考内核源代码包中Documentations/rtc.txt


   

      RTC中断服务程序

RTC中断服务程序包含在内核源代码树根目录下的driver/char/rtc.c文件中,该文件正是RTC设备的驱动程序——我们曾经提到过,中断服务程序一般由设备驱动程序提供,实现设备中断特有的操作。

SagaLinux中注册中断的步骤在Linux中同样不能少,实际上,两者的原理区别不大,只是Linux由于要解决大量的实际问题(比如SMP的支持、中断的共享等)而采用了更复杂的实现方法。

RTC驱动程序装载时,rtc_init()函数会被调用,对这个驱动程序进行初始化。该函数的一个重要职责就是注册中断处理程序:

        if (request_irq(RTC_IRQ,rtc_interrupt,SA_INTERRUPT,rtc,NULL)){
           printk(KERN_ERR rtc:cannot register IRQ %d\n,rtc_irq);
                return EIO;
        }

这个request_irq函数显然要比SagaLinux中同名函数复杂很多,光看看参数的个数就知道了。不过头两个参数两者却没有区别,依稀可以推断出:它们的主要功能都是完成中断号与中断服务程序的绑定。

关于Linux提供给系统程序员的、与中断相关的函数,很多书籍都给出了详细描述,如“Linux Kernel Development”。我这里就不做重复劳动了,现在集中注意力在中断服务程序本身上。

static (int , void *dev_id,

struct *)

{

        /*

         *     Can be an alarm interrupt, update complete interrupt,

         *     or a periodic interrupt. We store the status in the

         *     low byte and the number of interrupts received since

         *     the last read in the remainder of rtc_irq_data.

         */

        (&);

        += 0x100;

        &= ~0xff;

        |= (() & 0xF0);

 

        if ( & )

                (&,

+ /

q

感受RTC——


那么PowerOff(关机)算不算中断呢?如果从字面上讲,肯定符合汉语对中断的定义,但是从信号格式、处理方法等方面来看,就很难符合我们的理解了。Intel怎么说的呢?该中断没有采用通用的中断处理机制。那么到底是不时中断呢?我也说不上来:(

 

之所以这里使用汇编而不是C来实现这些函数,是因为C编译器会在函数的实现中推入额外的栈信息。而CPU在中断来临时保存和恢复现场都按照严格的格式进行,一个字节的变化都不能有。

 原文地址
阅读(1031) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~