4.20-4.22

Linux阻塞和非阻塞IO也是比较重要的,和上一节的并发与竞争都属于是进程调度与切换

在实际应用里

阻塞常常用在读取传感器数据,比如读取温度、湿度、陀螺仪、ADC传感器,串口通信摄像头、音频数据。数据不是随时都有,必须等转换完成。当数据有了再提醒读取

非阻塞常常用屏幕、触摸屏、按钮,高实时性控制,网络编程.要同时干多件事,不能被任何一件事卡住

上一节进程调度和切换是为了并发过程程序正常运行不发生错误也让CPU能够合理使用。Linux阻塞与非阻塞也是为了让CPU能够合理的使用,最大限度的发挥作用,和信号量有点类似。

阻塞:顾名思义就是不运行,当通知到(可能是中断或信号)时才执行非阻塞:不休眠一直在执行,就像死循环一直执行cpu占用率100%

阻塞实验

需要加入头文件include/linux/wait.h

阻塞是使用队列的方式实现

需要等待队列头,等待队列项,队列项添加到队列头,等待唤醒这四部

等待队列头:

void init_waitqueue_head(struct wait_queue_head *wq_head)参数 wq_head 就是要初始化的等待队列头。

等待队列项:

DECLARE_WAITQUEUE(name, tsk)

name 就是等待队列项的名字,tsk 表示这个等待队列项属于哪个任务(进程),一般设置为

current

将队列项添加/移除等待队列头:

void add_wait_queue(struct wait_queue_head *wq_head,

struct wait_queue_entry *wq_entry)

wq_head:等待队列项要加入的等待队列头。

wq_entry:要加入的等待队列项。

等待唤醒:

void wake_up_interruptible(struct wait_queue_head *wq_head)

wq_head 就是要唤醒的等待队列头

阻塞按键实验

在以前写的按键中断实验里加入等待队列头,等待队列项,队列项添加到队列头,等待唤醒

复制代码
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define KEY_CNT			1
#define KEY_NAME		"key"

enum key_status {
    KEY_PRESS = 0,
    KEY_RELEASE,
    KEY_KEEP,
};

struct key_dev{
	dev_t devid;
	struct cdev cdev;
	struct class *class;
	struct device *device;
	struct device_node	*nd;
	int key_gpio;
	struct timer_list timer;
	int irq_num;
	atomic_t status;
//加上wait_queue_head_t

	wait_queue_head_t r_wait;
};

static struct key_dev key;

static irqreturn_t key_interrupt(int irq, void *dev_id)
{
	mod_timer(&key.timer, jiffies + msecs_to_jiffies(15));
    return IRQ_HANDLED;
}

static int key_parse_dt(void)
{
	int ret;
	const char *str;

	key.nd = of_find_node_by_path("/key");
	if(key.nd == NULL) {
		printk("key node not find!\r\n");
		return -EINVAL;
	}

	ret = of_property_read_string(key.nd, "status", &str);
	if(ret < 0)
	    return -EINVAL;

	if (strcmp(str, "okay"))
        return -EINVAL;

	ret = of_property_read_string(key.nd, "compatible", &str);
	if(ret < 0) {
		printk("key: Failed to get compatible property\n");
		return -EINVAL;
	}

    if (strcmp(str, "alientek,key")) {
        printk("key: Compatible match failed\n");
        return -EINVAL;
    }

	key.key_gpio = of_get_named_gpio(key.nd, "key-gpio", 0);
	if(key.key_gpio < 0) {
		printk("can't get key-gpio");
		return -EINVAL;
	}

    key.irq_num = irq_of_parse_and_map(key.nd, 0);
    if(!key.irq_num){
        return -EINVAL;
    }

	printk("key-gpio num = %d\r\n", key.key_gpio);
	return 0;
}

static int key_gpio_init(void)
{
	int ret;
    unsigned long irq_flags;

	ret = gpio_request(key.key_gpio, "KEY0");
    if (ret) {
        printk(KERN_ERR "key: Failed to request key-gpio\n");
        return ret;
	}

    gpio_direction_input(key.key_gpio);

	irq_flags = irq_get_trigger_type(key.irq_num);
	if (IRQF_TRIGGER_NONE == irq_flags)
		irq_flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;

	ret = request_irq(key.irq_num, key_interrupt, irq_flags, "Key0_IRQ", NULL);
	if (ret) {
        gpio_free(key.key_gpio);
        return ret;
    }

	return 0;
}

static void key_timer_function(struct timer_list *arg)
{
    static int last_val = 0;
    int current_val;

//触发中断时唤醒队列
    current_val = gpio_get_value(key.key_gpio);
    if (1 == current_val && !last_val){
        atomic_set(&key.status, KEY_PRESS);
		wake_up_interruptible(&key.r_wait);
	}
    else if (0 == current_val && last_val) {
        atomic_set(&key.status, KEY_RELEASE);
		wake_up_interruptible(&key.r_wait);
	}
    else
        atomic_set(&key.status, KEY_KEEP);

    last_val = current_val;
}

static int key_open(struct inode *inode, struct file *filp)
{
	return 0;
}

static ssize_t key_read(struct file *filp, char __user *buf,
            size_t cnt, loff_t *offt)
{
    int ret;
//添加队列项

	ret = wait_event_interruptible(key.r_wait, KEY_KEEP != atomic_read(&key.status));
	if(ret)
		return ret;

    ret = copy_to_user(buf, &key.status, sizeof(int));

    atomic_set(&key.status, KEY_KEEP);

    return ret;
}

static ssize_t key_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}

static int key_release(struct inode *inode, struct file *filp)
{
	return 0;
}

static struct file_operations key_fops = {
	.owner = THIS_MODULE,
	.open = key_open,
	.read = key_read,
	.write = key_write,
	.release = 	key_release,
};

static int __init mykey_init(void)
{
	int ret;

//添加队列头
	init_waitqueue_head(&key.r_wait);

	timer_setup(&key.timer, key_timer_function, 0);

	atomic_set(&key.status, KEY_KEEP);

	ret = key_parse_dt();
	if(ret)
		return ret;

	ret = key_gpio_init();
	if(ret)
		return ret;

	ret = alloc_chrdev_region(&key.devid, 0, KEY_CNT, KEY_NAME);
	if(ret < 0) {
		pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", KEY_NAME, ret);
		goto free_gpio;
	}

	key.cdev.owner = THIS_MODULE;
	cdev_init(&key.cdev, &key_fops);

	ret = cdev_add(&key.cdev, key.devid, KEY_CNT);
	if(ret < 0)
		goto del_unregister;

	key.class = class_create(THIS_MODULE, KEY_NAME);
	if (IS_ERR(key.class)) {
		goto del_cdev;
	}

	key.device = device_create(key.class, NULL, key.devid, NULL, KEY_NAME);
	if (IS_ERR(key.device)) {
		goto destroy_class;
	}

	return 0;

destroy_class:
	device_destroy(key.class, key.devid);
del_cdev:
	cdev_del(&key.cdev);
del_unregister:
	unregister_chrdev_region(key.devid, KEY_CNT);
free_gpio:
	free_irq(key.irq_num, NULL);
	gpio_free(key.key_gpio);
	return -EIO;
}

static void __exit mykey_exit(void)
{
	cdev_del(&key.cdev);
	unregister_chrdev_region(key.devid, KEY_CNT);
	del_timer_sync(&key.timer);
	device_destroy(key.class, key.devid);
	class_destroy(key.class);
	free_irq(key.irq_num, NULL);
	gpio_free(key.key_gpio);
}

module_init(mykey_init);
module_exit(mykey_exit);
MODULE_LICENSE("GPL");
MODULE_INFO(intree, "Y");

在之前写的的按键中断实验里,等待按键是一直等待,加载驱动后,占cpu利用率高达99.4%,所以使用阻塞的方式更加合适。

加载驱动执行app程序,在读函数里创建队列项,未触发按键时cpu进入阻塞,当按键触发后唤醒队列执行读取按键

非阻塞实验:

常常使用poll、epoll 和 select 可以用于处理轮询

简单来说有点像死循环,当返回错误就行的去访问直到返回正常

添加头文件include/linux/poll.h

具体函数的poll、epoll 和 select使用和非阻塞按键代码下期再讲,因为最近真的忙

这期就到这了,有问题和建议欢迎评论

相关推荐
MmeD UCIZ2 小时前
GO 快速升级Go版本
开发语言·redis·golang
京师20万禁军教头2 小时前
28面向对象(中级)-封装
java
tERS ERTS2 小时前
头歌答案--爬虫实战
java·前端·爬虫
Fate_I_C2 小时前
Kotlin函数一
android·开发语言·kotlin
Eiceblue2 小时前
C# 实现 XLS 与 XLSX 格式双向互转(无需依赖 Office)
开发语言·c#·visual studio
识君啊2 小时前
中小厂数据库事务高频面试题
java·数据库·mysql·隔离级别·数据库事务·acid
代码中介商2 小时前
Linux 静态库与共享库完全指南:从制作到使用
linux·运维·服务器
皮卡蛋炒饭.2 小时前
网络基础概念
服务器·网络协议
少许极端2 小时前
算法奇妙屋(四十八)-单调栈
java·算法·单调栈