我是靠谱客的博主 甜美刺猬,最近开发中收集的这篇文章主要介绍Linux interrupt一、linux中中断的实现二、中断号三、中断的标志四、中断源对应的中断服务程序五、中断服务程序与原子上下文六、等待队列七、源码,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、linux中中断的实现

#include <linux/interrupt.h>
linux系统针对底层的中断处理过程做了封装,使用linux提供函数,可以实现中断的响应和处理。

1. 申请中断

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 ---->向中断服务程序发送的参数
返回值:
成功,返回0;失败,返回复数的错误码。

注意:

1)查看中断的信息:
[root@GEC6818 /]#cat /proc/interrupts 
           CPU0       CPU1       CPU2       CPU3       CPU4       CPU5       CPU6       CPU7       
 33:          0          0          0          0          0          0          0          0       GIC  pl08xdmac
 34:          0          0          0          0          0          0          0          0       GIC  pl08xdmac
 37:          0          0          0          0          0          0          0          0       GIC  rtc 1hz
 39:         11          5          4         12         18         12         10         18       GIC  nxp-uart
 48:          1          0          0          0          0          0          0          0       GIC  s3c2440-i2c.1
 49:       1096         48       1960       1223       1589       1246       1132       1533       GIC  s3c2440-i2c.2
中断号                          各个CPU上中断发生的次数                                           中断控制器   中断的名称
2)一个中断已经申请过了,再次申请,就申请不到了。
3)中断的优先级是高于所有的进程的。

2. 中断服务程序

注意问题:
裸机:中断服务程序是没有返回值和参数的。
linux:中断服务程序是底层的中断处理过程调用的函数,所以有参数和返回值。

irqreturn_t (*irq_handler_t)(int irq, void *dev);

参数说明:
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;

3. 释放中断

void free_irq(unsigned int, void *);

参数说明:
int irq ----> 中断号
void *dev —>申请中断的时候,传递的参数

二、中断号

每个中断源都有一个唯一的中断号,中断号和中断源是对应的,中断源和硬件是相关的,不同的处理器,中断源是不同的。

中断号的定义:
/arch/arm/mach-s5p6818/include/mach/s5p6818_irq.h

/*
 * GIC Interrupt (0 ~ 32), must be align 32
 */
#define IRQ_GIC_START			(0)
#define IRQ_GIC_PPI_START		(IRQ_GIC_START  +  16)
#define IRQ_GIC_PPI_PVT			(IRQ_GIC_START  +  29)
#define IRQ_GIC_PPI_WDT			(IRQ_GIC_START  +  30)
#define IRQ_GIC_PPI_VIC			(IRQ_GIC_START  +  31)
#define IRQ_GIC_END				(IRQ_GIC_START  +  32)

/*
 * Physical Interrupt Number 64 (0~63)
 */
#define IRQ_PHY_MCUSTOP					(0  + 32)
#define IRQ_PHY_DMA0					(1  + 32)
#define IRQ_PHY_DMA1					(2  + 32)
#define IRQ_PHY_CLKPWR_INTREQPWR		(3  + 32)
#define IRQ_PHY_CLKPWR_ALIVEIRQ			(4  + 32)
#define IRQ_PHY_CLKPWR_RTCIRQ			(5  + 32)
#define IRQ_PHY_UART1					(6	+ 32) // pl01115_Uart_modem
#define IRQ_PHY_UART0					(7	+ 32) // UART0_MODULE
#define IRQ_PHY_UART2					(8	+ 32) // UART1_MODULE
#define IRQ_PHY_UART3					(9	+ 32) // pl01115_Uart_nodma0
#define IRQ_PHY_UART4					(10 + 32)	// pl01115_Uart_nodma1
#define IRQ_PHY_UART5					(11 + 32)	// pl01115_Uart_nodma2
#define IRQ_PHY_SSP0					(12 + 32)
#define IRQ_PHY_SSP1					(13 + 32)
#define IRQ_PHY_SSP2					(14 + 32)
#define IRQ_PHY_I2C0					(15 + 32)
#define IRQ_PHY_I2C1					(16 + 32)
#define IRQ_PHY_I2C2					(17 + 32)
#define IRQ_PHY_DEINTERLACE				(18 + 32)
#define IRQ_PHY_SCALER					(19 + 32)
#define IRQ_PHY_AC97					(20 + 32)
#define IRQ_PHY_SPDIFRX					(21 + 32)
#define IRQ_PHY_SPDIFTX					(22 + 32)
#define IRQ_PHY_TIMER_INT0				(23 + 32)
#define IRQ_PHY_TIMER_INT1				(24 + 32)
#define IRQ_PHY_TIMER_INT2				(25 + 32)
#define IRQ_PHY_TIMER_INT3				(26 + 32)
#define IRQ_PHY_PWM_INT0				(27 + 32)
#define IRQ_PHY_PWM_INT1				(28 + 32)
#define IRQ_PHY_PWM_INT2				(29 + 32)
#define IRQ_PHY_PWM_INT3				(30 + 32)
#define IRQ_PHY_WDT						(31 + 32)
#define IRQ_PHY_MPEGTSI					(32 + 32)
#define IRQ_PHY_DPC_P					(33 + 32)
#define IRQ_PHY_DPC_S					(34 + 32)
#define IRQ_PHY_RESCONV					(35 + 32)
#define IRQ_PHY_HDMI					(36 + 32)
#define IRQ_PHY_VIP0					(37 + 32)
#define IRQ_PHY_VIP1					(38 + 32)
#define IRQ_PHY_MIPI					(39 + 32)
#define IRQ_PHY_VR						(40 + 32)
#define IRQ_PHY_ADC						(41 + 32)
#define IRQ_PHY_PPM						(42 + 32)
#define IRQ_PHY_SDMMC0					(43 + 32)
#define IRQ_PHY_SDMMC1					(44 + 32)
#define IRQ_PHY_SDMMC2					(45 + 32)
#define IRQ_PHY_CODA960_HOST			(46 + 32)
#define IRQ_PHY_CODA960_JPG				(47 + 32)
#define IRQ_PHY_GMAC					(48 + 32)
#define IRQ_PHY_USB20OTG				(49 + 32)
#define IRQ_PHY_USB20HOST				(50 + 32)
#define IRQ_PHY_CAN0					(51 + 32)
#define IRQ_PHY_CAN1					(52 + 32)
#define IRQ_PHY_GPIOA					(53 + 32)
#define IRQ_PHY_GPIOB					(54 + 32)
#define IRQ_PHY_GPIOC					(55 + 32)
#define IRQ_PHY_GPIOD					(56 + 32)
#define IRQ_PHY_GPIOE					(57 + 32)
#define IRQ_PHY_CRYPTO					(58 + 32)
#define IRQ_PHY_PDM						(59 + 32)
#define IRQ_PHY_TMU0                    (60 + 32)
#define IRQ_PHY_TMU1                    (61 + 32)
#define IRQ_PHY_VIP2					(72 + 32)

#define IRQ_PHY_MAX_COUNT       		(74 + 32) // ADD GIC IRQ

/*
 * GPIO Interrupt Number 160 (106~265)
 */
#define IRQ_GPIO_START			IRQ_PHY_MAX_COUNT
#define IRQ_GPIO_END			(IRQ_GPIO_START + 32 * 5)	// Group: A,B,C,D,E

#define IRQ_GPIO_A_START		(IRQ_GPIO_START + PAD_GPIO_A)
#define IRQ_GPIO_B_START		(IRQ_GPIO_START + PAD_GPIO_B)
#define IRQ_GPIO_C_START		(IRQ_GPIO_START + PAD_GPIO_C)
#define IRQ_GPIO_D_START		(IRQ_GPIO_START + PAD_GPIO_D)
#define IRQ_GPIO_E_START		(IRQ_GPIO_START + PAD_GPIO_E)

/*
 * ALIVE Interrupt Number 6 (266~271)
 */
#define IRQ_ALIVE_START			IRQ_GPIO_END
#define IRQ_ALIVE_END			(IRQ_ALIVE_START + 6)

#define IRQ_ALIVE_0				(IRQ_ALIVE_START + 0)
#define IRQ_ALIVE_1				(IRQ_ALIVE_START + 1)
#define IRQ_ALIVE_2				(IRQ_ALIVE_START + 2)
#define IRQ_ALIVE_3				(IRQ_ALIVE_START + 3)
#define IRQ_ALIVE_4				(IRQ_ALIVE_START + 4)
#define IRQ_ALIVE_5				(IRQ_ALIVE_START + 5)

/*
 * MAX(Physical+Virtual) Interrupt Number
 */
#define IRQ_SYSTEM_END			IRQ_ALIVE_END

其中常用的中断:

1. 串口的中断号

#define IRQ_PHY_UART1					(6	+ 32) // pl01115_Uart_modem
#define IRQ_PHY_UART0					(7	+ 32) // UART0_MODULE
#define IRQ_PHY_UART2					(8	+ 32) // UART1_MODULE
#define IRQ_PHY_UART3					(9	+ 32) // pl01115_Uart_nodma0
#define IRQ_PHY_UART4					(10 + 32)	// pl01115_Uart_nodma1
#define IRQ_PHY_UART5					(11 + 32)	// pl01115_Uart_nodma2

2. IIC的中断号

#define IRQ_PHY_I2C0					(15 + 32)
#define IRQ_PHY_I2C1					(16 + 32)
#define IRQ_PHY_I2C2					(17 + 32)

3. 定时器的中断号

#define IRQ_PHY_TIMER_INT0				(23 + 32)
#define IRQ_PHY_TIMER_INT1				(24 + 32)
#define IRQ_PHY_TIMER_INT2				(25 + 32)
#define IRQ_PHY_TIMER_INT3				(26 + 32)

4. 其他

#define IRQ_PHY_ADC						(41 + 32)
#define IRQ_PHY_WDT						(31 + 32)

5. 外部中断的中断号

#define IRQ_PHY_MAX_COUNT       	(74 + 32) // ADD GIC IRQ
#define IRQ_GPIO_START			IRQ_PHY_MAX_COUNT

enum {
    PAD_GPIO_A      = (0 * 32),
    PAD_GPIO_B      = (1 * 32),
    PAD_GPIO_C      = (2 * 32),
    PAD_GPIO_D      = (3 * 32),
    PAD_GPIO_E      = (4 * 32),
    PAD_GPIO_ALV    = (5 * 32),
};


#define IRQ_GPIO_A_START		(IRQ_GPIO_START + PAD_GPIO_A)
#define IRQ_GPIO_B_START		(IRQ_GPIO_START + PAD_GPIO_B)
#define IRQ_GPIO_C_START		(IRQ_GPIO_START + PAD_GPIO_C)
#define IRQ_GPIO_D_START		(IRQ_GPIO_START + PAD_GPIO_D)
#define IRQ_GPIO_E_START		(IRQ_GPIO_START + PAD_GPIO_E)

分析:
S5P6818一共32*5=160个GPIO。每个GPIO都可以作为外部中断使用:

按键 GPIO口 GPIO口号 中断号
K2 —> GPIOA28 ---->PAD_GPIO_A+28 ----->IRQ_GPIO_A_START + 28
K3 —> GPIOB30 ---->PAD_GPIO_B+30 ----->IRQ_GPIO_B_START + 30
K4 —> GPIOB31 ---->PAD_GPIO_B+31 ----->IRQ_GPIO_B_START + 31
K6 —> GPIOB9 ---->PAD_GPIO_B+9 ----->IRQ_GPIO_B_START + 9

头文件:
#include <cfg_type.h>

三、中断的标志

注意:中断一安装成功(request_irq()),中断默认是打开的。

includelinuxinterrupt.h

1. 外部中断的中断标志

#define IRQF_TRIGGER_RISING	0x00000001
#define IRQF_TRIGGER_FALLING	0x00000002
#define IRQF_TRIGGER_HIGH	0x00000004
#define IRQF_TRIGGER_LOW	0x00000008

双边沿触发:
IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING

2. 普通中断

// IRQF_DISABLED - keep irqs disabled when calling the action handler.
#define IRQF_DISABLED		0x00000020

当响应当前中断的时候,关闭其他的中断

// IRQF_SHARED - allow sharing the irq among several devices
#define IRQF_SHARED		0x00000080

当一个中断源,注册多次,也就是一个中断源对应多个中断服务程序时

// IRQF_TIMER - Flag to mark this interrupt as timer interrupt
#define __IRQF_TIMER		0x00000200

当注册定时器的中断时,使用的标志。

四、中断源对应的中断服务程序

通常情况下,一个中断源对应一个中断服务程序。
也可以多个中断源对应一个中断服务程序,但是在中断服务程序中,需要区分中断源。

1. 使用中断号来区分中断源

2. 使用申请中断的时候,传递给中断服务程序的参数来区分中断源

五、中断服务程序与原子上下文

#include <linux/delay.h>

/**
 * msleep - sleep safely even with waitqueue interruptions
 * @msecs: Time in milliseconds to sleep for
 */
void msleep(unsigned int msecs)

// 内核中的秒级睡眠
void ssleep(unsigned int seconds); 

1. 中断服务程序是一个原子过程

例:

irqreturn_t key2_isr(int irq, void *dev)
{
	printk("key2 is pressing, irq = %dn",irq);
	ssleep(1); //阻塞--->调度--->出错
	printk("after irqn");
	return 	IRQ_HANDLED;
}

2. 出错的信息:

[root@GEC6818 /test]#insmod key_drv.ko 
[   14.663000] gec6818 key driver init 
[root@GEC6818 /test]#[   17.544000] key2 is pressing, irq = 134   ---->已经进入到了中断服务程序
[   17.545000] BUG: scheduling while atomic: swapper/3/0/0x00010002   ----->出错,在一个原子过程中,进行进程的调度。
[   17.548000] Modules linked in: key_drv(O)
[   17.553000] [<c001517c>] (unwind_backtrace+0x0/0x134) from [<c0731340>] (__schedule+0x720/0x7d4)
[   17.561000] [<c0731340>] (__schedule+0x720/0x7d4) from [<c072f564>] (schedule_timeout+0x198/0x324)
[   17.570000] [<c072f564>] (schedule_timeout+0x198/0x324) from [<c0052e80>] (msleep+0x2c/0x38)
[   17.579000] [<c0052e80>] (msleep+0x2c/0x38) from [<bf00001c>] (key2_isr+0x1c/0x30 [key_drv])
[   17.587000] [<bf00001c>] (key2_isr+0x1c/0x30 [key_drv]) from [<c00b7344>] (handle_irq_event_percpu+0x80/0x284)
[   17.597000] [<c00b7344>] (handle_irq_event_percpu+0x80/0x284) from [<c00b7584>] (handle_irq_event+0x3c/0x5c)
[   17.607000] [<c00b7584>] (handle_irq_event+0x3c/0x5c) from [<c00b9b70>] (handle_level_irq+0xb4/0x108)
[   17.616000] [<c00b9b70>] (handle_level_irq+0xb4/0x108) from [<c001ef1c>] (gpio_handler+0x9c/0x17c)
[   17.625000] [<c001ef1c>] (gpio_handler+0x9c/0x17c) from [<c00b6b5c>] (generic_handle_irq+0x28/0x38)
[   17.634000] [<c00b6b5c>] (generic_handle_irq+0x28/0x38) from [<c000f4e4>] (handle_IRQ+0x58/0xb0)
[   17.643000] [<c000f4e4>] (handle_IRQ+0x58/0xb0) from [<c000854c>] (gic_handle_irq+0x28/0x58)
[   17.651000] [<c000854c>] (gic_handle_irq+0x28/0x58) from [<c000e840>] (__irq_svc+0x40/0x70)
[   17.660000] Exception stack(0xe30a9f88 to 0xe30a9fd0)
[   17.665000] 9f80:                   ffffffed 00000003 00000000 00000000 e30a8000 c0b298c8
[   17.673000] 9fa0: c0737290 e30a8000 e30a8000 c0aae128 00000000 00000000 00000019 e30a9fd0
[   17.681000] 9fc0: c000f83c c000f840 600b0013 ffffffff


3. 原子过程

**原子过程:**这个过程是一个连续的过程,该过程不能产生阻塞,不能被打断。

中断服务程序的执行过程就是一个原子过程,中断服务程序执行的过程中,使用的就是原子上下文。

**上下文:**程序执行过程中,使用的环境:代码、stack、heap

哪些函数可能会产生阻塞:
ssleep()
copy_to_user()
copy_from_user()
获取信号量的函数。

不能把中断服务程序挂起。

六、等待队列

设置一个等待的条件,条件满足,进程就继续向下执行;条件不满足,进程就阻塞在等待队列上。当条件满足后,中断会唤醒等待队列中的进程,进程再继续向下执行。

等待队列也是一种同步的方法。

1、定义一个等待的条件

static int key_press_flag = 0;

2、定义一个等待队列

static wait_queue_head_t 6818_key_wq;

3、初始化队列头

void init_waitqueue_head(wait_queue_head_t *q)

4、进程访问等待队列

void wait_event(wait_queue_head_t q, int condition) 
void wait_event_interruptible(wait_queue_head_t q, int condition)	

参数说明:
wait_queue_head_t *q —>定义并初始化好的等待队列
int condition—>等待条件

注意:
进程访问等待队列的时候,判断等待条件,条件满足,进程继续执行,条件不满足,进程就产生阻塞,进入睡眠状态,睡眠状态有两种。

1)进程进入睡眠的两种不同的状态:

**可中断睡眠:**进程在睡眠状态的时候,给该进程发信号,这个进程可以接收信号,并立即处理信号。
**不可中断睡眠:**进程在睡眠状态的时候,给该进程发信号,这个进程可以接收信号,但是不会立即响应这个信号,当进程进入执行状态,再去处理这个信号。

2)如何查看进程的状态:
[root@GEC6818 /]#top

Mem: 21092K used, 806356K free, 0K shrd, 892K buff, 2628K cached
CPU:  0.0% usr  1.1% sys  0.0% nic 98.8% idle  0.0% io  0.0% irq  0.0% sirq
Load average: 0.00 0.00 0.00 1/85 138
  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND
  138   125 root     R     3336  0.4   1  1.1 top
    1     0 root     S     3336  0.4   2  0.0 init
  125     1 root     S     3336  0.4   2  0.0 -/bin/sh
   44     2 root     SW       0  0.0   0  0.0 [kworker/0:1]
  102     2 root     SW       0  0.0   4  0.0 [mmcqd/0]
    2     0 root     SW       0  0.0   0  0.0 [kthreadd]
    3     2 root     SW       0  0.0   0  0.0 [ksoftirqd/0]
    4     2 root     SW       0  0.0   0  0.0 [kworker/0:0]
    5     2 root     SW       0  0.0   3  0.0 [kworker/u:0]
    6     2 root     SW       0  0.0   0  0.0 [migration/0]
    7     2 root     SW       0  0.0   0  0.0 [watchdog/0]
    8     2 root     SW       0  0.0   1  0.0 [migration/1]
    9     2 root     SW       0  0.0   1  0.0 [kworker/1:0]
   10     2 root     SW       0  0.0   1  0.0 [ksoftirqd/1]
   11     2 root     SW       0  0.0   1  0.0 [watchdog/1]
   12     2 root     SW       0  0.0   2  0.0 [migration/2]
   13     2 root     SW       0  0.0   2  0.0 [kworker/2:0]
   14     2 root     SW       0  0.0   2  0.0 [ksoftirqd/2]
   15     2 root     SW       0  0.0   2  0.0 [watchdog/2]
   16     2 root     SW       0  0.0   3  0.0 [migration/3]

可中断睡眠 — S; 不可中断睡眠 — D。

3)如何想一个进程发信号

#kill 信号 PID
#killall 信号 进程的名字

4)有哪些信号
[root@GEC6818 /]#kill -l
 1) HUP
 2) INT
 3) QUIT
 4) ILL
 5) TRAP
 6) ABRT
 7) BUS
 8) FPE
 9) KILL
10) USR1
11) SEGV
12) USR2
13) PIPE
14) ALRM
15) TERM
16) STKFLT
17) CHLD
18) CONT
19) STOP
20) TSTP
21) TTIN
22) TTOU
23) URG
24) XCPU
25) XFSZ
26) VTALRM
27) PROF
28) WINCH
29) POLL
30) PWR
31) SYS
32) RTMIN
64) RTMAX
5、唤醒等待队列的进程
void wake_up(wait_queue_head_t *q)

七、源码

例程

最后

以上就是甜美刺猬为你收集整理的Linux interrupt一、linux中中断的实现二、中断号三、中断的标志四、中断源对应的中断服务程序五、中断服务程序与原子上下文六、等待队列七、源码的全部内容,希望文章能够帮你解决Linux interrupt一、linux中中断的实现二、中断号三、中断的标志四、中断源对应的中断服务程序五、中断服务程序与原子上下文六、等待队列七、源码所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(56)

评论列表共有 0 条评论

立即
投稿
返回
顶部