休眠与唤醒

Linux-休眠与唤醒



前言

当应用程序必须等待某个时间发生,比如必须等待按键被按下时,可以使用“休眠-唤醒”机制,这一机制近似于,你是个早起困难户,想要早起,必须等待闹钟响了,才会起床。


一、“休眠-唤醒”机制

当应用程序调用read等函数读取数据时->应用程序进入内核驱动,调用驱动中对应的函数,若有数据则复制到应用空间,并且立刻返回->但是若没有数据,应用程序就会进入休眠->当有数据的时候,即按下按键,驱动程序中的中断服务函数就会被调用,在这个函数中,会记录数据,同时唤醒应用程序->唤醒应用程序之后,应用程序继续运行内核中对应的代码,也就是驱动中对应的函数,将数据复制到应用空间,并且立刻返回。

二、重要的函数及其数据结构

wait内核函数

休眠,直到 condition 为真;休眠期间是可被打断的,可以被信号打断

wait_event_interruptible(wq, condition)
参数1:等待队列,休眠的时候,会将进程/线程放入等待队列中,中断服务程序会从 wq 中把它取出来唤醒。
参数2:一直等待,直到 condition为真

休眠,直到 condition 为真;退出的唯一条件是 condition 为真,信号也不好使

wait_event(wq, condition)

休眠,直到 condition 为真或超时;休眠期间是可被打断的,可以被信号打断

wait_event_interruptible_timeout(wq,condition, timeout)

休眠,直到 condition 为真;退出的唯一条件是 condition 为真,信号也不好使

wait_event_timeout(wq, condition,
timeout)

唤醒函数

唤醒 x 队列中状态为“ TASK_INTERRUPTIBLE”的线程,只唤
醒其中的一个线程

wake_up_interruptible(x)

唤醒 x 队列中状态为“ TASK_INTERRUPTIBLE”的线程,只唤
醒其中的 nr 个线程

wake_up_interruptible_nr(x, nr)

唤 醒 x 队 列 中 状 态 为 “ TASK_INTERRUPTIBLE ” 或
“TASK_UNINTERRUPTIBLE”的线程,只唤醒其中的一个线程

wake_up(x)

唤醒 x 队列中状态为“ TASK_INTERRUPTIBLE”的线程,唤醒
其中的所有线程

wake_up_interruptible_all(x)

唤 醒 x 队 列 中 状 态 为 “ TASK_INTERRUPTIBLE ” 或
“TASK_UNINTERRUPTIBLE”的线程,只唤醒其中 nr 个线程

wake_up_interruptible_all(x)

唤 醒 x 队 列 中 状 态 为 “ TASK_INTERRUPTIBLE ” 或
“TASK_UNINTERRUPTIBLE”的线程,唤醒其中的所有线程

wake_up_all(x)

三.驱动编程步骤

1.初始化等待队列wq
2.read函数中调用wait_event_interruptible
它本身会判断 event 是否为 FALSE,如果为 FASLE 表示无数据,则休眠。
当从 wait_event_interruptible 返回后,把数据复制回用户空间。
3.在中断服务程序里:
设置 event 为 TRUE,并调用 wake_up_interruptible 唤醒线程。


附录(源码)

#include <linux/module.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/sched.h>

struct gpio_key {
	int gpio;
	struct gpio_desc *gpiod;
	int flag;
	int irq;
};


static struct gpio_key *gpio_key_imx6ull;

/*
 *创建设备号需要的一些参数
 */
static int major = 0;//主设备号
static struct class *cls;//类
static struct device *dev;//设备

/* 创建环形缓冲区
 *用于保存数据,防止数据丢失
 */
#define BUF_LEN 128
static int g_keys[BUF_LEN];//环形缓冲区的大小
static int r, w;

#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));
}

//入队列write
static void put_key(int key)
{
	if (!is_key_buf_full())
	{
		g_keys[w] = key;
		w = NEXT_POS(w);
	}
}

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

//定义gpio_key_wait等待队列,并初始化
static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);

//中断服务1程序,用于唤醒队列
static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
	struct gpio_key *gpio_key = dev_id;
	int val;
	int key;
	
	val = gpiod_get_value(gpio_key->gpiod);
	

	printk("key %d %d\n", gpio_key->gpio, val);
	key = (gpio_key->gpio << 8) | val;
	put_key(key);
	wake_up_interruptible(&gpio_key_wait);//唤醒等待队列
	
	return IRQ_HANDLED;
}


static ssize_t gpio_key_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int err;
	int key;

	//如果!is_key_buf_empty()为假,则休眠,否则就是有数据
	wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());
	key = get_key();
	err = copy_to_user(buf, &key, 4);
	
	return 4;

}


static struct file_operations gpio_key_drv = {
	.owner = THIS_MODULE,
	.read = gpio_key_drv_read,
};

//设备匹配成功后执行
static int gpio_key_drv_probe(struct platform_device *pdev)
{
	int count;
	int i;
	int err;
	int ret;
	struct device_node *node = pdev->dev.of_node;
	enum of_gpio_flags flag;

	//创建设备号
	major = register_chrdev(0, "gpio_key", &gpio_key_drv);

	//创建类
	cls = class_create(THIS_MODULE, "gpio_key_class");
	if(IS_ERR(cls)){
		printk("class_create failed\n");
		ret = PTR_ERR(cls);
		goto err_0; 
		}

	//创建设备
	dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "gpio_key"); /* /dev/100ask_gpio_key */
	if (IS_ERR(dev)) {
		printk( "device_create failed\n");
		ret = PTR_ERR(dev);
		goto err_1;
	}
	count = of_gpio_count(node);
	if (!count)
	{
		printk("%s %s line %d, there isn't any gpio available\n", __FILE__, __FUNCTION__, __LINE__);
		return -1;
	}

	//获取设备树中的信息
	gpio_key_imx6ull = kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL);
	for (i = 0; i < count; i++)
	{
		gpio_key_imx6ull[i].gpio = of_get_gpio_flags(node, i, &flag);
		if (gpio_key_imx6ull[i].gpio < 0)
		{
			printk("%s %s line %d, of_get_gpio_flags fail\n", __FILE__, __FUNCTION__, __LINE__);
			return -1;
		}
		gpio_key_imx6ull[i].gpiod = gpio_to_desc(gpio_key_imx6ull[i].gpio);
		gpio_key_imx6ull[i].flag = flag & OF_GPIO_ACTIVE_LOW;
		gpio_key_imx6ull[i].irq  = gpio_to_irq(gpio_key_imx6ull[i].gpio);
	}

	for (i = 0; i < count; i++)
	{
		err = request_irq(gpio_key_imx6ull[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "100ask_gpio_key", &gpio_key_imx6ull[i]);
	}

		return 0;
err_0:
		unregister_chrdev(major, "gpio_key");
		return ret;
err_1:
		class_destroy(cls);
		return ret;

}

static int gpio_key_drv_remove(struct platform_device *pdev)
{
	int count;
	int i;
	struct device_node *node = pdev->dev.of_node;
	
	count = of_gpio_count(node);
		for (i = 0; i < count; i++)
		{
			free_irq(gpio_key_imx6ull[i].irq, &gpio_key_imx6ull[i]);
		}
		kfree(gpio_key_imx6ull);
		return 0;
	device_destroy(cls, MKDEV(major, 0));
	class_destroy(cls);
	unregister_chrdev(major, "gpio_key");

	
}

//匹配列表
static const struct of_device_id gpio_key_device[] = {
    { .compatible = "atkalpha-key" },
    { },
};

//platform驱动结构体
static struct platform_driver gpio_key_driver = {
	.probe = gpio_key_drv_probe,
	.remove = gpio_key_drv_remove,
	.driver = {
		.name = "imx6ull_gpio_key",//驱动名字,用于和设备匹配
		.of_match_table = gpio_key_device,//设备树匹配
		},
};


static int __init gpio_key_drv_init(void)
{

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
   return  platform_driver_register(&gpio_key_driver); 
}


static void __exit gpio_key_drv_exit(void)
{
	platform_driver_unregister(&gpio_key_driver);
}

module_init(gpio_key_drv_init);
module_exit(gpio_key_drv_exit);
MODULE_LICENSE("GPL");


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苦梨甜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值