中断深入-->中断下半部线程化(工作队列+ threaded irq)

在此之前

前定时器、下半部 tasklet,它们都是在中断上下文中执行,它们无法休眠。
所以复杂的事情,就不能放在下半部,不然系统会很卡
如果使用线程来处理这些耗时的工作,那就可以解决系统卡顿的问题:因为线程可以休眠

不自己创建线程 —> 工作队列

在内核中,我们并不需要自己去创建线程,可以使用“工作队列”(workqueue)。内核初始化工作队列
是,就为它创建了内核线程。以后我们要使用“工作队列”,只需要把“工作”放入“工作队列中”,对应的
内核线程就会取出“工作”,执行里面的函数。
工作队列的应用场合:要做的事情比较耗时,甚至可能需要休眠,那么可以使用工作队列。

缺点
多个工作(函数)是在某个内核线程中依序执行的,前面函数执行很慢,就会影响到后面的函数。
在多 CPU 的系统下,一个工作队列可以有多个内核线程,可以在一定程度上缓解这个问题。

自己创建线程 —> threaded irq

工作队列用起来挺简单,但是它有一个
缺点:工作队列中有多个 work,前一个 work 没处理完会影响后面的 work。解决方法有很多种,比如干脆自
己创建一个内核线程,不跟别的 work 凑在一块了。在 Linux 系统中,对于存储设备比如 SD/TF 卡,它的驱
动程序就是这样做的,它有自己的内核线程。
对于中断处理,还有另一种方法:threaded irq,线程化的中断处理。中断的处理仍然可以认为分为上
半部、下半部。上半部用来处理紧急的事情,下半部用一个内核线程来处理,这个内核线程专用于这个中断。
在这里插入图片描述
你可以只提供 thread_fn,系统会为这个函数创建一个内核线程。发生中断时,系统会立刻调用 handler
函数,然后唤醒某个内核线程,内核线程再来执行 thread_fn 函数。

顺带讲解一个函数 container_of()

有一个结构体内部变量的地址,用这个函数能推测出结构体的地址(自己算也可以)
在这里插入图片描述
比如知道参数 button_work的地址 我们就能算出gpio_key的地址
得到gpio_key这个结构体

工作队列

驱动中如何使用

①构造 work_struct 结构体
在这里插入图片描述
从这个定义函数我们知道,构造结构体,先要传入一个work结构体,和他要执行的方法 func

struct gpio_key{
   
	int gpio;
	struct gpio_desc *gpiod;
	int flag;
	int irq;
	struct tasklet_struct button_tasklet;
	struct work_struct	button_work;
} ;

INIT_WORK(&gpio_keys_100ask[i].button_work,button_tasklet);

② 把这个 work_struct 结构体放入工作队列,内核线程就会运行 work 中的函数。

schedule_work( &(gpio_key->button_work) );

③其他函数
在这里插入图片描述

驱动代码
#include <linux/module.h>
#include <linux/poll.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <asm/current.h>


struct gpio_key{
   
	int gpio;
	struct gpio_desc *gpiod;
	int flag;
	int irq;
	struct timer_list key_timer;
	struct tasklet_struct tasklet;
	struct work_struct work;
} ;

static struct gpio_key *gpio_keys_100ask;

/* 主设备号                                                                 */
static int major = 0;
static struct class *gpio_key_class;

/* 环形缓冲区 */
#define BUF_LEN 128
static int g_keys[BUF_LEN];
static int r, w;

struct fasync_struct *button_fasync;

#define NEXT_POS(x) ((x+1) % BUF_LEN)

static int is_key_buf_empty(void)
{
   
	return (r == w);
}

static int is_key_buf_full(void)
{
   
	return (r == NEXT_POS(w));
}

static void put_key(int key)
{
   
	if (!is_key_buf_full())
	{
   
		g_keys[w] = key;
		w = NEXT_POS(w);
	}
}

static int get_key(void)
{
   
	int key = 0;
	if (!is_key_buf_empty())
	{
   
		key = g_keys[r];
		r = NEXT_POS(r);
	}
	return key;
}


static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);

static void key_timer_expire(unsigned long data)
{
   
	/* data ==> gpio */
	struct gpio_key *gpio_key = data;
	int val;
	int key;

	val = gpiod_get_value(gpio_key->gpiod);


	printk("key_timer_expire key %d %d\n", gpio_key->gpio, val);
	key = (gpio_key->gpio << 8) | val;
	put_key(key);
	wake_up_interruptible(&gpio_key_wait);
	kill_fasync(&button_fasync, SIGIO, POLL_IN);
}

static void key_tasklet_func(unsigned long data)
{
   
	/* data ==> gpio */
	struct gpio_key *gpio_key = data;
	int val;
	int key;

	val = gpiod_get_value(gpio_key->gpiod);


	printk("key_tasklet_func key %d %d\n", gpio_key->gpio, val);
}

static void key_work_func(struct work_struct *work)
{
   
	struct gpio_key *gpio_key = container_of(work, struct gpio_key, work);
	int val;

	val = gpiod_get_value(gpio_key->gpiod);

	printk("key_work_func: the process is %s pid %d\n",current->comm, current->pid);	
	printk("key_work_func key %d %d\n", gpio_key->gpio, val);
}
Linux内核编程中,软中断(Softirqs)和工作队列(Workqueues)是两种常见的异步执行机制,它们用于处理需要延迟执行的任务,但它们的使用场景和特性有所不同。 软中断: 1. 软中断是一种轻量级的上下文,用于延迟处理硬件中断服务程序中的非紧急工作。 2. 软中断中断上下文之外执行,这意味着它们不会阻塞硬件中断。 3. 软中断是固定优先级的,并且是在中断上下文中执行的,因此它们必须非常迅速和高效。 4. 软中断适合处理时间敏感的任务,如网络数据包的接收和发送处理。 工作队列: 1. 工作队列工作推送到内核线程中执行,因此它们运行在进程上下文中。 2. 工作队列可以阻塞,适合执行那些需要睡眠、等待或需要更长执行时间的任务。 3. 工作队列提供了更多灵活性,因为它们可以被不同的内核线程处理,包括它们自己的工作队列线程。 4. 工作队列适用于不能或不应该在中断上下文中执行的耗时任务,例如磁盘I/O操作。 使用软中断工作队列的决定因素包括: - 任务的紧急程度:需要快速执行且不会阻塞的任务应该使用软中断。 - 执行时间长度:耗时较长或者需要睡眠的任务应该使用工作队列。 - 是否需要睡眠:如果任务中需要睡眠或者等待,应该使用工作队列,因为软中断中不允许睡眠。 - 系统资源:如果系统资源紧张,应该更加谨慎地使用软中断,因为它们是在中断上下文中执行的。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值