Linux系统驱动(九)IO模型---异步通知IO模型

news2024/9/24 4:23:48

文章目录

  • 一、概念
  • 二、异步通知IO模型驱动实现
    • (一)异步通知IO模型实现
      • 1. fcntl(fd,F_GETFL)调用流程
      • 2. fcntl(fd,F_SETFL,flags|FASYNC)
      • 3. fcntl(fd,F_SETOWN,getpid())
    • (二)驱动层提供异步通知模型
      • 1. 驱动层中实现异步通知IO模型对应的函数指针
      • 2. 驱动层使用示例
      • 3. 关于fasync_helper函数
  • 三、实现区分用户发来的信号
    • (一)sigaction函数
    • (二)实现示例

一、概念

当硬件设备的数据就绪时,会产生中断,然后在中断处理函数中给上层的进程发送信号SIGIO(29),进程收到信号时执行信号处理函数,进程没有收到信号时可以执行其他代码。

异步通知:信号的发送和进程的执行是异步的

二、异步通知IO模型驱动实现

(一)异步通知IO模型实现

#include <my_head.h>

void my_handler(int signum){
    printf("This is my_handler\n");
}

int main(int argc, char const *argv[])
{
    int fd=0;
	//此处打开的是键盘的驱动文件
    if(-1 == (fd = open("/dev/input/event1",O_RDWR))){
        //打开文件失败
        ERR_LOG("open error");
    }

    //第一步:注册信号
    if(SIG_ERR == signal(SIGIO,my_handler))
        ERR_LOG("signal error");

    //第二步:调用底层的fasync函数
    unsigned int flags = fcntl(fd,F_GETFL);
    fcntl(fd,F_SETFL,flags|FASYNC);

    //第三步:告诉驱动,该进程可以接收信号
    fcntl(fd,F_SETOWN,getpid());
    while(1){
        //此时程序可以执行其他操作
        //当收到信号后触发信号处理函数
    }
    return 0;
}

1. fcntl(fd,F_GETFL)调用流程

US:
 unsigned int flags = fcntl(fd,F_GETFL)
---------------------------------------------
KS:  vi -t sys_fcntl
SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
	==>err = do_fcntl(fd, cmd, arg, f.file); 
		==>
        	switch (cmd) {  
             	case F_GETFL:
                err = filp->f_flags;
          		break;}
   return err;
  • 注:操作为F_GETFL时,本质是将struct file结构体中的f_flags返回

  • 注:此处使用的 vi -t 命令需要安装exuberant-ctags软件包,执行命令sudo apt-get install exuberant-ctags安装

2. fcntl(fd,F_SETFL,flags|FASYNC)

US:
 fcntl(fd,F_SETFL,flags|FASYNC);
-------------------------------------------------------------------
KS:  vi -t sys_fcntl
SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
	==>err = do_fcntl(fd, cmd, arg, f.file); 
		==>switch (cmd) {  
            	case F_SETFL:
                err = setfl(fd, filp, arg);
                	==>  //arg = filp->f_flags | FASYNC;
                    	if (((arg ^ filp->f_flags) & FASYNC) && filp->f_op->fasync) {
                        	//调用驱动的fasync函数执行
                        	error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
                    	}
                    	break;   
-------------------------------------------------------------------
int mycdev_fasync(int fd, struct file *filp, int on)
{ }
  • 注:当操作为F_SETFL时,会执行arg^filp->flags操作,执行结果最后会剩下FASYNC标志位,此时与FASYNC相与,如果为真,则说明置位了FASYNC标志位,进而通过struct file结构体找到驱动中的fasync函数

3. fcntl(fd,F_SETOWN,getpid())

US:
 fcntl(fd,F_SETOWN,getpid())
-------------------------------------------------------------------
KS:  vi -t sys_fcntl
SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
	==>err = do_fcntl(fd, cmd, arg, f.file); 
		==>switch (cmd) {  
           		case F_SETOWN:  
             		err = f_setown(filp, arg, 1);
                		==>__f_setown(filp, pid, type, force);
     						filp->f_owner.pid = get_pid(pid);
            		break;
  • 注:操作为F_SETOWN时,本质就是将pid写入到struct file结构体中的f_owner中的pid
struct file {struct fown_struct	f_owner;}
		|
struct fown_struct{	struct pid *pid;/* pid or -pgrp where SIGIO should be sent */};

(二)驱动层提供异步通知模型

1. 驱动层中实现异步通知IO模型对应的函数指针

struct file_operations {
	int (*fasync) (int, struct file *, int);
}

2. 驱动层使用示例

//定义异步通知队列头
struct fasync_struct * my_fasync=NULL;

ssize_t mycdev_write(struct file* file,
    const char __user* ubuf, size_t size, loff_t* offs)
{
    int ret;
    memset(kbuf, 0, sizeof(kbuf));
    if (size > sizeof(kbuf))
        size = sizeof(kbuf);
    ret = copy_from_user(kbuf, ubuf, size);
    if (ret) {
        pr_err("copy_from_user error\n");
        return -EIO;
    }
    cond=1;
    //此示例中仍采用,当调用write时,发送信号给驱动的方式
    kill_fasync(&my_fasync,SIGIO,POLL_IN);
    return size;
}

int mycdev_fasync(int fd, struct file * filp, int on){
    return fasync_helper(fd,filp,on,&my_fasync);
}

const struct file_operations myfops={
    .open=mycdev_open,
    .release=mycdev_close,
    .write=mycdev_write,
    .read=mycdev_read,
    .poll=mycdev_poll,
    .fasync=mycdev_fasync, //指定fasync函数指针指向的函数
};

3. 关于fasync_helper函数

int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp){	return fasync_add_entry(fd, filp, fapp);}
==>
static int fasync_add_entry(int fd, struct file *filp, struct fasync_struct **fapp)
{
	new = fasync_alloc(); //动态分配内存
	if (fasync_insert_entry(fd, filp, fapp, new)) 
}
==>
struct fasync_struct *fasync_insert_entry(int fd, struct file *filp, struct fasync_struct **fapp, struct fasync_struct *new)
{
	1.判断*fapp是否为NULL,不为NULL则说明new不是队列头,采用类似头插的操作
	2.初始化new的值,包括fd和filp
	3.返回队列头
}
  • 注:即用户使用时需要定义一个struct fasync_struct * 的变量,用来保存函数回传的异步通知队列头。

三、实现区分用户发来的信号

使用上述方法,无论收到的是POLL_IN信号还是POLL_OUT信号都会触发中断。
如果想要区分POLL_IN信号和POLL_OUT信号,需要更改应用程序,使用sigaction函数

(一)sigaction函数

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
功能:获取或者设置信号的属性
参数:
    signum:信号的编号 除了 SIGKILL 和 SIGSTOP
    act:信号新的属性
    oldact:信号之前的属性
返回值:
    成功 0
    失败 -1 重置错误码

struct sigaction {
    void     (*sa_handler)(int);//信号处理函数
    void     (*sa_sigaction)(int, siginfo_t *, void *);//信号处理函数
    sigset_t   sa_mask;//关于阻塞的掩码
    int        sa_flags;//属性标志位
        SA_RESTART  表示信号的自重启属性,关闭自重启属性后 被中断的系统调用会失败 错误码为 EINTR
        SA_SIGINFO	如果使用第2个信号处理函数时,需要置位这个属性
    void     (*sa_restorer)(void);
};

(二)实现示例

test.c

#include <my_head.h>

void my_handler(int signum, siginfo_t *info, void *arg){
    printf("This is my_handler\n");
    if(signum == SIGIO){
        switch (info->si_code)
        {
        case POLL_IN:
            printf("The signal is POLL_IN\n");
            break;
        
        case POLL_OUT:
            printf("The signal is POLL_OUT\n");
            break;
        }
    }
}

int main(int argc, char const *argv[])
{
    int fd=0;

    if(-1 == (fd = open("/dev/mycdev",O_RDWR))){
        //打开文件失败
        ERR_LOG("open error");
    }

    //第一步:注册信号
    fcntl(fd,__F_SETSIG,SIGIO); //让内核空间的数据拷贝到用户空间
    struct sigaction action={ //定义结构体
        .sa_sigaction = my_handler, //信号处理函数
        .sa_flags = SA_SIGINFO, //使用第二个信号处理函数必须置位这个
    };
    if(sigaction(SIGIO,&action,NULL))
        ERR_LOG("signal error");

    //第二步:调用底层的fasync函数
    unsigned int flags = fcntl(fd,F_GETFL);
    fcntl(fd,F_SETFL,flags|FASYNC);

    //第三步:告诉驱动,该进程可以接收信号
    fcntl(fd,F_SETOWN,getpid());
    while(1){
        //此时程序可以执行其他操作
        //当收到信号后触发信号处理函数
    }
    return 0;
}

在这里插入图片描述

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

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

相关文章

基于QT实现的简易WPS(已开源)

一、开发工具及开源地址&#xff1a; 开发工具&#xff1a;QTCreator &#xff0c;QT 5 开源地址&#xff1a; GitHub - Whale-xh/WPS_official: Simple WPS based on QTSimple WPS based on QT. Contribute to Whale-xh/WPS_official development by creating an acc…

【JavaEE初阶】定时器

&#x1f4d5; 引言 定时器是什么&#xff1f; 定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就执行某个指定好的代码 定时器是一种实际开发中非常常用的组件.比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连.比如…

CTFHUB-web-RCE-过滤目录分隔符

开启题目 从源码发现管道符被过滤&#xff0c;使用分号 &#xff1b;分隔符&#xff0c;拼接执行注入&#xff0c;发现了 flag 的可疑文件 127.0.0.1;ls 查看 flag_is_here&#xff0c;发现有一个flag_12069946857.php 127.0.0.1;ls flag_is_here 查看flag_12069946857.php&a…

react项目中使用redux和reduxjs/toolkit案例

1、安装依赖 npm i react-redux reduxjs/toolkit2、在store/modules文件夹中新建todo.js &#xff08;billSlice.js&#xff09; // 账单列表 import { createSlice } from reduxjs/toolkit import axios from axiosconst billStore createSlice({name: billStore,// 数据状态…

零基础学习Python(五)

1. 数据描述符与非数据描述符 首先&#xff0c;描述符只能作用于类属性&#xff0c;如果将描述符作用于对象属性&#xff0c;则不会生效。 class D:def __get__(self, instance, owner):print("~get")class C:def __init__(self):self.x D() 应该将D对象赋值给类C…

72.树形列表绑定对应的右键菜单

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;易道云信息技术研究院 上一个内容&#xff1a;71.对象数据存储设计 以 71.对象数据存储设计 它的代码为基础进行修改 效果图&#xf…

后端常见问题及深度解决方案

&#x1f41f;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢编程&#x1fab4; &#x1f421;&#x1f419;个人主页&#x1f947;&#xff1a;Aic山鱼 &#x1f420;WeChat&#xff1a;z7010cyy &#x1f988;系列专栏&#xff1a;&#x1f3de;️ 前端-JS基础专栏✨前…

linux脚本: 检测多个服务端口是否处于侦听状态或监听状态

目录 一、背景 1、系统监控和健康检查 2、安全性评估 3、故障排查 4、合规性检查 5、资源管理 6、服务依赖性检查 二、需求和分析 1、需求 2、分析 三、脚本文件 1. 创建脚本文件 2. 编写脚本代码 3、代码解释 &#xff08;1&#xff09;定义要检查的端口列表 …

视觉全能!自回归要反超扩散?Lumina-mGPT:任意分辨率丝滑逼真图像生成(上海AI Lab)

文章链接&#xff1a;https://arxiv.org/pdf/2408.02657 git链接&#xff1a;https://github.com/Alpha-VLLM/Lumina-mGPT 亮点直击 通过多模态生成预训练的自回归Transformer&#xff0c;而不是从头训练&#xff0c;可以作为逼真的文本到图像生成和视觉与语言任务统一的有效初…

Linux shell编程:监控进程CPU使用率并使用 perf 抓取高CPU进程信息

0. 概要 本文将介绍一个用于监控一组进程CPU使用率的Shell脚本&#xff0c;&#xff0c;当检测到某进程的CPU使用率超出阈值时&#xff0c;使用 perf 工具抓取该进程的详细信息。 本shell脚本为了能在普通嵌入式系统上运行做了妥协和优化。 1. shell脚本流程的简要图示&#…

Spring 中请求作用域的数据存储在 ThreadLocal 中还是 Spring 容器中?

微信中阅读,欢迎👏👏👏关注公众号:CodeFit 。 创作不易,如果你觉得这篇文章对您有帮助,请不要忘了 点赞、分享 和 关注,为我的 持续创作 提供 动力! 最近看到一个有趣的问题,Request Scope(请求作用域) 的数据是存储在 ThreadLocal 中,还是 Spring 容器中? 事…

前端(六):Vue组件库Element

一、引入 Element&#xff1a;是饿了吗团队研发&#xff0c;一套为开发者、设计师和产品经理准备的基于vue2.0的桌面端组件库。组件&#xff1a;组成网页的部件&#xff0c;例如超链接、按钮、图片、表格、表单、分页条等。官网&#xff1a;https://element.eleme.cn/#/zh-CN …

景芯SoC DDR子系统

Memory子系统主要由DDR Controller和DDR_PHY(含DDR_IO)两个部分组成。DDR Controller主要承担其它子系统&#xff08;如CPU&#xff09;与Memory子系统进行数据交互时的传输效率及调度&#xff0c;DDR_PHY主要负责数据交互过程中的传输速度。 DDR内存接口IP解决方案包括DDR控制…

Revit二次开发选择过滤器,SelectionFilter

过滤器分为选择过滤器与规则过滤器 规则过滤器可以看我之前写的这一篇文章: Revit二次开发在项目中给链接模型附加过滤器 选择过滤器顾名思义就是可以将选择的构件ID集合传入并加入到视图过滤器中,有一些场景需要对某些构件进行过滤选择,但是没有共同的逻辑规则进行筛选的情况…

健康管理系统

目录 第1章 系统概述 第2章 可行性研究 2.1 项目背景及意义 2.2 可行性研究 第3章 需求分析 3.1 功能性需求 3.2 非功能性需求 3.2.1 性能需求 第4章 总体设计 4.1 技术架构 4.2功能模块设计 第5章 详细设计 5.1 主页 5.2 写剧本杀 5.3 剧本杀分类管理 5.4 个人…

PointNet: Deep Learning on Point Sets for 3D Classification and Segmentation

Abstract 通常情况下研究人员会把点云数据转换为规则的3D体素网格或图像集合。这导致数据不必要的庞大&#xff0c;所以本文引入了一种新型的神经网络&#xff0c;能很好的尊重点云的排列不变性&#xff0c;名称是pointnet&#xff0c;并且能够应用于分类、分割、场景解析等下…

大模型入门无敌!《大模型基础》教材发布,已开源!

浙江大学DAILY实验室毛玉仁研究员、高云君教授领衔撰写的《大模型基础》教材第一版付梓。 本书旨在针对大语言模型感兴趣的读者系统地讲解相关基础知识、介绍前沿技术。作者团队将认真严肃开源社区以及广大专家学者的建议&#xff0c;持续进行月度更新&#xff0c;致力打造易读…

SQLServer Manager Studio扩展开发从入门到弃坑(针对17 ,18 。19)

Visualstudio的已经开发好了 可以在这里找到。 (如果低版本不适用&#xff0c;那么&#xff0c;我还要重新下载老版本vs开发一版) image.png image.png &#xff0c;可这个就是不行&#xff0c;直接运行点这些按钮加载失败&#xff0c;而我直接不调试模式&#xff0c;则直接什么…

【运维类】智慧运维系统建设方案(PPT原件完整版)

建设方案目录&#xff1a; 1、智慧运维系统建设背景 2、智慧运维系统建设目标 3、智慧运维系统建设内容 4、智慧运维系统建设技术 5、智慧运维系统建设流程 6、智慧运维系统建设收益 企业对运维管理的需求&#xff1a; 1、提高运维效率&#xff1a;降低运维成本&#xff0c;提高…

我定制了一个属于自己的录屏软件

相信很多朋友都用过不同软件的录屏功能&#xff0c;但是这些软件多少还让存在一些缺点&#xff0c;让我们在录屏的时候不能得心应手。 今天我就来手把手教大家自己来制作一个私人订制的浏览器录屏器&#xff0c; 录屏器 我们可以使用浏览器的屏幕捕获API接口来帮助我们来捕获…