linux中断相关知识

中断相关API:
补充:request_irq最后一个参数 dev_id ,和 free_irq 最后一个参数devid和中断服务函数的最后一个参数 dev_id 是同一个参数。

1)中断注册函数:
request_irq(unsigned int irq,      //中断号
            irq_handler_t handler, //中断服务函数
            unsigned long flags,   //中断标志,
              const char *name,      //中断名,随便
              void *dev_id)          //中断id,只要是惟一就可以

Interrupt.h (include\linux) 18493   2011/8/20

int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
        const char *name, void *dev)
{
    return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}

返回值:
request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。

PS:
1.request_irq()函数可能会睡眠,所以,不能在中断上下文或其它不允许阻塞的代码中调用该函数。
2.当一个给定的中断处理程序正在执行时,这条中断线上的其它中断都会被屏蔽。but,所有其他中断线上的中断都是打开的。因此这些不同中断线上的其他中断都能被处理。 

Manage.c (kernel\irq)   27547   2009/12/3
/**
 *  request_threaded_irq - allocate an interrupt line
 *  @irq: Interrupt line to allocate
 *  @handler: Function to be called when the IRQ occurs.
 *        Primary handler for threaded interrupts
 *        If NULL and thread_fn != NULL the default
 *        primary handler is installed
 *  @thread_fn: Function called from the irq handler thread
 *          If NULL, no irq thread is created
 *  @irqflags: Interrupt type flags
 *  @devname: An ascii name for the claiming device
 *  @dev_id: A cookie passed back to the handler function
 *
 *  This call allocates interrupt resources and enables the
 *  interrupt line and IRQ handling. From the point this
 *  call is made your handler function may be invoked. Since
 *  your handler function must clear any interrupt the board
 *  raises, you must take care both to initialise your hardware
 *  and to set up the interrupt handler in the right order.
 *
 *  If you want to set up a threaded irq handler for your device
 *  then you need to supply @handler and @thread_fn. @handler ist
 *  still called in hard interrupt context and has to check
 *  whether the interrupt originates from the device. If yes it
 *  needs to disable the interrupt on the device and return
 *  IRQ_WAKE_THREAD which will wake up the handler thread and run
 *  @thread_fn. This split handler design is necessary to support
 *  shared interrupts.
 *
 *  Dev_id must be globally unique. Normally the address of the
 *  device data structure is used as the cookie. Since the handler
 *  receives this value it makes sense to use it.
 *
 *  If your interrupt is shared you must pass a non NULL dev_id
 *  as this is required when freeing the interrupt.
 *
 *  Flags:
 *
 *  IRQF_SHARED     Interrupt is shared
 *  IRQF_DISABLED   Disable local interrupts while processing
 *  IRQF_SAMPLE_RANDOM  The interrupt can be used for entropy
 *  IRQF_TRIGGER_*      Specify active edge(s) or level
 *
 */
int request_threaded_irq(unsigned int irq,
                                           irq_handler_t handler,
                                                 irq_handler_t thread_fn,
                                                 unsigned long irqflags,
                                                 const char *devname,
                                                 void *dev_id);

irq:
是要申请的硬件中断号。

handler:
是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它。原型定义为:typedef irqreturn_t (*irq_handler_t)(int, void *);

irqflags:linux 2.4 (SA_) 和 linux 2.6(IRQF_) 不同
是中断处理的属性,若设置了IRQF_DISABLED (老版本中的SA_INTERRUPT),则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置了IRQF_SHARED (老版本中的SA_SHIRQ),则表示多个设备共享中断,若设置了IRQF_SAMPLE_RANDOM(老版本中的SA_SAMPLE_RANDOM),表示对系统获取随机数有好处。(这几个flag是可以通过或的方式同时使用的)。若是外部中断还要设置IRQF_TRIGGER_XXX标志,表示选择外部中断的触发电平。
/* Interrupt.h (include\linux)  18937   2013/7/14
 * These correspond to the IORESOURCE_IRQ_* defines in
 * linux/ioport.h to select the interrupt line behaviour.  When
 * requesting an interrupt without specifying a IRQF_TRIGGER, the
 * setting should be assumed to be "as already configured", which
 * may be as per machine or firmware initialisation.
 */
#define IRQF_TRIGGER_NONE       0x00000000    //中断边沿未设置
#define IRQF_TRIGGER_RISING   0x00000001    //上升沿中断
#define IRQF_TRIGGER_FALLING    0x00000002    //下降沿中断
#define IRQF_TRIGGER_HIGH       0x00000004    //高电平中断
#define IRQF_TRIGGER_LOW        0x00000008    //低电平中断
#define IRQF_TRIGGER_MASK   (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
                 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE    0x00000010

/* Interrupt.h (include\linux)  18937   2013/7/14
 * These flags used only by the kernel as part of the
 * irq handling routines.
 *
 * IRQF_DISABLED - keep irqs disabled when calling the action handler
 * IRQF_SAMPLE_RANDOM - irq is used to feed the random generator
 * IRQF_SHARED - allow sharing the irq among several devices
 * IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
 * IRQF_TIMER - Flag to mark this interrupt as timer interrupt
 * IRQF_PERCPU - Interrupt is per cpu
 * IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
 * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
 *                registered first in an shared interrupt is considered for
 *                performance reasons)
 * IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished.
 *                Used by threaded interrupts which need to keep the
 *                irq line disabled until the threaded handler has been run.
 */
/*快速中断标志,对应老版本中的SA_INTERRUPT标志,
表明在处理本中断时屏蔽所有中断,而在没设置此标志位的程序中,
都是开中断处理的,可以进行中断嵌套。*/
#define IRQF_DISABLED           0x00000020  //快速中断标志
#define IRQF_SAMPLE_RANDOM  0x00000040  //用于随机数据产生
#define IRQF_SHARED           0x00000080  //用于共享中断,设置此标志时,
                                        //request_irq最后一个参数dev不能为NULL,对应老版本内核的SA_SHIRQ;
#define IRQF_PROBE_SHARED     0x00000100  //探测共享中断;
#define IRQF_TIMER            0x00000200
#define IRQF_PERCPU           0x00000400
#define IRQF_NOBALANCING      0x00000800
#define IRQF_IRQPOLL            0x00001000
#define IRQF_ONESHOT            0x00002000

devname:
设置中断名称,通常是设备驱动程序的名称  在cat /proc/interrupts中可以看到此名称。

dev_id:
中断服务程序的参数,可以为NULL,但在注册共享中断时,此参数不能为NULL。在中断共享时会用到,一般设置为这个设备的设备结构体。

返回值:
返回0表示成功,返回 -INVAL 表示中断号无效或处理函数指针为NULL,返回 -EBUSY 表示中断已经被占用且不能共享。

注销函数定义在Kernel/irq/manage.c中定义:
void free_irq(unsigned int irq, void *dev_id)

ps:
dev_id参数为什么必须的,而且是必须唯一的。
当调用free_irq注销中断处理函数时(通常卸载驱动时其中断处理函数也会被注销掉),因为dev_id是唯一的,所以可以通过它来判断从共享中断线上的多个中断处理程序中删除指定的一个。如果没有这个参数,那么kernel不可能知道给定的中断线上到底要删除哪一个处理程序。

/**
 *  disable_irq_nosync - disable an irq without waiting
 *  @irq: Interrupt to disable
 *
 *  Disable the selected interrupt line.  Disables and Enables are
 *  nested.
 *  Unlike disable_irq(), this function does not ensure existing
 *  instances of the IRQ handler have completed before returning.
 *
 *  This function may be called from IRQ context.
 */
void disable_irq_nosync(unsigned int irq)
{
    struct irq_desc *desc = irq_to_desc(irq);
    unsigned long flags;

    if (!desc)
        return;

    chip_bus_lock(irq, desc);
    spin_lock_irqsave(&desc->lock, flags);
    __disable_irq(desc, irq, false);
    spin_unlock_irqrestore(&desc->lock, flags);
    chip_bus_sync_unlock(irq, desc);
}

/**
 *  disable_irq - disable an irq and wait for completion
 *  @irq: Interrupt to disable
 *
 *  Disable the selected interrupt line.  Enables and Disables are
 *  nested.
 *  This function waits for any pending IRQ handlers for this interrupt
 *  to complete before returning. If you use this function while
 *  holding a resource the IRQ handler may need you will deadlock.
 *
 *  This function may be called - with care - from IRQ context.
 */
void disable_irq(unsigned int irq)
{
    struct irq_desc *desc = irq_to_desc(irq);

    if (!desc)
        return;

    disable_irq_nosync(irq);
    if (desc->action)
        synchronize_irq(irq);
}

disable_irq 关闭中断并等待中断处理完后返回, 而disable_irq_nosync立即返回.
在中断处理程序中应该使用disable_irq_nosync来关闭中断

关闭中断并等待中断处理完后返回.从代码中可以看到, disable_irq先是调用了disable_irq_nosync, 然后检测desc->action是否为1. 在中断处理程序中, action是置1的, 所以进入synchronize_irq函数中.

/**
 * synchronize_irq - wait for pending IRQ handlers (on other CPUs)
 * @irq: interrupt number to wait for
 *
 * This function waits for any pending IRQ handlers for this interrupt
 * to complete before returning. If you use this function while
 * holding a resource the IRQ handler may need you will deadlock.
 *
 * This function may be called - with care - from IRQ context.
 */
void synchronize_irq(unsignedint irq)
{
 struct irq_desc *desc= irq_to_desc(irq);
 unsigned int status;
 if (!desc)
  return;
 do {
  unsigned long flags;
  /*
   * Wait until we‘re out of the critical section. This might
   * give the wrong answer due to the lack of memory barriers.
   */
  while (desc->status& IRQ_INPROGRESS)
   cpu_relax();
  /* Ok, that indicated we‘re done: double-check carefully. */
  spin_lock_irqsave(&desc->lock, flags);
  status = desc->status;
  spin_unlock_irqrestore(&desc->lock, flags);
  /* Oops, that failed? */
 } while (status & IRQ_INPROGRESS);
 /*
  * We made sure that no hardirq handler is running. Now verify
  * that no threaded handlers are active.
  */
 wait_event(desc->wait_for_threads,!atomic_read(&desc->threads_active));
}

注释中说明该函数是在等待中断处理程序的结束, 这也是disable_irq与disable_irq_nosync不同的主要所在. 但是在中断处理函数中调用会发生什么情况呢? 进入中断处理函数前IRQ_INPROGRESS会被__setup_irq设置, 所以程序会一直陷在while循环中, 而此时内核以经被独占, 这就导致系统死掉.

/**
 *  enable_irq - enable handling of an irq
 *  @irq: Interrupt to enable
 *
 *  Undoes the effect of one call to disable_irq().  If this
 *  matches the last disable, processing of interrupts on this
 *  IRQ line is re-enabled.
 *
 *  This function may be called from IRQ context only when
 *  desc->chip->bus_lock and desc->chip->bus_sync_unlock are NULL !
 */
void enable_irq(unsigned int irq)
{
    struct irq_desc *desc = irq_to_desc(irq);
    unsigned long flags;

    if (!desc)
        return;

    chip_bus_lock(irq, desc);
    spin_lock_irqsave(&desc->lock, flags);
    __enable_irq(desc, irq, false);
    spin_unlock_irqrestore(&desc->lock, flags);
    chip_bus_sync_unlock(irq, desc);
}

local_irq_disable仅仅是设置当前CPU的中断屏蔽位
disable_irq      是禁用全部cpu中断(只是当前irq)

如果你要禁止所有的中断该怎么办? 在2.6内核中,可以通过下面两个函数中的其中任何一个关闭当前处理器上的所有中断处理,这两个函数定义在 <asm/system.h>中:

void local_irq_save(unsigned long flags);关中断
void local_irq_disable(void);           ;关中断

对 local_irq_save的调用将把当前中断状态保存到flags中,然后禁用当前处理器上的中断发送。注意, flags 被直接传递, 而不是通过指针来传递。 local_irq_disable不保存状态而关闭本地处理器上的中断发送; 只有我们知道中断并未在其他地方被禁用的情况下,才能使用这个版本。
可通过如下函数打开中断:
void local_irq_restore(unsigned long flags);开中断
void local_irq_enable(void);                开中断

示例:
int flag;
local_irq_save(flags);
.....
local_irq_restore(flags);

第一个版本将local_irq_save保存的flags状态值恢复, 而local_irq_enable无条件打开中断. 与 disable_irq不同, local_irq_disable不会维护对多次的调用的跟踪。 如果调用链中有多个函数需要禁止中断, 应该使用local_irq_save.
在2.6内核, 没有方法全局禁用整个系统的所有中断。 内核开发者认为关闭所有中断的代价太高,因此没有必要提供这个能力。如果读者使用的老驱动程序调用了类似cli和sti这样的函数,为了该驱动程序能够在2.6下使用,则需要进行修改而使用正确的锁。

in_interrupt()是判断当前进程是否处于中断上下文,这个中断上下文包括底半部和硬件中断处理过程,

函数实现:

#define in_interrupt() ({ const int __cpu = smp_processor_id(); /
(local_irq_count(__cpu) + local_bh_count(__cpu) != 0); })

判断中断计数和底半部计数是否〉0,如果只希望判断是否在硬件中断上下文,则可以使用:in_irq()。

==============================================================================
图片  irq2
1. Linux定义了名字为irq_desc的中断例程描述符表:(include/linux/irq.h)
struct irqdesc irq_desc[NR_IRQS];
NR_IRQS表示中断源的数目。
2. irq_desc[]是一个指向irq_desc结构的数组, irq_desc结构是各个设备中断服务例程的描述符。
struct irq_desc {
irq_flow_handler_t handle_irq;
struct irq_chip *chip;
void *handler_data;
void *chip_data;
struct irqaction *action;
unsigned int status;
unsigned intdepth;
unsigned int wake_depth;
unsigned int irq_count;
unsigned int irqs_unhandled;
spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_t affinity;
unsigned int cpu;
#endif
#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
cpumask_t pending_mask;
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry*dir;
#endif
const char *name;
} ____cacheline_aligned;
Irq_desc结构体中的成员action指向该中断号对应的irqaction结构体链表。Irqaction结构体定义如下:
// include/linux/interrupt.h
struct irqaction{
irq_handler_t handler; // 指向中断服务程序
unsigned long flags; // 中断标志
unsigned long mask; // 中断掩码
const char *name;// I/O设备名
void *dev_id;// 设备标识
struct irqaction*next;// 指向下一个描述符
int irq;// IRQ线
struct proc_dir_entry *dir; // 指向IRQn相关的/proc/irq/n目录的描述符
};
其中关键的handler成员指向了该设备的中断服务程序,由执行request_irq时建立。
3. 在驱动程序初始化时,若使用到中断,通常调用函数request_irq()建立该驱动程序对应的irqaction结构体,并把它登记到irq_desc [irq_num]->action链表中。Iqr_num为驱动程序申请的中断号。
request_irq()函数的原型如下:
// kernel/irq/manage.c
int request_irq(unsignedint irq,
irqreturn_t (*handler)(int,void*,struct pt_regs*),
unsignedlong irqflags,
const char *devname,
void *dev_id);
参数irq是设备中断求号,在向irq_desc []数组登记时,它做为数组的下标。把中断号为irq的irqaction结构体的首地址写入irq_desc [irq]->action。这样就把设备的中断请求号与该设备的中断服务例程irqaction联系在一起了。
这样当CPU接收到中断请求后,就可以根据中断号通过irq_desc []找到该设备的中断服务程序。流程如上图所示。
4. 关于共享中断
共享中断的不同设备的iqraction结构体都会添加进该中断号对应的irq_desc结构体的action成员所指向的irqaction链表内。当内核发生中断时,它会依次调用该链表内所有的handler函数。因此,若驱动程序需要使用共享中断机制,其中断处理函数必须有能力识别是否是自己的硬件产生了中断。通常是通过读取该硬件设备提供的中断flag标志位进行判断。
时间: 2024-10-08 04:36:52

linux中断相关知识的相关文章

linux信号处理相关知识

因为要处理最近项目中碰上的多个子进程退出信号同时到达,导致程序不当产生core的情况,今天我花了时间看了一些关于linux信号处理的博客. 总结一下:(知识未经实践) 1 1 linux信号分两种,一种实时信号(也叫可靠信号),一种非实时信号(也叫不可靠信号). 2 2 实时信号之所以是可靠的,因为在进程阻塞该信号的时间内,发给该进程的所有实时信号会排队,而非实时信号则会合并为一个信号.(可以这样理解,同一个不可靠信号同时产生时,只有一个信号会被处理,其它的丢弃) 3 3 早期设计的信号都是不可

Linux文件系统相关知识整理

1.      磁盘的物理结构 盘面:磁盘是由一叠盘面组成 磁头(Heads):每个磁头对应一个盘面,负责该盘面上的数据的读写 磁道(Track):每个盘面会围绕圆心划分出多个同心圆圈,每个圆圈叫做一个磁道 柱面(Cylinders):所有盘片上的同一位置的磁道组成的立体叫做一个柱面 扇区(Sector):以磁道为单位管理磁盘仍然太大,所以计算机前辈们又把每个磁道划分出了多个扇区 磁盘容量的大小一般和磁道的密集程度有关,扇区数一般是不变的 2.      磁盘I/O的过程 首先是磁头径向移动来寻

Linux虚拟内存相关知识

Linux 的虚拟内存管理有几个关键概念: 1.每个进程都有独立的虚拟地址空间,进程访问的虚拟地址并不是真正的物理地址: 2.虚拟地址可通过每个进程上的页表(在每个进程的内核虚拟地址空间)与物理地址进行映射,获得真正物理地址: 3.如果虚拟地址对应物理地址不在物理内存中,则产生缺页中断,真正分配物理地址,同时更新进程的页表:如果此时物理内存已耗尽,则根据内存替换算法淘汰部分页面至物理磁盘中. Linux 虚拟地址空间如何分布? Linux 使用虚拟地址空间,大大增加了进程的寻址空间, 虚拟地址空

Linux中相关知识(atexit(),fork(),粘滞位)

1.atexit()函数 函数名: atexit 头文件:#include<stdlib.h> 功 能: 注册终止函数(即main执行结束后调用的函数) 用 法: int atexit(void (*func)(void)); 注意:一个进程可以登记32个函数,这些函数由exit自动调用,这些函数被称为终止处理函数,atexit函数可以登记这些函数. exit调用终止处理函数的顺序和atexit登记的顺序相反,如果一个函数被多次登记,也会被多次调用. 程序例: #include <std

linux操作系统相关知识

一.什么是 Linux 呢?! ·  试说明 Unix 不 Linux 的历史? o  Multics 系统:由 Bell(贝尔实验室).MIT(麻省理工学院)不 GE(美国通用电器) 合作开发的一个系统: o  1969:K. Thompson 替 DEC 公司写了一个简单的 file system 系统,此为 Unix 的前 身,但是 Unix 一词尚未出现 o  1973:由 Bell 的 D. Richie 以 C 诧言改写了 Thompson 的小系统,全部以 ASCII 档案 迚行改

Linux网络相关知识

ifconfig命令 查看网卡IP 如果系统没有该命令可以使用yum安装: [[email protected] ~]# yum install -y net-tools 查看网卡IP还可以使用命令:ip add,这个显示有点乱! ifconfig -a 显示所有网卡信息(包括down掉的或者没有IP地址的网卡) 启动/关闭网卡 ifup/ifdown [网卡名] 应用环境:更改单个指定网卡配置后需要重启才能生效,为了避免关闭或重启所有网卡可以对单个网卡执行该命令.注: 如果网卡正在使用中,不要

&lt; 独立项目 - 文本挖掘 &gt; - 2016/10/25 第一更 - &lt;Linux相关知识准备&gt;

< 独立项目 -  文本挖掘 > 项目立项的相关背景介绍,TODO方向. 一.Ubuntu环境配置 主机系统:Windows 7 SP1  64位操作系统 | i5-4210 CPU | 16GB RAM VirtualBox虚拟环境:GUN VitrualBox Linux系统:Ubuntu 15.10(ubuntu-15.10-desktop-i386) 二.Linux Ubuntu 基础知识 使用Ctrl+Alt+[F1~F6],可以切换到1~6号控制台.使用Ctrl+Alt+F7返回图

linux内存基础知识和相关调优方案

内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁.计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大.内存作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据.只要计算机在运行中,CPU就会把需要运算的数据调到内存中进行运算,当运算完成后CPU再将结果传送出来,内存的运行也决定了计算机的稳定运行.对于整个操作系统来说,内存可能是最麻烦的的设备.而其性能的好坏直接影响着整个操作系统. 我们知道CPU是不能与硬盘打交道的,只有数据被载入到内存中才可

linux 服务器分区格式化相关知识 -mount

关于linux 系统mount和mkfs 的相关知识: 使用mount 1)  Mount的相关格式:mount [-t 文件类型][-o  选项] devicedir 详解: -t 文件类型,通常默认mount会自动选择正确的类型,通常类型ext2/ext3/ext4之类的. 常用的类型有:                  光盘或光盘镜像:iso9660 DOS fat16文件系统:msdos Windows 9x fat32文件系统:vfat Windows NT ntfs文件系统:ntf