LINUX内核中断编程及等待队列

今天学习关于内核的中断

抛砖引玉:
提出问题
1.请简述中断的处理过程
贴上一个百科知识中断
还找了一个关于异常和中断的关系的中断与异常的区别

在这里插入图片描述
在这里插入图片描述

CPU对中断处理的流程
我们首先必须了解CPU在接收到中断信号时会做什么。没办法,操作系统必须了解硬件的机制,不配合硬件就寸步难行。现在我们假定内核已被初始化,CPU在保护模式下运行。

 

CPU执行完一条指令后,下一条指令的逻辑地址存放在cs和eip这对寄存器中。在执行新指令前,控制单元会检查在执行前一条指令的过程中是否有中断或异常发生。如果有,控制单元就会抛下指令,进入下面的流程:

1.确定与中断或异常关联的向量i (0£i£255)2.籍由idtr寄存器从IDT表中读取第i项(在下面的描述中,我们假定该IDT表项中包含的是一个中断门或一个陷阱门)。

3.从gdtr寄存器获得GDT的基地址,并在GDT表中查找,以读取IDT表项中的选择符所标识的段描述符。这个描述符指定中断或异常处理程序所在段的基地址。

 

4.确信中断是由授权的(中断)发生源发出的。首先将当前特权级CPL(存放在cs寄存器的低两位)与段描述符(存放在GDT中)的描述符特权级DPL比较,如果CPL小于DPL,就产生一个“通用保护”异常,因为中断处理程序的特权不能低于引起中断的程序的特权。对于编程异常,则做进一步的安全检查:比较CPL与处于IDT中的门描述符的DPL,如果DPL小于CPL,就产生一个“通用保护”异常。这最后一个检查可以避免用户应用程序访问特殊的陷阱门或中断门。

 

5.检查是否发生了特权级的变化,也就是说, CPL是否不同于所选择的段描述符的DPL。如果是,控制单元必须开始使用与新的特权级相关的栈。通过执行以下步骤来做到这点:

    a.读tr寄存器,以访问运行进程的TSS段。

b.用与新特权级相关的栈段和栈指针的正确值装载ss和esp寄存器。这些值可以在TSS中找到(参见第三章的“任务状态段”一节)。

c.在新的栈中保存ss和esp以前的值,这些值定义了与旧特权级相关的栈的逻辑地址。

 

    6.如果故障已发生,用引起异常的指令地址装载cs和eip寄存器,从而使得这条指令能再次被执行。

    7.在栈中保存eflag、cs及eip的内容。

8.如果异常产生了一个硬错误码,则将它保存在栈中。

9.装载cs和eip寄存器,其值分别是IDT表中第i项门描述符的段选择符和偏移量域。这些值给出了中断或者异常处理程序的第一条指令的逻辑地址。
————————————————
版权声明:本文为CSDN博主「七七侠」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/kanghua/article/details/1843788

2.中断源有哪些

一般来说,外部硬件中断主要有以下几种:

一、I/O设备:如显示器、键盘、打印机等。

二、数据通道:软盘、硬盘、光盘等。

三、实时时钟:如外部的定时电路等。

四、用户故障源:如掉电、奇偶校验错误等。

产生于CPU内部的软件中断源有几种:

一、由CPU得运行结果产生:如除数为0、结果溢出、单步执行等。

二、执行中断指令INT:INT3

三、非法操作或指令引起异常处理。

3.中断发生时 暂停当前任务的时候采用的是哪一种处理方式(涉及哪些数据和结构)

4.中断的分类有哪些
软硬中断
5.恢复现场涉及的操作有哪些
从字面的意思也可以看成,这部分的代码就是将进入中断的时候保存的现场(寄存器值)恢复到实际的ARM的各个寄存器中,从而完全返 回到了中断发生的那一点

IRQ和FIQ

查询所用处理器的中断数据(查询用户手册)

以tiny4412为例

内核中断的操作过程

linux系统中有专门的中断子系统,原理很复杂,驱动开发者不需要知道具体细节,只需要知道如何应用该子系统提供的api来编写中断驱动代码即可
内核使用struct irqaction结构体描述一个中断,编写中断程序终极目标就是实现这个结构体
在内核中,文件大多以功能命名,内核中提供了一个interrupt.h的文件用来进行中断先关接口及数据结构的声明
interrupt.h include\linux 22053 2019/6/24 125

 * struct irqaction - per interrupt action descriptor
 * @handler:	interrupt handler function
 * @name:	name of the device
 * @dev_id:	cookie to identify the device-----------是否是一个共享中断
 * @percpu_dev_id:	cookie to identify the device
 * @next:	pointer to the next irqaction for shared interrupts
 * @irq:	interrupt number
 * @flags:	flags (see IRQF_* above)
 * @thread_fn:	interrupt handler function for threaded interrupts
 * @thread:	thread pointer for threaded interrupts
 * @thread_flags:	flags related to @thread
 * @thread_mask:	bitmask for keeping track of @thread activity
 * @dir:	pointer to the proc/irq/NN/name entry
 */
struct irqaction {
	irq_handler_t		handler;    //用户注册的中断处理函数 
	void			*dev_id;		//可以是用户传递的参数或者用来区分共享中断
	void __percpu		*percpu_dev_id;
	struct irqaction	*next;		//irqaction结构链,一个共享中断可以有多个中断处理函数 
	irq_handler_t		thread_fn;
	struct task_struct	*thread;
	unsigned int		irq;	//中断号
	unsigned int		flags;	//中断标识
	unsigned long		thread_flags;
	unsigned long		thread_mask;
	const char		*name;	//用户注册的中断名字,cat/proc/interrupts时可以看到 
	struct proc_dir_entry	*dir;
} ____cacheline_internodealigned_in_smp;

@flags:	flags (see IRQF_* above)

#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

/*
 * These flags used only by the kernel as part of the
 * irq handling routines.
 *
 * IRQF_DISABLED - keep irqs disabled when calling the action handler.
 *                 DEPRECATED. This flag is a NOOP and scheduled to be removed
 * 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.
 * IRQF_NO_SUSPEND - Do not disable this IRQ during suspend
 * IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set
 * IRQF_NO_THREAD - Interrupt cannot be threaded
 * IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device
 *                resume time.
 */
#define IRQF_DISABLED		0x00000020
#define IRQF_SAMPLE_RANDOM	0x00000040
#define IRQF_SHARED		0x00000080
#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
#define IRQF_NO_SUSPEND		0x00004000
#define IRQF_FORCE_RESUME	0x00008000
#define IRQF_NO_THREAD		0x00010000
#define IRQF_EARLY_RESUME	0x00020000

#define IRQF_TIMER		(__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD)
IRQF_DISABLED:私有中断,即一个中断请求对应一个中断服务函数
IRQF_SHARED:共享中断,多个中断请求对应一个中断服务函数

中断函数的函数指针:
typedef irqreturn_t (*irq_handler_t)(int, void *);
int:irq中断号
void *:dev设备标识
/**
 * enum irqreturn
 * @IRQ_NONE		interrupt was not from this device
 * @IRQ_HANDLED		interrupt was handled by this device
 * @IRQ_WAKE_THREAD	handler requests to wake the handler thread
 */
enum irqreturn {
	IRQ_NONE		= (0 << 0),
	IRQ_HANDLED		= (1 << 0),
	IRQ_WAKE_THREAD		= (1 << 1),
};

typedef enum irqreturn irqreturn_t;

操作过程

1.向内核提出中断申请
函数:static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
参数:

unsigned int irq:中断号(线)
irq_handler_t handler:中断服务函数
unsigned long flags:中断标志
const char *name:设备名
void *dev:设备标识,只有在共享中断中此参数此参数才有效,因为私有中断不共享服务函数,无需判断是哪一个设备触发的中断
私有中断中可有直接赋值NULL

使能
extern void enable_irq(unsigned int irq);
失能
extern void disable_irq(unsigned int irq);
2.释放中断
函数:extern void free_irq(unsigned int, void *);

关于gpio的接口函数

1将gpio转换后成相对应的irq
gpio_to_irq:
2 setvalue
gpio_set_value
3 getvalue
gpio_get_value

编程

.

发布了34 篇原创文章 · 获赞 10 · 访问量 3346

猜你喜欢

转载自blog.csdn.net/ice_masters/article/details/105088162