3.5.5. Linux内核API之中断机制
3.5.5.1. __tasklet_hi_schedule
__tasklet_hi_schedule
函数功能描述:其主要作用是将参数t代表的软中断的描述符添加到向量tasklet_hi_vec的尾部,等待获得CPU资源,
被调度执行.tasklet_hi_vec代表高优先级的软中断描述符链表.通过此函数添加的软中断具有较高的优先级,会先被调度执行.
#include <linux/interrupt.h>
//源码位置kernel/softirp.c
void __tasklet_hi_schedule(struct tasklet_struct *t)
函数输入参数是struct tasklet_struct结构体类型的指针,保存软中断的描述符信息,其定义见文件include/linux/interrupt.h
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
}
next: 指向链表的下一个元素
state: 定义来当前软中断的状态,内核中只使用了bit1和bit0两个状态位.bit1表示当前tasklet正在执行,它仅对SMP系统有意义.bit0=1表示当前tasklet已经被调度, 等待获得CPU资源执行
enum
{
TASKLET_STATE_SCHED, /*软中断被调度,但未执行*/
TASKLET_STATE_RUN /*软中断正在执行*/
}
count: 原子计数值,代表当前tasklet的引用计数值,只有当count等于0时,tasklet对应的中断处理函数才能被执行
func: 函数指针,代表中断的处理函数
data: 中断处理函数执行时的参数,即字段func执行时的参数
测试代码
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/init.h>
static unsigned long data = 0x00;
static struct tasklet_struct tasklet, tasklet_1;
static void irq_tasklet_action(unsigned long data)
{
printk("in irq_tasklet_action the state of tasklet is : %ld\n", (&tasklet)->state);
printk("tasklet running.\n");
return;
}
static void irq_tasklet_action1(unsigned long data)
{
printk("in irq_tasklet_action1 the state of tasklet is : %ld\n", (&tasklet1)->state);
printk("tasklet1 running.\n");
return;
}
static int __init __tasklet_hi_schedule_init(void)
{
tasklet_init(&tasklet, irq_tasklet_action, data);
tasklet_init(&tasklet1, irq_tasklet_action1, data);
printk("in irq_tasklet_action the state of tasklet is : %ld\n", (&tasklet)->state);
printk("in irq_tasklet_action1 the state of tasklet is : %ld\n", (&tasklet1)->state);
tasklet_schedule(&tasklet); //将中断送入普通中断队列
if(!test_and_set_bit(TASKLET_STATE_SCHED, &tasklet1.state))
__tasklet_hi_schedule(&tasklet1); //将中断送入高优先级队列
tasklet_kill(&tasklet);
tasklet_kill(&tasklet1);
return 0;
}
static void __exit __tasklet_hi_schedule_exit(void)
{
return;
}
3.5.5.2. disable_irq
disable_irq
:此函数在实现过程中先后调用了 disable_irq_nosync
完成增加中断所处的深度和改变中断的状态,然后调用 synchronize_irq
使处理器处于
检测中断号所对应的中断状态
#include <linux/interrupt.h>
//源码位置kernel/irq/manage.c
void disable_irq(unsigned int irq);
void disable_irq_nosync(unsigned int irq);
void enable_irq(unsigned int irq);
测试代码
#include <linux/interrupt.h>
#include <linux/module.h>
static int ieq = 3;
static irqreturn_t irq_handler(int data, void *dev_id)
{
printk("the data is: %d\n", data);
return IRQ_NONE;
}
static int __init disable_irq_nosync_init(void)
{
int ret;
ret = request_irq(irq, irq_handler, IRQF_DISABLE, "A_New_Device", NULL); //申请一个新的中断
disable_irq_nosync(irq); //使中断的深度增加1
enable_irq(irq); //使中断的深度减少1,同时触发中断处理函数执行
printk("the result of rquest_irq is : %d\n", result);
return 0;
}
static void __exit disable_irq_nosync_exit(void)
{
free_irq(irq, NULL);
return;
}
module_init(disable_irq_nosync_init);
module_exit(disable_irq_nosync_exit);
3.5.5.3. irq_set_chip
irq_set_chip
:此函数是为irq_desc数组中对应下标为irq的元素设定irq_chip的值,如果传入的参数为NULL,则使用系统定义好的no_irq_chip为它赋值.
#include <linux/irq.h>
//代码位置kernel/irq/chip.c
int irq_set_chip(unsigned int irq, struct irq_chip *chip)
参数chip是一个struct irq_chip型的结构体变量,是对应的硬件中断的描述符的irq_chip字段的值,定义见include/linux/irq.h中
struct irq_chip {
const char *name;
unsigned int (*irq_startup)(struct irq_data *data);
void (*irq_shutdown)(struct irq_data *data);
void (*irq_enable)(struct irq_data *data);
void (*irq_disable)(struct irq_data *data);
void (*irq_ack)(struct irq_data *data);
void (*irq_mask)(struct irq_data *data);
void (*irq_mask_ack)(struct irq_data *data);
void (*irq_unmask)(struct irq_data *data);
void (*irq_eoi)(struct irq_data *data);
void (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
void (*irq_retrigger)(struct irq_data *data);
void (*irq_set_type)(struct irq_data *data, unsigned int flow_type);
void (*irq_set_wake)(struct irq_data *data, unsigned int on);
void (*irq_bus_lock)(struct irq_data *data);
void (*irq_bus_sync_unlock)(struct irq_data *data);
void (*irq_cpu_online)(struct irq_data *data);
void (*irq_cpu_offline)(struct irq_data *data);
void (*irq_suspend)(struct irq_data *data);
void (*irq_resume)(struct irq_data *data);
void (*irq_pm_shutdown)(struct irq_data *data);
void (*irq_calc_mask)(struct irq_data *data);
void (*irq_print_chip)(struct irq_data *data, struct seq_file *p);
void (*irq_request_resources)(struct irq_data *data);
void (*irq_release_resources)(struct irq_data *data);
void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);
ungsigned long flags;
};
如果传入的参数chip为NULL,则系统用no_irq_chip进行初始化,no_irq_chip的定义见kernel/irq/dummychip.c
struct irq_chip no_irq_chip = {
.name = "none",
.irq_startup = noop_ret,
.irq_shutdown = noop,
.irq_enable = noop,
.irq_disable = noop,
.irq_ack = ack_bad,
};
测试代码
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/module.h>
static int irq = 4;
static irqreturn_t irq_handler(int irq, void *dev_id)
{
printk("the irq is : %d\n", irq);
return IRQ_WAKE_THREAD;
}
static irqreturn_t irq_thread_fn(int irq, void *dev_id)
{
printk("the irq is : %d\n", irq);
return IRQ_HANDLED;
}
static int __init irq_set_chip_init(void)
{
request_threaded_irq(irq, irq_handler, irq_thread_fn, IRQF_DISABLED, "A_New_Device", NULL);
irq_set_chip(irq, NULL);
return 0;
}
static void __exit irq_set_chip_exit(void)
{
free_irq(irq, NULL);
return;
}
module_init(irq_set_chip_init);
module_exit(irq_set_chip_exit);
3.5.5.4. irq_set_chip_data
#include <linux/irq.h>
//源码位置kernel/irq/chip.c
int irq_set_chip_data(unsigned int irq, void *data)
irq: 设备对应的中断号,对应数组irq_desc中元素的下标
data: 为irq_desc数组中元素的chip字段中的函数体哦嗯一个私有的数据区,以实现chip字段中函数的共享执行
#include <linux/irq.h>
//源码位置kernel/irq/chip.c
int irq_set_irq_type(unsigned int irq, unsigned int type)
中断触发类型 |
描述 |
IRQ_TYPE_NOE |
系统默认,没有明确指明类型的触发模式 |
IRQ_TYPE_EDGE_RISING |
上升沿触发 |
IRQ_TYPE_EDGE_FAILLING |
下降沿触发 |
IRQ_TYPE_EDGE_BOTH |
上升沿或下降沿触发 |
IRQ_TYPE_LEVEL_HIGH |
高电平触发 |
IRQ_TYPE_LEVEL_LOW |
低电平触发 |
IRQ_TYPE_SENSE_MASK |
以上任何一种方式触发 |
IRQ_TYPE_DEFAULT |
IRQ_TYPPE_SENSE_MASK以上任何一种方式 |
测试代码
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/module.h>
static int irq = 10;
static irqreturn_t irq_handler(int irq, void *dev_id)
{
printk("the irq is : %d\n", irq);
return IRQ_WAKE_THREAD;
}
static irqreturn_t irq_thread_fn(int irq, void *dev_id)
{
printk("the irq is : %d\n", irq);
return IRQ_HANDLED;
}
struct chip_data
{
int num;
char *name;
int flags;
};
static int __init irq_set_chip_data_init(void)
{
struct chip_data data;
request_threaded_irq(irq, irq_handler, irq_thread_fn, IRQF_DISABLED, "A_New_Device", NULL);
irq_set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);
irq_set_chip(irq, NULL);
irq_set_chip_data(irq, &data);
return 0;
}
static void __exit irq_set_chip_data_exit(void)
{
free(irq, NULL);
return;
}
module_init(irq_set_chip_data_init);
module_exit(irq_set_chip_data_exit);
3.5.5.5. irq_set_irq_wake
irq_set_irq_wake
:此函数用于改变中断的状态及中断的唤醒深度,其对中断状态及中断唤醒深度的影响根据参数on不同会有不同的结果.
如果on为0,函数使中断处于睡眠状态,不能被唤醒,减少中断唤醒深度wake_depth的值,如果on的值为非0,函数将中断从睡眠状态唤醒,使中断处于唤醒状态,
增加其唤醒深度wake_depth的值
#include <linux/interrupt.h>
//源码位置kernel/irq/manage.c
int irq_set_irq_wake(unsigned int irq, unsigned int on)
测试代码
#include <linux/module.h>
#include <linux/interrupt.h>
static int irq = 3;
static irqreturn_t irq_handler(int data, void *data_id)
{
printk("the data is : %d\n", data);
return IRQ_NONE;
}
static int __init irq_set_irq_wake_init(void)
{
request_irq(irq, irq_handler, IRQF_DISABLED, "A_New_Device", NULL);
irq_set_irq_wake(irq, 0); //使中断处于睡眠状态,减少唤醒深度
irq_set_irq_wake(irq, 1); //使中断处于唤醒状态,增加唤醒深度
return 0;
}
static void __exit irq_set_irq_wake_exit(void)
{
free_irq(irq, NULL);
return;
}
module_init(irq_set_irq_wake_init);
module_exit(irq_set_irq_wake_exit);
3.5.5.6. tasklet_init
tasklet_init
: 用于初始化一个struct tasklet_struct结构体类型的变量,将其state字段及其count字段的值清零,并完成func及data的赋值
#include <linux/interrupt.h>
//内核源码kernel/softirq.c
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data)
tasklet_enable
:用于减少结构体tasklet_struct中字段count的值,当此字段的值等于0时,相应的软中断被重新使能,对应的中断处理函数能够
被CPU调度执行,处理相应的中断
#include <linux/interrupt.h>
//内核源码include/linux/interrupt.h
static inline void tasklet_enable(struct tasklet_struct *t)
{
smp_mb_befor_atomic();
atomic_dec(&t->count);
}
tasklet_disable
:用于增加软中断描述符中count字段的值,使软中断处于睡眠状态,不能响应对应的中断
#include <linux/interrupt.h>
//内核源码include/linux/interrupt.h
static inline void tasklet_disable(struct tasklet_struct *t)
{
tasklet_disable_nosync(t);
tasklet_unlock_wait(t);
smp_mb();
}
static inline void tasklet_disable_nosync(struct tasklet_struct *t)
{
atomic_inc(&t->count);
smp_mb_after_atomic_inc();
}
tasklet_kill
:用于阻塞当前线程,等待中断处理函数的执行完毕.此函数通过循环检测中断字段state的值,判断中断处理函数的执行情况,
当中断处理函数执行完毕之后,循环结束,然后将字段state清零
#include <linux/interrupt.h>
//内核源码kernel/softirq.c
void tasklet_kill(struct tasklet_struct *t)
tasklet_trylock
:返回0表示此中断不可在此CPU上调度
#include <linux/interrupt.h>
//内核源码include/linux/interrupt.h
static inline int tasklet_trylock(struct tasklet_struct *t)
{
return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state);
}
测试代码
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/init.h>
static unsigned long data = 10;
static struct tasklet_struct tasklet;
static void irq_tasklet_action(unsigned long data)
{
printk("the data value of tasklet is : %ld\n", (&tasklet)->data);
return;
}
static int __init tasklet_init_init(void)
{
if(tasklet.func == NULL) {
printk("the tasklet has not been initialized!\n");
}
tasklet_init(&tasklet, irq_tasklet_action, data); //初始化一个struct tasklet_struct变量
printk("the data value of the tasklet is : %ld\n", tasklet.data);
if(tasklet.func == NULL) {
printk("the tasklet has not been initialized!\n");
} else {
printk("the tasklet has been initialized!\n");
}
tasklet_schedule(&tasklet); //把软中断放入调度队列,等待调度执行
printk("the count value of the tasklet befor tasklet_disable is : %d\n", tasklet.count);
tasklet_disable(&tasklet); //调用tasklet_disable使tasklet对应的处理函数不能执行
if(atomic_read(&(tasklet.count)) != 0) //测试当前count值
printk("tasklet is disabled.\n");
printk("the count value of the tasklet after tasklet_disable is : %d\n", tasklet.count);
tasklet_enable(&tasklet); //使能tasklet
if(atomic_read(&(tasklet.count)) == 0)
printk("tasklet is enabled.\n");
tasklet_kill(&tasklet); //等待tasklet被调度执行完毕
return 0;
}
static void __exit tasklet_init_exit(void)
{
return;
}
module_init(tasklet_init_init);
module_exit(tasklet_init_exit);
3.5.5.7. request_irq
request_irq
:动态申请注册一个中断
#include <linux/interrupt.h>
//内核源码include/linux/interrupt.h
static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags, const char *name, void *dev)
{
return request_thread_irq(irq, handler, NULL, flags, name, dev);
}
int __must_check request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thrad_fn,
unsigned long flags, const char *name, void *dev);
irq: 为对应的中断号,系统已用的是0~31,其中IRQ9, IRQ10, IRQ15系统保留,32~16640用于用户定义的中断
handler: 是对应的中断处理函数,返回值类型是irq_handler_t
enum irqreturn {
IRQ_NONE = (0 << 0), //中断不是此设备发出
IRQ_HANDLED = (1 << 0), //中断被此设备处理
IRQ_WAKE_THREAD = (1 << 1), //中断处理函数需要唤醒中断处理线程
thread_fn: 对应的中断线程处理函数,如果中断处理函数的返回值是IRQ_WAKE_THREAD,则此时的中断线程处理函数将被调用,此函数是对中断处理函数的补充
flags: 用来标识中断的类型
#define IRQF_DISABLED 0x00000020 //中断失能
#define IRQF_SHARED 0x00000080 //设备共享
#define IRQF_PROBE_SHARED 0x00000100 //错序共享中断
#define __IRQF_TIMER 0x00000200 //时钟中断
#define IRQF_PERCPU 0x00000400 //CPU中断
#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 //提起恢复IRQ而不是在设备恢复期间
#define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD)
remove_irq
:此函数用于卸载IRQ链表中的与输入参数对应的irqaction描述符
#include <linux/irq.h>
//内核源码kernel/irq/manage.c
void remove_irq(unsigned int irq, struct irqcation *act);
irq: 中断号
act: 参数act是与系统对应的一个irqaction标识符
struct irqaction {
irq_hander_t handler; //中断处理函数
void *dev_id; //设备标识符,用于设别设备
void __percpu *percpu_dev_id; //设备标识符,用于识别设备
struct irqaction *next; //指向中断向量链表中的下一个中断标识符
irq_handkler_t thread_fn; //中断线程处理函数
struct task_struct *thread; //任务描述符,指向与此中断线程对应的线程
unsigned int irq; //中断号
unsigned int flags; //中断类型
unsigned long thread_flags; //线程标识
unsigned long thread_mask; //CPU掩码,表示此中断所在的CPU编号
const char *name; //中断标识符对应的设备名
struct proc_dir_entry *dir; //目录入口指针,指向在文件夹/proc/irq中与此中断标识符对应的中断号相应的文件夹
} ___cacheline_internodealinged_in_smp;