📄 x1206.html
字号:
<HTML
><HEAD
><TITLE
>Interrupt Handlers</TITLE
><META
NAME="GENERATOR"
CONTENT="Microsoft FrontPage 4.0"><LINK
REL="HOME"
TITLE="The Linux Kernel Module Programming Guide"
HREF="index.html"><LINK
REL="UP"
TITLE="Interrupt Handlers"
HREF="interrupthandlers.html"><LINK
REL="PREVIOUS"
TITLE="Interrupt Handlers"
HREF="interrupthandlers.html"><LINK
REL="NEXT"
TITLE="Symmetric Multi Processing"
HREF="c1289.html"></HEAD
><BODY
CLASS="SECT1"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>Linux内核驱动模块编程指南 (内核版本2.2, 2.4)</TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="interrupthandlers.html"
ACCESSKEY="P"
>返回</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
>第12章. 中断处理</TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="c1289.html"
ACCESSKEY="N"
>继续</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="SECT1"
><H1
CLASS="SECT1"
><A
NAME="AEN1206"
></A
>12.1. 中断处理</H1
><DIV
CLASS="SECT2"
><H2
CLASS="SECT2"
><A
NAME="AEN1213"
></A
>12.1.1. 中断处理</H2
><P
> </P
><P
>除了刚结束的那章,我们目前在内核中所做的每件事都只不过是对某个请求的进程的响应,要么是对某个特殊的文件的处理,要么是发送一个 <TT
CLASS="FUNCTION"
>ioctl()</TT
>
,要么是调用一个系统调用。但是内核的工作不仅仅是响应某个进程的请求。还有另外一项非常重要的工作就是负责对硬件的管理。</P
><P
>在CPU和硬件之间的活动大致可分为两种。第一种是CPU发送指令给硬件,第二种就是硬件要返回某些信息给CPU。后面的那种又叫做中断,因为要知道何时同硬件对话才适宜而较难实现。硬件设备通常只有很少的缓存,如果你不及时的读取里面的信息,这些信息就会丢失。</P
><P
>在Linux中,硬件中断被叫作IRQ(<EM
>I</EM
>nterrupt
<EM
>R</EM
>e<EM
>q</EM
>uests中断请求)<A
NAME="AEN1222"
HREF="#FTN.AEN1222"
><SPAN
CLASS="footnote"
>[1]</SPAN
></A
><SPAN
CLASS="footnote"
>。有两种硬件中断,短中断和长中断。短中断占用的时间非常短,在这段时间内,整个系统被阻塞,任何其它中断都不会处理。长中断占用的时间相对较长,在此期间,可能会有别的中断发生请求处理(不是相同设备发出的中断)。可能的话,尽量将中断声明为长中断。</SPAN
></P
><P
>当CPU接收到一个中断时,它停止正在处理的一切事务(除非它在处理另一个更重要的中断,在这种情况下它只会处理完这个重要的中断才会回来处理新产生的中断),将运行中的那些参数压入栈中然后调用中断处理程序。这同时意味着中断处理程序本身也有一些限制,因为此时系统的状态并不确定。解决的办法是让中断处理程序尽快的完成它的事务,通常是从硬件读取信息和向硬件发送指令,然后安排下一次接收信息的相关处理(这被称为“bottom
half”
<A
NAME="AEN1299"
HREF="#FTN.AEN1299"
><SPAN
CLASS="footnote"
>[2]</SPAN
></A
>
),然后返回。内核确保被安排的事务被尽快的执行——当被执行时,在内核模块中允许的操作就是被允许的。</P
><P
>实现的方法是调用 <TT
CLASS="FUNCTION"
>request_irq()</TT
> 函数,当接受到相应的IRQ时(共有15种中断,在Intel架构平台上再加上1种用于串连中断控制器的中断)去调用你的中断处理程序。该函数接收IRQ号,要调用的处理IRQ函数的名称,中断请求的类别标志位,文件
<TT
CLASS="FILENAME"
>/proc/interrupts</TT
>
中声明的设备的名字,和传递给中断处理程序的参数。中断请求的类别标志位可以为
<TT
CLASS="PARAMETER"
><I
>SA_SHIRQ</I
></TT
>
来告诉系统你希望与其它中断处理程序共享该中断号(这通常是由于一些设备共用相同的IRQ号),也可以为
<TT
CLASS="PARAMETER"
><I
>SA_INTERRUPT</I
></TT
>
来告诉系统这是一个快速中断,这种情况下该函数只有在该IRQ空闲时才会成功返回,或者同时你又决定共享该IQR。</P
><P
>然后,在中断处理程序内部,我们与硬件对话,接着使用带 <TT
CLASS="FUNCTION"
>tq_immediate()</TT
> 和 <TT
CLASS="FUNCTION"
>mark_bh(BH_IMMEDIATE)</TT
> 的 <TT
CLASS="FUNCTION"
>queue_task_irq()</TT
> 去对bottom half队列进行调度。我们不能使用2.0版本种标准的
<TT
CLASS="FUNCTION"
>queue_task</TT
> 的原因中断可能就发生在别人的 <TT
CLASS="FUNCTION"
>queue_task</TT
><A
NAME="AEN1258"
HREF="#FTN.AEN1258"
><SPAN
CLASS="footnote"
>[3]</SPAN
></A
> 中。我们需要 <TT
CLASS="FUNCTION"
>mark_bh</TT
> 是因为早期版本的Linux只有一个可以存储32个bottom
half的数组,并且现在它们中的一个(<TT
CLASS="PARAMETER"
><I
>BH_IMMEDIATE</I
></TT
>)已经被用来连接没有分配到队列中的入口的硬件驱动的bottom
half。</P
></DIV
><DIV
CLASS="SECT2"
><H2
CLASS="SECT2"
><A
NAME="KEYBOARD"
></A
>12.1.2. Intel架构中的键盘</H2
><P
>剩余的这部分是只适用Intel架构的。如果你不使用Intel架构的平台,它们将不会工作,不要去尝试编译以下的代码。</P
><P
>在写这章的事例代码时,我遇到了一些困难。一方面,我需要一个可以得到实际有意义结果的,能在各种平台上工作的例子。另一方面,内核中已经包括了各种设备驱动,并且这些驱动将无法和我的例子共存。我找到的解决办法是为键盘中断写点东西,当然首先禁用普通的键盘中断。因为该中断在内核中定义为一个静态连接的符号(见
<TT
CLASS="FILENAME"
>drivers/char/keyboard.c</TT
>),我们没有办法恢复。所以在 <B
><TT
CLASS="USERINPUT"
>insmod </TT
></B
>前,如果你爱惜你的机器,新打开一个终端运行 <TT
CLASS="USERINPUT"
><B
>sleep 120
; reboot</B
></TT
> 。</P
><P
>该代码将自己绑定在IRQ 1,
也就是Intel架构中键盘的IRQ。然后,当接收到一个键盘中断请求时,它读取键盘的状态(那就是<TT
CLASS="USERINPUT"
><B
>inb(0x64)</B
></TT
>的目的)和扫描码,也就是键盘返回的键值。然后,一旦内核认为这是符合条件的,它运行 <TT
CLASS="FUNCTION"
>got_char</TT
> 去给出操作的键(扫描码的头7个位)和是按下键(扫描码的第8位为0)还是弹起键(扫描码的第8位为1)。</P
><DIV
CLASS="EXAMPLE"
><A
NAME="AEN1286"
></A
><P
><B
>Example 12-1. intrpt.c</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>/* intrpt.c - An interrupt handler.
*
* Copyright (C) 2001 by Peter Jay Salzman
*/
/* The necessary header files */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -