驱动开发--阻塞与非阻塞

news2025/1/14 1:00:45

一、五种IO模型------读写外设数据的方式

  1. 阻塞: 不能操作就睡觉

  2. 非阻塞:不能操作就返回错误

  3. 多路复用:委托中介监控

  4. 信号驱动:让内核如果能操作时发信号,在信号处理函数中操作

  5. 异步IO:向内核注册操作请求,内核完成操作后发通知信号

二、阻塞与非阻塞

应用层:

open时由O_NONBLOCK指示read、write时是否阻塞

open以后可以由fcntl函数来改变是否阻塞:

flags = fcntl(fd,F_GETFL,0);
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);

驱动层:通过等待队列

wait_queue_head_t //等待队列头数据类型
​
init_waitqueue_head(wait_queue_head_t *pwq) //初始化等待队列头
    
wait_event_interruptible(wq,condition)//浅度睡眠,条件不成立,则进入等待队列
/*
功能:条件不成立则让任务进入浅度睡眠,直到条件成立醒来
    wq:等待队列头
    condition:C语言表达式
返回:正常唤醒返回0,信号唤醒返回非0(此时读写操作函数应返回-ERESTARTSYS)
*/
        
wait_event(wq,condition) //深度睡眠

​
wake_up_interruptible(wait_queue_head_t *pwq)//唤醒浅度睡眠
        
wake_up(wait_queue_head_t *pwq)//唤醒深度睡眠
  
    
/*
1. 读、写用不同的等待队列头rq、wq
2. 无数据可读、可写时调用wait_event_interruptible(rq、wq,条件)
3. 写入数据成功时唤醒rq,读出数据成功唤醒
*/



​

 

三、以读函数队列为例

 1.添加2个等待队列 (读、写)

2.在init函数里面,初始化这两个等待队列 

 3.判断是否为非阻塞

4.write函数写数据的话,唤醒读等待队列

1. shell命令

linux@linux:~/fs4412/mydrivercode$ lsmod | grep char
linux@linux:~/fs4412/mydrivercode$ sudo insmod ./mychar.ko
[sudo] password for linux: 
linux@linux:~/fs4412/mydrivercode$ cat /proc/devices |grep cahr
linux@linux:~/fs4412/mydrivercode$ cat /proc/devices |grep char
 11 mychar
linux@linux:~/fs4412/mydrivercode$ sudo mknod /dev/mydev c 11 0
linux@linux:~/fs4412/mydrivercode$ ls /dev/mydev* -l
crw-r--r-- 1 root root 11, 0 Jan 18 00:19 /dev/mydev
linux@linux:~/fs4412/mydrivercode$ sudo chmod a+w /dev/mydev
linux@linux:~/fs4412/mydrivercode$ ls /dev/mydev* -l
crw-rw-rw- 1 root root 11, 0 Jan 18 00:19 /dev/mydev
linux@linux:~/fs4412/mydrivercode$ vi testmychar_app.c 
linux@linux:~/fs4412/mydrivercode$ cp testmychar_app.c  testmychar_nonblockread.c
linux@linux:~/fs4412/mydrivercode$ vi testmychar_nonblockread.c 
linux@linux:~/fs4412/mydrivercode$ gcc testmychar_nonblockread.c -o trn -Wall
linux@linux:~/fs4412/mydrivercode$ ./trn /dev/mydev
read data failed

2.mychar.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include "mychar.h"

#define BUF_LEN 100
int major = 11;//主设备号
int minor =0;//次设备号
int mychar_num = 1;//次设备数量
//ioctrl
struct mychar_dev
{
	struct cdev mydev;
	char mydev_buf[BUF_LEN];
	int curlen;//100个字节中已经存有的数据
	wait_queue_head_t rq;
	wait_queue_head_t wq;
};

struct mychar_dev gmydev;

int mychar_open(struct inode *pnode,struct file *pfile)
{//inode * pnode 中有一个成员 i_rdev存放这mydev的地址,现在用struct成员的地址求出结构体的地址
	//求出gmydev的地址
	pfile->private_data = (void *)container_of(pnode->i_cdev,struct mychar_dev,mydev);
	printk("mychar_open is called\n");
	return 0;
}

int mychar_close(struct inode *pnode,struct file *pfile)
{
	printk("mychar_close is called\n");
	return 0;
}
ssize_t mychar_read(struct file *pfile,char __user *puser,size_t count,loff_t *p_pos)
{//内核空间mydev_buf  向   用户空间puser  进行copy
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	//定义一个指针pmydev
	int size = 0;
	int ret = 0;

	if(pmydev->curlen<=0)//如果无数据可读
	{
		if(pfile->f_flags & O_NONBLOCK)//如果为真,fd描述符为非阻塞

		{//非阻塞,且无数据可读,返回错误
			printk("O_NONBLOCK no data to read\n");
			return -1;
		}
		else
		{//阻塞,且无数据可读,加入到等待队列中去,进入浅度睡眠中		   
			ret = wait_event_interruptible(pmydev->rq,pmydev->curlen > 0);
			if(ret)//if ret==0,条件成立,正常唤醒,下面语句不执行
			{
				printk("wake up by signal\n");
				return -ERESTART;
			}
		}
	}

	if(count >pmydev->curlen)//如果期望读取的数据大小大于了原本数据大小
	{
		size = pmydev->curlen;//读取的数据为被读取数据的大小
	}
	else
	{
		size = count;//被读取的数据大小为期望读取的数据大小

	}
	ret = copy_to_user(puser,pmydev->mydev_buf,size);
	if(ret)//
	{
		printk("copy_to_user failed\n");
		return -1;
	}
	//将没被读取的数据拷贝到初始读取的位置.
	memcpy(pmydev->mydev_buf,pmydev->mydev_buf + size,pmydev->curlen - size);
	pmydev->curlen =pmydev-> curlen -size;//被读取后剩余这么多字节

	
	//一旦读取一些数据后,curlen一定小于BUF_LEN,直接唤醒write函数中的写队列
	wake_up_interruptible(&pmydev->wq);

	return size;

}

ssize_t mychar_write(struct file *pfile,const char __user *puser,size_t count,loff_t *p_pos)
{//用户空间puser  向 内核空间mydev_buf  copy
	int ret=0;
	int size = 0;
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;

	//阻塞与非阻塞
	if(pmydev->curlen>=BUF_LEN)//如果空间可写
	{
		if(pfile->f_flags & O_NONBLOCK)//如果为真,fd描述符为非阻塞

		{//非阻塞,且无数据可读,返回错误
			printk("O_NONBLOCK no space to write\n");
			return -1;
		}
		else
		{//阻塞,且无空间可写,加入到等待队列中去,进入浅度睡眠中		   
			ret = wait_event_interruptible(pmydev->wq,pmydev->curlen<BUF_LEN);
			if(ret)//if ret==0,条件成立,正常唤醒,下面语句不执行
			{
				printk("wake up by signal\n");
				return -ERESTART;
			}
		}
	}



	if(count>BUF_LEN - pmydev->curlen)//如果期望写入的数据大小大于100个字节剩余的空间
	{
		size = BUF_LEN - pmydev->curlen;
	}
	else 
	{
		size = count;
	}
	ret = copy_from_user(pmydev->mydev_buf,puser,size);
	//将用户空间puser中 size大小的数据写入到内核空间mydev_buf为始的地址中去
	if(ret)
	{
		printk("copy_from_user is failed\n");
		return -1;
	}
	pmydev->curlen = pmydev->curlen +size;
	//mydev_buf中存在的数据大小

	//一旦写入数据后,curlen一定大于0,直接唤醒read函数中的睡眠队列
	wake_up_interruptible(&pmydev->rq);

	return size;

}
//实现ioctl
long mychar_ioctl(struct file *pfile,unsigned int cmd,unsigned long arg)
{

	int __user *pret = (int *)arg;
	int maxlen = BUF_LEN;
	int ret = 0;
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	switch(cmd)
	{
	case MYCHAR_IOCTL_GET_MAXLEN:
		//把最大值copy到用户空间去
		ret = copy_to_user(pret,&maxlen,sizeof(int));
		if(ret)
		{
			printk("copy_to_user maxlen failed\n ");
			return -1;
		}
		break;
	case MYCHAR_IOCTL_GET_CURLEN:
		//把当前值copy到用户空间去
		ret = copy_to_user(pret,&pmydev->curlen,sizeof(int));
		if(ret)
		{
			printk("copy_to_user curlen failed\n ");
			return -1;
		}
		break;
	default:
		printk("the cmd is unknow\n");
		return -1;
	}
	return 0;
}
struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = mychar_open,
	.release = mychar_close,
	.read = mychar_read,
	.write = mychar_write,
	.unlocked_ioctl = mychar_ioctl,
};

int __init mychar_init(void)
{
	int ret = 0;
	dev_t devno = MKDEV(major,minor);//组合成完整的设备号
	/*申请设备号*/
	ret = register_chrdev_region(devno,mychar_num,"mychar");
	if(ret)//ret非0,表示失败
	{
		ret = alloc_chrdev_region(&devno,minor,mychar_num,"mychar");
		//此设备号申请后填写到devno地址中去,从minor开始申请mychar_num个
		if(ret)
		{
			printk("get devno failed\n");
			return -1;
		}
		major = MAJOR(devno);//获取新的设备号,不要遗漏
		//次设备号都是0,所以不用再次提取
	}
	//给struct_cdev对象制定操作函数集
	cdev_init(&gmydev.mydev,&myops);    
	//将struct_cdev对象添加到内核对应的数据结构里
	gmydev.mydev.owner = THIS_MODULE;
	cdev_add(&gmydev.mydev,devno,1);

	init_waitqueue_head(&gmydev.rq);
	init_waitqueue_head(&gmydev.wq);
	return 0;
}

void __exit mychar_exit(void)
{
	dev_t devno = MKDEV(major,minor);
	cdev_del(&gmydev.mydev);

	unregister_chrdev_region(devno,mychar_num);

}


MODULE_LICENSE("GPL");

module_init(mychar_init);
module_exit(mychar_exit);

3.testmychar_nonblockread.c

#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include "mychar.h"



int main(int argc, const char *argv[])
{
	int fd = -1;
	char buf[8] = "";
	int ret = 0;
	

	if(argc<2)
	{
		printf("the argument is too few\n");
		return 1;
	}
	//设为非阻塞,阻塞则去掉 |
	fd = open(argv[1],O_RDWR|O_NONBLOCK);
	if(fd<0)
	{
		printf("open %s failed\n",argv[1]);
		return 2;:
	}
	
ret=	read(fd,buf,8);
if(ret<0)
{
printf("read data failed\n");

}
else
{
	printf("buf = %s\n",buf);

}


	close(fd);
	fd = -1 ;
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1392882.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

助力工业焊缝质量检测,基于YOLOv8【n/s/m/l/x】全系列参数模型开发构建工业焊接场景下钢材管道焊缝质量检测识别分析系统

焊接是一个不陌生但是对于开发来说相对小众的场景&#xff0c;在我们前面的博文开发实践中也有一些相关的实践&#xff0c;感兴趣的话可以自行移步阅读即可&#xff1a; 《轻量级模型YOLOv5-Lite基于自己的数据集【焊接质量检测】从零构建模型超详细教程》 《基于DeepLabV3Pl…

5-微信小程序语法参考

1. 数据绑定 官网传送门 WXML 中的动态数据均来自对应 Page 的 data。 数据绑定使用 Mustache 语法&#xff08;双大括号&#xff09;将变量包起来 ts Page({data: {info: hello wechart!,msgList: [{ msg: hello }, { msg: wechart }]}, })WXML <view class"vie…

分类问题:人工神经网络(ANN)+BP算法(误差后向传播)+考试例题讲解

学习链接:分类问题:人工神经网络(ANN)+BP算法(误差后向传播)+考试例题讲解 资料链接:链接:https://pan.baidu.com/s/1ijvMQmwtRgLO4KDSsNODMw 提取码:vyok 神经网络的应用非常的广,它核心思想非常简单,就是人是如何认知感知并且处理这个世界中的现实问题的。…

【React】Redux的使用详解

文章目录 Redux的三大原则Redux官方图react-redux使用 1、创建store管理全局状态​ 2、在项目index.js根节点引用 3、 在需要使用redux的页面或者组件中&#xff0c;通过connect高阶组件映射到该组件的props中 redux中异步操作如何使用redux-thunkcombineReducers函数 Re…

[C#]winform部署官方yolov8-rtdetr目标检测的onnx模型

【官方框架地址】 https://github.com/ultralytics/ultralytics 【算法介绍】 RTDETR&#xff0c;全称“Real-Time Detection with Transformer for Object Tracking and Detection”&#xff0c;是一种基于Transformer结构的实时目标检测和跟踪算法。它在目标检测和跟踪领域…

保证Kafka消息有序性

一、Kafka特性 写入同一个partion分区中的数据是一定有顺序的kafka中一个消费者消费一个partion的数据&#xff0c;消费者取出数据时&#xff0c;也是有顺序的 二、保证消息Kafka消息有序性 在生产者端&#xff0c;应保证消息被写入同一分区。可以在构造消息时指定消息的key…

Kafka-消费者-KafkaConsumer分析

与KafkaProducer不同的是&#xff0c;KafkaConsumer不是一个线程安全的类。 为了便于分析&#xff0c;我们认为下面介绍的所有操作都是在同一线程中完成的&#xff0c;所以不需要考虑锁的问题。 这种设计将实现多线程处理消息的逻辑转移到了调用KafkaConsumer的代码中&#x…

葡萄酒术语“干”是什么意思呢?

一个初学品酒的人常常会感到力不从心&#xff0c;有如此多的术语&#xff0c;如甜、干、单宁、酒体等等&#xff0c;很容易让人迷失。嗯&#xff0c;就像情人眼里出西施一样&#xff0c;“好酒”因人而异。虽然品尝各种不同的葡萄酒是了解你喜欢什么的最好方法&#xff0c;但我…

springboot开启HTTPS

目录 一、前言 HTTP和HTTPS的含义以及区别 二、域名映射 三、添加SSL证书 四、Http转Https 五、内网穿透 一、前言 我们平常写完一个接口&#xff0c;其访问一般都是使用http协议 我们最终想要的结果是使用安全的HTTPS来访问 在我们开始实现之前&#xff0c;我们要先搞明…

前端——框架——Vue

提示&#xff1a; 本文只是从宏观角度简要地梳理一遍vue3&#xff0c;不至于说学得乱七八糟、一头雾水、不知南北&#xff0c;如果要上手写代码、撸细节&#xff0c;可以根据文中的关键词去查找资料 简问简答&#xff1a; vue.js是指vue3还是vue2&#xff1f; Vue.js通常指的是…

软件测试|sqlalchemy relationship

简介 SQLAlchemy是一个流行的Python ORM&#xff08;对象关系映射&#xff09;库&#xff0c;它允许我们以面向对象的方式管理数据库。在SQLAlchemy中&#xff0c;relationship是一个重要的功能&#xff0c;用于建立表之间的关系。在本文中&#xff0c;我们将详细探讨relation…

阿里云国外云服务器地域、收费标准及活动报价2024新版

阿里云国外服务器优惠活动「全球云服务器精选特惠」&#xff0c;国外服务器租用价格24元一个月起&#xff0c;免备案适合搭建网站&#xff0c;部署独立站等业务场景&#xff0c;阿里云服务器网aliyunfuwuqi.com分享阿里云国外服务器优惠活动&#xff1a; 全球云服务器精选特惠…

【前后端的那些事】评论功能实现

文章目录 聊天模块1. 数据库表2. 后端初始化2.1 controller2.2 service2.3 dao2.4 mapper 3. 前端初始化3.1 路由创建3.2 目录创建3.3 tailwindCSS安装 4. tailwindUI5. 前端代码编写 前言&#xff1a;最近写项目&#xff0c;发现了一些很有意思的功能&#xff0c;想写文章&…

Power Designer 连接 PostgreSQL 逆向工程生成pd表结构操作步骤以及过程中出现的问题解决

一、使用PowerDesigner16.5 链接pg数据库 1.1、启动PD.选择Create Model…。 1.2、选择Model types / Physical Data Model Physical Diagram&#xff1a;选择pgsql直接【ok】 1.3、选择connect 在工具栏选择Database-Connect… 快捷键&#xff1a;ctrlshiftN.如下图&#xff…

第八站:C++面向对象(继承和派生)

继承和派生 派生:由父类派生出子类 继承:子类继承父类(继承不会继承析构函数和构造函数:父类的所有成员函数&#xff0c;以及数据成员&#xff0c;都会被子类继承&#xff01;) "子类派生出的类"会指向"父类被继承的类",父类就是基类 实例1: 先创建一个父…

Flask框架小程序后端分离开发学习笔记《2》构建基础的HTTP服务器

Flask框架小程序后端分离开发学习笔记《2》构建基础的HTTP服务器 Flask是使用python的后端&#xff0c;由于小程序需要后端开发&#xff0c;遂学习一下后端开发。本节提供一个构建简单的本地服务器的代码&#xff0c;仔细看注释&#xff0c;学习每一步的流程&#xff0c;理解服…

react-app框架——使用monaco editor实现online编辑html代码编辑器

文章目录 ⭐前言&#x1f496;react系列文章 ⭐配置monaco-editor&#x1f496;引入react-monaco-editor&#x1f496;引入react-app-rewired&#x1f496;通过config-overrides.js添加monaco插件配置 ⭐编辑代码的react页面配置&#x1f496;扩展 可自定义配置语言 ⭐效果⭐总…

使用 mybatis-plus 的mybaits的一对多时, total和record的不匹配问题

应该是框架的问题&#xff0c;去官方仓库提了个issues&#xff0c;等回复 https://github.com/baomidou/mybatis-plus/issues/5923 背景 发现 record是两条&#xff0c;但是total显示3 使用resultMap一对多时&#xff0c;三条数据会变成两条&#xff0c;但是total确是3条 下…

新能源汽车智慧充电桩方案:基于视频监控的可视化智能监管平台

一、方案概述 TSINGSEE青犀&触角云新能源汽车智慧充电桩方案围绕互联网、物联网、车联网、人工智能、视频技术、大数据、4G/5G等技术&#xff0c;结合云计算、移动支付等&#xff0c;实现充电停车一体化、充电桩与站点管理等功能&#xff0c;达到充电设备与站点的有效监控…

[足式机器人]Part2 Dr. CAN学习笔记-Ch04 Advanced控制理论

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记 - Ch04 Advanced控制理论 1. 绪论2. 状态空间表达State-Space Representation3. Phase Portrait相图&#xff0c;相轨迹3 1. 1-D3 2. 2-D3 3. General Form3 4. Summary3.5. 爱情中的数学-Phase …