2022-12-29 驱动-IO多路复用

news2024/9/25 19:24:36

IO多路复用的原理:
1>select和poll原理:将文件描述符添加到集合中,监测是否有准备就绪的文件描述符,将未准备就绪的文件描述符剔除集合,准备就绪的文件描述符实现相关操作

2>对于epoll函数原理:创建树的根节点,将文件描述符添加到根节点为epfd的二叉树中,若有准备好的文件描述符,将其摘下放入集合中,实现相关的操作

select为例:在用户层创建一个集合存放要监测的文件描述符,然后通过调用select()函数等待事件发生。若事件发生,读取用户空间的数据。
在内核和用户层之间有一个虚拟文件系统层,当用户层调用select函数后,虚拟文件系统层会调用sys_select()函数,在该函数中实现的功能:
(1)在内核申请一块内存,用来保存用户表,通过copy_from_user将用户表从用户空间拷贝到这块内存
(2)从表中获取文件描述符fd->fd_arry[fd]->struct file->f_op指针->poll()函数
mask=poll(),接收poll()的返回值,对返回值进行判断
mask为0,数据没有准备好;mask不等于0为POLLIN/POLLOUT
(3)如果fd_set表的所有文件描述符对应的poll()返回值都为0,的话,进程进入可中断的休眠状态
(4)在虚拟文件系统层收到一个或多个硬件的唤醒条件后,再次遍历文件描述符,找到准备好的文件描述符,拷贝到用户空间
内核层:poll函数提交等待队列头,被唤醒就返回POLLIN

//mycdev.c
#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<linux/uaccess.h>
#include<linux/slab.h>
#include<linux/io.h>
#include<linux/device.h>
#include<linux/poll.h>

//定义字符设备驱动结构体对象指针
struct cdev* cdev;
char kbuf[128]={0};
dev_t devno;//设备号
unsigned int minor=0;//次设备号
int count=3;//设备数量
struct class* cls;//指向设备目录的指针
struct device* dev;//指向设备节点的指针
#if 1
unsigned int major=0;//进行动态申请设备号
#else
unsigned int major=500;//进行静态指定设备号
#endif

//定义等待对列头
wait_queue_head_t wq_head;
unsigned int condition=0;

int mycdev_open(struct inode *inode,struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
ssize_t mycdev_read(struct file *file,char __user *ubuf,size_t size,loff_t *off)
{
	int ret;
	//先判断size的大小是否大于内核kbuf的大小,如果比kbuf大,把size的数据修改为kbuf的大小
	if(size>sizeof(kbuf))
		size=sizeof(kbuf);
	//将kbuf的数据拷贝给用户空间
	ret=copy_to_user(ubuf,kbuf,size);
	if(ret)
	{
		printk("copy_to_user failed\n");
		return -EIO;
	}
	condition=0;
	return 0;
}
ssize_t mycdev_write(struct file *file,const char __user *ubuf,size_t size,loff_t *off)
{
    int ret;// 定义变量接收函数返回值
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    //先判断size的大小是否大于内核kbuf的大小,如果比kbuf大,把size的数据修改为kbuf的大小
    if(size>sizeof(kbuf))
        size=sizeof(kbuf);
    //从用户空间拷贝数据到内核
    ret=copy_from_user(kbuf,ubuf,size);
    if(ret)
    {
        printk("copy_from_user failed\n");
        return -EIO;
    }
	//唤醒进程1
	condition=1;
	wake_up_interruptible(&wq_head);
    return 0;
}
__poll_t mycdev_poll(struct file* file,struct poll_table_struct* wait)
{
	__poll_t mask=0;
	//向上提交等待队列头
	poll_wait(file,&wq_head,wait);
	if(condition)
	{
		mask|=POLLIN;
	}
	return mask;
}
 int mycdev_close(struct inode *inode,struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
//定义操作方法结构体
struct file_operations fops =
{
    .open=mycdev_open,
    .read=mycdev_read,
    .write=mycdev_write,
    .release=mycdev_close,
	.poll=mycdev_poll,
};

 static int __init mycdev_init(void)
{
	int ret,i;
	//给字符设备驱动对象申请空间
	cdev=cdev_alloc();
	if(cdev==NULL)
	{
		printk("给字符设备驱动对象申请空间失败\n");
		ret=-ENOMEM;
		goto ERR1;
	}
	printk("给字符设备驱动对象申请空间成功\n");
	//对象的初始化
	cdev_init(cdev,&fops);
	//申请设备号
	if(major==0)//动态申请
	{
		ret=alloc_chrdev_region(&devno,minor,count,"mycdev");
		if(ret)
		{
			printk("动态指定设备号失败\n");
			goto ERR2;
		}
		//获取主设备号的次设备号
		major=MAJOR(devno);//获取主设备号
		minor=MINOR(devno);//获取次设备号
	}
	else if(major>0)
	{
		ret=register_chrdev_region(MKDEV(major,minor),count,"mycdev");
		if(ret)
		{
			printk("静态指定设备号失败\n");
			goto ERR2;
		}
	}
	printk("设备号申请成功\n");
	//将字符设备驱动注册进内核
	ret=cdev_add(cdev,MKDEV(major,minor),count);
	if(ret)
	{
		printk("字符设备驱动注册失败\n");
		goto ERR3;
	}
	//自动创建设备节点
	//向上提交目录
	cls=class_create(THIS_MODULE,"mycdev");
	if(IS_ERR(cls))
	{
		printk("向上提交目录失败\n");
		ret=PTR_ERR(cls);
		goto ERR4;
	}
	printk("向上提交目录成功\n");
	//向上提交设备节点
	for(i=0;i<count;i++)
	{
		dev=device_create(cls,NULL,MKDEV(major,i),NULL,"mycdev%d",i);
		if(IS_ERR(dev))
		{
			printk("向上提交节点信息失败\n");
			ret=PTR_ERR(dev);
			goto ERR5;
		}
	}
	printk("向上提交节点信息成功\n");

	//初始化等待队列头
	init_waitqueue_head(&wq_head);
	return 0;
ERR5:
	for(--i;i>=0;i--)
	{
		device_destroy(cls,MKDEV(major,i));
	}
	class_destroy(cls);
ERR4:
	cdev_del(cdev);
ERR3:
	unregister_chrdev_region(MKDEV(major,minor),count);
ERR2:
	kfree(cdev);
ERR1:
	return ret;
}
static void __exit mycdev_exit(void)
{
	//销毁设备节点
	int i;
	for(i=0;i<count;i++)
	{
		device_destroy(cls,MKDEV(major,i));
	}
	//销毁目录
	class_destroy(cls);
	//删除驱动对象
	cdev_del(cdev);
	//销毁设备号
	unregister_chrdev_region(MKDEV(major,minor),count);
	//释放动态申请的字符设备驱动的对象空间
	kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
//select-test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/time.h>
int main(int argc, const char *argv[])
{
	char buf[128]={0};
	int ret;
	int fd1=open("/dev/mycdev0",O_RDWR);
	if(fd1<0)
	{
		printf("open err\n");
		exit(-1);
	}
	int fd2=open("/dev/input/mouse0",O_RDWR);
	if(fd2<0)
	{
		printf("open err\n");
		exit(-1);
	}
	fd_set rfds;
	while(1)
	{
		//清空集合
		FD_ZERO(&rfds);
		//将文件描述符添加到集合
		FD_SET(fd1,&rfds);
		FD_SET(fd2,&rfds);
		//等待事件
		ret=select(fd2+1,&rfds,NULL,NULL,NULL);
		if(ret<0)
		{
			printf("select err\n");
		}
		if(FD_ISSET(fd1,&rfds))
		{
			read(fd1,buf,sizeof(buf));
			printf("mycdev:%s\n",buf);
		}
		if(FD_ISSET(fd2,&rfds))
		{
			read(fd2,buf,sizeof(buf));
			printf("mouse:%s\n",buf);
		}

	}
	return 0;
}
//1.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
	char buf[128]="hello";
	int fd=open("/dev/mycdev0",O_RDWR);
	if(fd<0)
	{
		printf("open err\n");
		exit(-1);
	}
	write(fd,buf,sizeof(buf));
	close(fd);
	return 0;
}
//epoll-test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/epoll.h>
int main(int argc, const char *argv[])
{
	char buf[128]={0};
	int ret,epfd,i;
	struct epoll_event event;
	struct epoll_event events[10];
	int fd1=open("/dev/mycdev0",O_RDWR);
	if(fd1<0)
	{
		printf("open err\n");
		exit(-1);
	}
	int fd2=open("/dev/input/mouse0",O_RDWR);
	if(fd2<0)
	{
		printf("open err\n");
		exit(-1);
	}
	//创建epoll
	epfd=epoll_create(10);
	if(epfd<0)
	{
		printf("epoll_create err\n");
		exit(-1);
	}
	//将文件描述符挂到epfd为根节点的树上
	event.events=EPOLLIN;
	event.data.fd=fd1;
	if(epoll_ctl(epfd,EPOLL_CTL_ADD,fd1,&event)<0)
	{
		printf("epoll_ctl err\n");
		exit(-1);
	}
	event.events=EPOLLIN;
	event.data.fd=fd2;
	if(epoll_ctl(epfd,EPOLL_CTL_ADD,fd2,&event)<0)
	{
		printf("epoll_ctl err\n");
		exit(-1);
	}
	while(1)
	{
		ret=epoll_wait(epfd,events,10,-1);
		if(ret<0)
		{
			printf("epoll_wait err\n");
			exit(-1);
		}
		for(i=0;i<ret;i++)
		{
			if(events[i].events&EPOLLIN)
			{
				read(events[i].data.fd,buf,sizeof(buf));
				printf("buf:%s\n",buf);
			}
		}
	}
	close(fd1);
	close(fd2);
	close(epfd);
	return 0;
}

 结果:

 

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

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

相关文章

超融合一体流式引擎,打造分布式数据库新纪元

12月28日&#xff0c;“数智驱动 全栈赋能”亚信科技2022年度系列产品发布会“数据库专场”线上成功举办&#xff0c;亚信科技重磅发布“超融合一体流式引擎”的AntDB V7.2数据库&#xff0c;并分享了在通信、交通等行业的应用实践。 百行千业数智化转型风起云涌&#xff0c;企…

布局云计算的下一个主场,紫光云引领行业云三大趋势

早在2018年&#xff0c;中国信通院发布2018云计算白皮书并指出&#xff1a;行业云时代全面开启。4年后的2022年底&#xff0c;Gartner将行业云平台列入2023年十大战略技术趋势&#xff0c;Gartner预测到2027年将有超过50%的企业使用行业云平台加速其业务项目。可以说&#xff0…

数据结构----树

树的概念 1.树的定义&#xff1a;树是n(n>0)个结点的有限集合 当n0时&#xff0c;称为”空树“ 当n>0时&#xff0c;有且仅有一个称为”根“的特定结点&#xff0c;该结点没有前驱&#xff0c;但是却有一个或者多个后继结点。 除了根节点以外的n-1个结点可划分为多个有…

项目管理范围(下)

创建WBS 什么是WBS&#xff1f; WBS(Work Breakdown Structure) --- 工作分解结构 创建WBS是将项目的可交付成果和项目工作分解成较小的&#xff0c;更易于管理的组件的过程。 它包含哪些内容&#xff1f; 举例&#xff1a; 里程碑的例子&#xff1a;比如需求评审&#xff…

计算机SCI论文润色,更容易被录用吗? - 易智编译EaseEditing

SCI论文润色后投稿&#xff0c;相较于没有润色投稿&#xff0c;确实概率要大一些。 特别有些英语水平较差的作者&#xff0c;投稿的论文&#xff0c;没有润色之前&#xff0c;可能审稿人会不理解&#xff0c;或读不通。 润色后&#xff0c;可能帮助审稿人理解论文。 目前不少…

告别动态规划

一、动态规划的三大步骤 动态规划&#xff0c;无非就是利用历史记录&#xff0c;来避免我们的重复计算。而这些历史记录&#xff0c;我们得需要一些变量来保存&#xff0c;一般是用一维数组或者二维数组来保存。下面我们先来讲下做动态规划题很重要的三个步骤 第一步骤&#…

九、树结构基础

1、为什么需要树这种数据结构&#xff1f; 数组存储方式的分析 优点&#xff1a;通过下标方式访问元素&#xff0c;速度快。对于有序数组&#xff0c;还可使用二分查找提高检索速度。 缺点&#xff1a;如果要检索具体某个值&#xff0c;或者插入值(按一定顺序)会整体移动&…

简单BGP实验

&#xff08;我这里AS1 BGP为100 AS2BGP为200 AS3 BGP为300&#xff09; 这里已经配置好ip环回&#xff08;这里tcp使用的是rip也已经提前配置好&#xff09; 首先先配置AS2中的ibgp [r2]bgp 200 [r2-bgp]router-id 2.2.2.2 [r2-bgp]peer 4.4.4.4 as-number 100 [r2-bgp]…

白话初识UDP协议

UDP是什么 UDP是一个传输层的数据包装协议&#xff0c;特点有&#xff1a; 无连接&#xff08;不需要两端连通就可以发送消息给接收端&#xff09;不可靠传输&#xff08;无法知道数据是否送达&#xff09;面向数据报&#xff08;以数据报为数据传输单位&#xff09;全双工&…

vue项目cli2 升级 cli3

vue-cli2 升级 vue-cli3 由于vue-cli2 版本太低&#xff0c;升级到vue-cli3 采用的方法是新建cli3项目&#xff0c;对比差异&#xff0c;在cli2项目单独升级 检查环境 node环境检查版本是否在8.11版本以上 npm uninstall vue-cli -g 卸载旧版本的vue-cli npm install -g vue/…

广州蓝景分享—VSCode 前端开发必备插件推荐

Hello~~各位小伙伴好&#xff0c;今天广州蓝景整理一波前端开发必备插件&#xff0c;绝对可以提高你的生产力&#xff0c;希望这篇文章对大家有所帮助。 开发综合推荐 别名路径跳转 插件名&#xff1a;别名路径跳转 使用说明: 别名路径跳转插件&#xff0c;支持任何项目&…

BrightlyPro - 照片视屏后期自动调色增亮工具

BrightlyPro - 照片视屏后期自动调色增亮工具 BrightlyPro 自动增强您的照片和视频&#xff0c;你最需要它。通过一个滑块&#xff0c;BrightlyPro 立即使您的照片自适应地亮起来&#xff0c;同时保持光影之间令人愉悦的平衡&#xff0c;而不会扭曲已经点亮的区域。 下载 ➤ …

day07 面向对象

1. 类和对象 **面向对象和面向过程的思想对比 : ** ​ **面向过程 &#xff1a;**是一种以过程为中心的编程思想&#xff0c;实现功能的每一步&#xff0c;都是自己实现的 ​ **面向对象 &#xff1a;**是一种以对象为中心的编程思想&#xff0c;通过指挥对象实现具体的功能…

Java开发学习(三十四)----Maven私服(二)本地仓库访问私服配置与私服资源上传下载

一、本地仓库访问私服配置 我们通过IDEA将开发的模块上传到私服&#xff0c;中间是要经过本地Maven的 本地Maven需要知道私服的访问地址以及私服访问的用户名和密码 私服中的仓库很多&#xff0c;Maven最终要把资源上传到哪个仓库? Maven下载的时候&#xff0c;又需要携带用…

Nginx限制每秒请求次数,限制每秒连接次数,下载速度限制

Hi I’m Shendi 为了防止网站被恶意攻击&#xff0c;总是需要做一些防护措施 最外层的web服务器是Nginx&#xff0c;于是寻找 nginx 的一些关于防护措施的配置&#xff0c;记录在此 https://sdpro.top/blog/html/article/1005.html 一些变量 首先列举出会使用到的一些变量 …

XXEXML漏洞详解

XXE&XML漏洞详解1.XML基础1.1.XML基础介绍1.2.XML文档结构1.3.DTD介绍1.4.实体类介绍1.5.外部实体2.XXE漏洞基本介绍2.1.XXE漏洞基本概念2.2.XXE漏洞危害2.3.XXE漏洞输出形式2.4.XXE漏洞相关差异3.XXE漏洞测试3.1.有回显测试3.1.1.有回显案例3.1.1.1.测试回显位置3.1.1.2.构…

【XR】Oculus揭秘Touch控制器追踪技术:LED 匹配

Oculus揭秘Touch控制器追踪技术&#xff1a;LED 匹配 稳健的状态机&#xff0c;它允许我们能够在各种求解器之间进行转换 &#xff08;映维网 2019年11月05日&#xff09;映维网曾在九月分享了一篇关于Oculus Constellation追踪系统&#xff0c;其中负责AR/VR设备输入追踪的F…

动态规划——区间dp

区间dp 文章目录区间dp概述模板例题石子合并思路代码环形石子合并思路代码能量项链思路代码加分二叉树思路代码凸多边形的划分思路代码棋盘分割思路代码总结概述 区间dp就是在区间上进行动态规划&#xff0c;求解一段区间上的最优解。主要是通过合并小区间的最优解进而得出整个…

Infragistics Ignite UI for Web

Infragistics Ignite UI for Web 添加了新的透视网格组件&#xff0c;它在透视表中显示数据&#xff0c;并帮助对数据集执行复杂的分析。 使用透视网格以多维透视表结构配置和显示数据。 行和列表示不同的数据组&#xff0c;数据单元值表示聚合。 这允许基于简单的平面数据集进…

Sql Server 中FOR XML PATH(‘‘)函数用法

1. 如下表 2. 转换xml -- 将查询结果转换成xml形式 select name from zz FOR XML PATH(root);结果 说明 FOR XML PATH(‘root’)中root是xml根节点&#xff0c;所以在结果集中每条数据根节点为root&#xff0c;上面的sql将多行数据合并为一行&#xff0c;并且使用属性名name…