中断上半部与下半部机制

一、上半部与下半部

起源:

  1. 中断处理程序执行时间过长引起的问题
  2. 有些设备的中断处理程序必须要处理一些耗时操作

二、下半部机制之tasklet ---- 基于软中断

6.1 结构体

struct tasklet_struct

{

​ struct tasklet_struct *next;

​ unsigned long state;

​ atomic_t count;

​ void (*func)(unsigned long);

​ unsigned long data;

};

6.2 定义tasklet的中断底半部处理函数

void tasklet_func(unsigned long data);

6.3 初始化tasklet

DECLARE_TASKLET(name, func, data);
/*
定义变量并初始化
参数:name:中断底半部tasklet的名称
	 Func:中断底半部处理函数的名字
	 data:给中断底半部处理函数传递的参数
*/
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data)

6.4 调度tasklet

void tasklet_schedule(struct tasklet_struct *t)
//参数:t:tasklet的结构体

三、按键驱动之tasklet版

按键驱动没有必要分上半部分和下半部分,因为按键的触发时间很短
下面是在key2按键驱动的改动
key2.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <asm/ioctl.h>

#include "fs4412_key.h"

int major = 11;					//主设备号
int minor = 0;					//次设备号
int fs4412key2_num = 1;				//设备号数量

struct fs4412key2_dev 
{
	struct cdev mydev;
	
	int gpio;
	int irqno;

	struct keyvalue data;
	int newflag;
	spinlock_t lock;

	wait_queue_head_t rq;

	/* 定义下半部分 tasklet结构体 */
	struct tasklet_struct tsk;
};
struct fs4412key2_dev *pgmydev = NULL;

int fs4412key2_open (struct inode *pnode, struct file *pfile)//打开设备
{
	return 0;
}

int fs4412key2_close(struct inode *pnode, struct file *pfile)//关闭设备
{
	return 0;
}

ssize_t fs4412key2_read (struct file *pfile, char __user *puser, size_t count, loff_t *p_pos) {

	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
	int size = 0;
	int ret = 0;

	if(count < sizeof(struct keyvalue)) {
		printk("expect read size is invalid\n");
		return -1;
	}

	spin_lock(&pmydev->lock);

	/* 判断是否有数据可读 */
	if(!pmydev->newflag) {

		if(pfile->f_flags & O_NONBLOCK) { //非阻塞
			spin_unlock(&pmydev->lock);
			printk("O_NONBLOCK Not Data Read\n");
			return -1;

		} else { //阻塞

			spin_unlock(&pmydev->lock);
			/* 睡眠 当curlen>0 时返回 */
			ret = wait_event_interruptible(pmydev->rq, pmydev->newflag == 1);
			if(ret) {
				printk("Wake up by signal\n");
				return -ERESTARTSYS;
			}
		}
		spin_lock(&pmydev->lock);
	}

	// 确定要读取的数据长度,如果请求大于设备当前数据长度,则读取全部可用数据
	if (count > sizeof(struct keyvalue)) {
		size = sizeof(struct keyvalue);
	}
	else {
		size = count;
	}

	// 将设备数据复制到用户空间缓冲区
	ret = copy_to_user(puser, &pmydev->data, size);
	if(ret) {
		spin_unlock(&pmydev->lock);
		printk("copy_to_user failed\n");
		return -1;
	}

	pmydev->newflag = 0;

	spin_unlock(&pmydev->lock);

	// 返回实际读取的字节数
	return size;
}

unsigned int fs4412key2_poll(struct file *pfile, poll_table *ptb) {

	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
	unsigned int mask = 0;

	poll_wait(pfile, &pmydev->rq, ptb);

	spin_lock(&pmydev->lock);
	if(pmydev->newflag) {
		mask |= POLLIN | POLLRDNORM;
	}
	spin_unlock(&pmydev->lock);

	return mask;
}

struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = fs4412key2_open,
	.release = fs4412key2_close,
	.read = fs4412key2_read,
	.poll = fs4412key2_poll,
};

/* 中断底半部处理函数 */
irqreturn_t key2_irq_handle(int no,void *arg) {

	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
	
	/* 调度tasklet */
	tasklet_schedule(&pmydev->tsk);

	return IRQ_HANDLED;
}

void bottom_irq_func(unsigned long arg) {
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
	int status1 = 0;
	int status2 = 0;
	int status = 0;

	status1 = gpio_get_value(pmydev->gpio);
	mdelay(1);
	status2 = gpio_get_value(pmydev->gpio);

	if(status1 != status2) {
		return IRQ_NONE;
	}

	status = status1;

	spin_lock(&pmydev->lock);
	if(status == pmydev->data.status) {
		spin_unlock(&pmydev->lock);
		return IRQ_NONE;
	}

	pmydev->data.code = KEY2;
	pmydev->data.status = status;
	pmydev->newflag = 1;

	spin_unlock(&pmydev->lock);
	wake_up(&pmydev->rq);

	return IRQ_HANDLED;
}

int __init fs4412key2_init(void) 
{
	int ret = 0;
	dev_t devno = MKDEV(major, minor);
	struct device_node * pnode = NULL;

	pnode = of_find_node_by_path("/mykey2_node");
	if(NULL == pnode) {
		printk("find node by path failed\n");
		return -1;
	}

	pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev), GFP_KERNEL);
	if(NULL == pgmydev) {
		printk("kmalloc for 'struct fs4412key2_dev' failed\n");
		return -1;
	}

	pgmydev->gpio = of_get_named_gpio(pnode, "key2-gpio", 0);

	pgmydev->irqno = irq_of_parse_and_map(pnode, 0);


	/* 手动申请设备号 */
	ret = register_chrdev_region(devno, fs4412key2_num, "fs4412key2");
	if (ret) {
		/* 动态申请设备号 */
		ret = alloc_chrdev_region(&devno, minor, fs4412key2_num, "fs4412key2");
		if(ret){
			printk("get devno failed\n");
			return -1;
		}
		/*申请成功 更新设备号*/
		major = MAJOR(devno);
	}
	
	
	/* 给struct cdev对象指定操作函数集 */
	cdev_init(&pgmydev->mydev, &myops);

	/* 将struct cdev对象添加到内核对应的数据结构中 */
	pgmydev->mydev.owner = THIS_MODULE;
	cdev_add(&pgmydev->mydev, devno, fs4412key2_num);

	/* 初始化阻塞机制 */
	init_waitqueue_head(&pgmydev->rq);

	/* 初始化tasklet */
	tasklet_init(&pgmydev->tsk,bottom_irq_func,(unsigned long)pgmydev);

	spin_lock_init(&pgmydev->lock);

	ret = request_irq(pgmydev->irqno, key2_irq_handle, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "fs4412key2", pgmydev);
	if(ret) {
		printk("request_irq failed\n");
		cdev_del(&pgmydev->mydev);
		kfree(pgmydev);
		pgmydev = NULL;
		unregister_chrdev_region(devno, fs4412key2_num);
		return -1;
	}
	return 0;
}

void __exit fs4412key2_exit(void) 
{

	dev_t devno = MKDEV(major, minor);
	
	free_irq(pgmydev->irqno, pgmydev);

	/* 从内核中移除一个字符设备 */
	cdev_del(&pgmydev->mydev);

	/* 回收设备号 */
	unregister_chrdev_region(devno, fs4412key2_num);

	kfree(pgmydev);
	pgmydev = NULL;
}

MODULE_LICENSE("GPL");
module_init(fs4412key2_init);
module_exit(fs4412key2_exit);

四、下半部机制之workqueue ----- 基于内核线程

8.1 工作队列结构体:

typedef void (*work_func_t)(struct work_struct *work)

struct work_struct {

​ atomic_long_t data;

​ struct list_head entry;

​ work_func_t func;

#ifdef CONFIG_LOCKDEP

​ struct lockdep_map lockdep_map;

#endif

};

8.2 定义工作队列底半部处理函数

void work_queue_func(struct work_struct *work);

8.3 初始化工作队列

struct work_struct work_queue;

初始化:绑定工作队列及工作队列的底半部处理函数

INIT_WORK(struct work_struct * pwork, _func) ;

参数:pwork:工作队列

​ func:工作队列的底半部处理函数

8.4 工作队列的调度函数

bool schedule_work(struct work_struct *work);

五、按键驱动之workqueue版

按键驱动没有必要分上半部分和下半部分,因为按键的触发时间很短
下面是在key2按键驱动的改动
key2.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <asm/ioctl.h>

#include "fs4412_key.h"

int major = 11;					//主设备号
int minor = 0;					//次设备号
int fs4412key2_num = 1;				//设备号数量

struct fs4412key2_dev 
{
	struct cdev mydev;
	
	int gpio;
	int irqno;

	struct keyvalue data;
	int newflag;
	spinlock_t lock;

	wait_queue_head_t rq;

	/* 定义下半部分 tasklet结构体 */
	// struct tasklet_struct tsk;

	/* 定义下半部分 workqueue结构体 */
	struct work_struct wk;
};
struct fs4412key2_dev *pgmydev = NULL;

int fs4412key2_open (struct inode *pnode, struct file *pfile)//打开设备
{
	return 0;
}

int fs4412key2_close(struct inode *pnode, struct file *pfile)//关闭设备
{
	return 0;
}

ssize_t fs4412key2_read (struct file *pfile, char __user *puser, size_t count, loff_t *p_pos) {

	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
	int size = 0;
	int ret = 0;

	if(count < sizeof(struct keyvalue)) {
		printk("expect read size is invalid\n");
		return -1;
	}

	spin_lock(&pmydev->lock);

	/* 判断是否有数据可读 */
	if(!pmydev->newflag) {

		if(pfile->f_flags & O_NONBLOCK) { //非阻塞
			spin_unlock(&pmydev->lock);
			printk("O_NONBLOCK Not Data Read\n");
			return -1;

		} else { //阻塞

			spin_unlock(&pmydev->lock);
			/* 睡眠 当curlen>0 时返回 */
			ret = wait_event_interruptible(pmydev->rq, pmydev->newflag == 1);
			if(ret) {
				printk("Wake up by signal\n");
				return -ERESTARTSYS;
			}
		}
		spin_lock(&pmydev->lock);
	}

	// 确定要读取的数据长度,如果请求大于设备当前数据长度,则读取全部可用数据
	if (count > sizeof(struct keyvalue)) {
		size = sizeof(struct keyvalue);
	}
	else {
		size = count;
	}

	// 将设备数据复制到用户空间缓冲区
	ret = copy_to_user(puser, &pmydev->data, size);
	if(ret) {
		spin_unlock(&pmydev->lock);
		printk("copy_to_user failed\n");
		return -1;
	}

	pmydev->newflag = 0;

	spin_unlock(&pmydev->lock);

	// 返回实际读取的字节数
	return size;
}

unsigned int fs4412key2_poll(struct file *pfile, poll_table *ptb) {

	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
	unsigned int mask = 0;

	poll_wait(pfile, &pmydev->rq, ptb);

	spin_lock(&pmydev->lock);
	if(pmydev->newflag) {
		mask |= POLLIN | POLLRDNORM;
	}
	spin_unlock(&pmydev->lock);

	return mask;
}

struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = fs4412key2_open,
	.release = fs4412key2_close,
	.read = fs4412key2_read,
	.poll = fs4412key2_poll,
};

/* 中断底半部处理函数 */
irqreturn_t key2_irq_handle(int no,void *arg) {

	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
	
	/* 调度tasklet */
	tasklet_schedule(&pmydev->tsk);
	/* 调度work_struct */
	schedule_work(&pmydev->wk);

	return IRQ_HANDLED;
}

// void bottom_irq_func(unsigned long arg) {
    void bottom_irq_func(struct work_struct *pwk) {
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
	int status1 = 0;
	int status2 = 0;
	int status = 0;

	status1 = gpio_get_value(pmydev->gpio);
	mdelay(1);
	status2 = gpio_get_value(pmydev->gpio);

	if(status1 != status2) {
		return IRQ_NONE;
	}

	status = status1;

	spin_lock(&pmydev->lock);
	if(status == pmydev->data.status) {
		spin_unlock(&pmydev->lock);
		return IRQ_NONE;
	}

	pmydev->data.code = KEY2;
	pmydev->data.status = status;
	pmydev->newflag = 1;

	spin_unlock(&pmydev->lock);
	wake_up(&pmydev->rq);

	return IRQ_HANDLED;
}

int __init fs4412key2_init(void) 
{
	int ret = 0;
	dev_t devno = MKDEV(major, minor);
	struct device_node * pnode = NULL;

	pnode = of_find_node_by_path("/mykey2_node");
	if(NULL == pnode) {
		printk("find node by path failed\n");
		return -1;
	}

	pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev), GFP_KERNEL);
	if(NULL == pgmydev) {
		printk("kmalloc for 'struct fs4412key2_dev' failed\n");
		return -1;
	}

	pgmydev->gpio = of_get_named_gpio(pnode, "key2-gpio", 0);

	pgmydev->irqno = irq_of_parse_and_map(pnode, 0);


	/* 手动申请设备号 */
	ret = register_chrdev_region(devno, fs4412key2_num, "fs4412key2");
	if (ret) {
		/* 动态申请设备号 */
		ret = alloc_chrdev_region(&devno, minor, fs4412key2_num, "fs4412key2");
		if(ret){
			printk("get devno failed\n");
			return -1;
		}
		/*申请成功 更新设备号*/
		major = MAJOR(devno);
	}
	
	
	/* 给struct cdev对象指定操作函数集 */
	cdev_init(&pgmydev->mydev, &myops);

	/* 将struct cdev对象添加到内核对应的数据结构中 */
	pgmydev->mydev.owner = THIS_MODULE;
	cdev_add(&pgmydev->mydev, devno, fs4412key2_num);

	/* 初始化阻塞机制 */
	init_waitqueue_head(&pgmydev->rq);

	/* 初始化tasklet */
	// tasklet_init(&pgmydev->tsk,bottom_irq_func,(unsigned long)pgmydev);

	/* 初始化work_struct */
	INIT_WORK(&pgmydev->wk,bottom_irq_func);

	spin_lock_init(&pgmydev->lock);

	ret = request_irq(pgmydev->irqno, key2_irq_handle, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "fs4412key2", pgmydev);
	if(ret) {
		printk("request_irq failed\n");
		cdev_del(&pgmydev->mydev);
		kfree(pgmydev);
		pgmydev = NULL;
		unregister_chrdev_region(devno, fs4412key2_num);
		return -1;
	}
	return 0;
}

void __exit fs4412key2_exit(void) 
{

	dev_t devno = MKDEV(major, minor);
	
	free_irq(pgmydev->irqno, pgmydev);

	/* 从内核中移除一个字符设备 */
	cdev_del(&pgmydev->mydev);

	/* 回收设备号 */
	unregister_chrdev_region(devno, fs4412key2_num);

	kfree(pgmydev);
	pgmydev = NULL;
}

MODULE_LICENSE("GPL");
module_init(fs4412key2_init);
module_exit(fs4412key2_exit);

六、下半部机制比较

任务机制

​ workqueue ----- 内核线程 能睡眠 运行时间无限制

异常机制 ------- 不能睡眠 下半部执行时间不宜太长( < 1s)

​ 软中断 ---- 接口不方便

​ tasklet ----- 无具体延后时间要求时

​ 定时器 -----有具体延后时间要求时

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值