异步通知实验学习

异步通知实验

Linux 应用程序可以通过阻塞或者非阻塞这两种方式来访问驱动设备,通过阻塞方式访问的话应用程序会处于休眠态,等待驱动设备可以使用,非阻塞方式的话会通过 poll 函数来不断的轮询.查看驱动设备文件是否可以使用。这两种方式都需要应用程序主动的去查询设备的使用情况,
“信号”为此应运而生,信号类似于我们硬件上使用的“中断”,只不过信号是软件层次上的。算是在软件层次上对中断的一种模拟,驱动可以通过主动向应用程序发送信号的方式来报告自己可以访问了,应用程序获取到信号以后就可以从驱动设备中读取或者写入数据了。整个过程就相当于应用程序收到了驱动发送过来了的一个中断,然后应用程序去响应这个中断,在整个处理过程中应用程序并没有去查询驱动设备是否可以访问,一切都是由驱动设备自己告诉给应用程序的。
异步通知的核心就是信号,在 arch/xtensa/include/uapi/asm/signal.h 文件中定义了 Linux 所支
持的所有信号,这些信号如下所示:

示例代码 53.1.1.1 Linux 信号
34 #define SIGHUP 1 /* 终端挂起或控制进程终止 */
35 #define SIGINT 2 /* 终端中断(Ctrl+C 组合键) */
36 #define SIGQUIT 3 /* 终端退出(Ctrl+\组合键) */
37 #define SIGILL 4 /* 非法指令 */
38 #define SIGTRAP 5 /* debug 使用,有断点指令产生 */
39 #define SIGABRT 6 /* 由 abort(3)发出的退出指令 */
40 #define SIGIOT 6 /* IOT 指令 */
41 #define SIGBUS 7 /* 总线错误 */
42 #define SIGFPE 8 /* 浮点运算错误 */
43 #define SIGKILL 9 /* 杀死、终止进程 */
44 #define SIGUSR1 10 /* 用户自定义信号 1 */
45 #define SIGSEGV 11 /* 段违例(无效的内存段) */
46 #define SIGUSR2 12 /* 用户自定义信号 2 */
47 #define SIGPIPE 13 /* 向非读管道写入数据 */
48 #define SIGALRM 14 /* 闹钟 */
49 #define SIGTERM 15 /* 软件终止 */
50 #define SIGSTKFLT 16 /* 栈异常 */
51 #define SIGCHLD 17 /* 子进程结束 */
52 #define SIGCONT 18 /* 进程继续 */
53 #define SIGSTOP 19 /* 停止进程的执行,只是暂停 */
54 #define SIGTSTP 20 /* 停止进程的运行(Ctrl+Z 组合键) */
55 #define SIGTTIN 21 /* 后台进程需要从终端读取数据 */
56 #define SIGTTOU 22 /* 后台进程需要向终端写数据 */
57 #define SIGURG 23 /* 有"紧急"数据 */
58 #define SIGXCPU 24 /* 超过 CPU 资源限制 */
59 #define SIGXFSZ 25 /* 文件大小超额 */
60 #define SIGVTALRM 26 /* 虚拟时钟信号 */
61 #define SIGPROF 27 /* 时钟信号描述 */
62 #define SIGWINCH 28 /* 窗口大小改变 */
63 #define SIGIO 29 /* 可以进行输入/输出操作 */
64 #define SIGPOLL SIGIO
65 /* #define SIGLOS 29 */
66 #define SIGPWR 30 /* 断点重启 */
67 #define SIGSYS 31 /* 非法的系统调用 */
68 #define SIGUNUSED 31 /* 未使用信号 */

在 Linux 和其他类 Unix 系统中,信号是一种进程间通信机制,用于通知进程某些事件的发生。大多数信号可以被进程捕获、忽略或默认处理,但有两个特殊的信号 —— SIGKILLSIGSTOP —— 不能被进程忽略或捕获。这两个信号的特殊性主要基于它们在系统中的作用和设计目的:

SIGKILL (信号 9)

  • 目的: 强制终止进程。
  • 特性: SIGKILL 信号用于立即终止进程,不给进程任何清理或保存数据的机会。系统管理员和用户可以使用这个信号来终止那些无响应或不能正常关闭的进程。
  • 为什么不能被忽略或捕获: 如果进程能够忽略或捕获 SIGKILL,它们可能会阻止自身被终止,从而导致僵尸进程或无法控制的进程。这会使系统管理员难以管理系统,尤其是在进程不响应其他终止尝试时。

SIGSTOP (信号 19)

  • 目的: 暂停进程的执行。
  • 特性: SIGSTOP 信号用于暂停(停止)进程的执行,直到收到 SIGCONT 信号。这对于调试和系统管理非常有用。
  • 为什么不能被忽略或捕获: 如果进程能够忽略或捕获 SIGSTOP,它们可能会继续执行,即使系统管理员或操作系统需要它们停止。这样的设计确保了系统管理员可以可靠地控制进程的执行,包括必要时暂停进程。
    使用中断的时候需要设置中断处理函数,同样的,如果要在应用程序中使用信号,那么就必须设置信号所使用的信号处理函数,在应用程序中使用 signal 函数来设置指定信号的处理函数, signal 函数原型如下所示:
sighandler_t signal(int signum, sighandler_t handler)
函数参数和返回值含义如下:
signum:要设置处理函数的信号。
handler: 信号的处理函数。
返回值: 设置成功的话返回信号的前一个处理函数,设置失败的话返回 SIG_ERR

信号处理函数原型如下所示:

typedef void (*sighandler_t)(int)

我们前面讲解的使用“ kill -9 PID”杀死指定进程的方法就是向指定的进程(PID)发送SIGKILL 这个信号。当按下键盘上的 CTRL+C 组合键以后会向当前正在占用终端的应用程序发出 SIGINT 信号, SIGINT 信号默认的动作是关闭当前应用程序。这里我们修改一下 SIGINT 信号的默认处理函数,当按下 CTRL+C 组合键以后先在终端上打印出“SIGINT signal!”这行字符串,然后再关闭当前应用程序。

驱动中的信号处理

1、 fasync_struct 结构体

首先我们需要在驱动程序中定义一个 fasync_struct 结构体指针变量, fasync_struct 结构体内容如下:

示例代码 53.1.2.1 fasync_struct 发结构体
struct fasync_struct {spinlock_t fa_lock;int magic;int fa_fd;struct fasync_struct *fa_next;struct file *fa_file;struct rcu_head fa_rcu;
};

一般将 fasync_struct 结构体指针变量定义到设备结构体中,比如在上一章节的 imx6uirq_dev结构体中添加一个 fasync_struct 结构体指针变量,结果如下所示:struct fasync_struct *async_queue; /* 异步相关结构体 */
如果要使用异步通知,需要在设备驱动中实现 file_operations 操作集中的 fasync 函数,此函数格式如下所示:

int (*fasync) (int fd, struct file *filp, int on)

fasync 函数里面一般通过调用 fasync_helper 函数来初始化前面定义的 fasync_struct 结构体指针, fasync_helper 函数原型如下:

int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)

fasync_helper 函数的前三个参数就是 fasync 函数的那三个参数,第四个参数就是要初始化的 fasync_struct 结构体指针变量。当应用程序通过“fcntl(fd, F_SETFL, flags | FASYNC)”改变fasync 标记的时候,驱动程序 file_operations 操作集中的 fasync 函数就会执行:
驱动程序中的 fasync 函数参考示例如下:

示例代码 53.1.2.3 驱动中 fasync 函数参考示例
1 struct xxx_dev {
2 ......
3 struct fasync_struct *async_queue; /* 异步相关结构体 */
4 };
5 6
static int xxx_fasync(int fd, struct file *filp, int on)
7 {
8 struct xxx_dev *dev = (xxx_dev)filp->private_data;
9
10 if (fasync_helper(fd, filp, on, &dev->async_queue) < 0)
11 return -EIO;
12 return 0;
13 }
14
15 static struct file_operations xxx_ops = {
16 ......
17 .fasync = xxx_fasync,
18 ......
19 };

在关闭驱动文件的时候需要在 file_operations 操作集中的 release 函数中释放 fasync_struct,fasync_struct 的释放函数同样为 fasync_helper, release 函数参数参考实例如下:

示例代码 53.1.2.4 释放 fasync_struct 参考示例
1 static int xxx_release(struct inode *inode, struct file *filp)
2 {
3 return xxx_fasync(-1, filp, 0); /* 删除异步通知 */
4 }
kill_fasync 函数

当设备可以访问的时候,驱动程序需要向应用程序发出信号,相当于产生“中断”。 kill_fasync
函数负责发送指定的信号, kill_fasync 函数原型如下所示:

void kill_fasync(struct fasync_struct **fp, int sig, int band)
函数参数和返回值含义如下:
fp:要操作的 fasync_struct。
sig: 要发送的信号。
band: 可读时设置为 POLL_IN,可写时设置为 POLL_OUT。
返回值: 无。

应用程序对异步通知的处理

应用程序对异步通知的处理:

1、注册信号处理函数

应用程序根据驱动程序所使用的信号来设置信号的处理函数,应用程序使用 signal 函数来
设置信号的处理函数。前面已经详细的讲过了,这里就不细讲了。

2、将本应用程序的进程号告诉给内核

使用 fcntl(fd, F_SETOWN, getpid())将本应用程序的进程号告诉给内核。

3、开启异步通知

使用如下两行程序开启异步通知:

flags = fcntl(fd, F_GETFL); /* 获取当前的进程状态 */
fcntl(fd, F_SETFL, flags | FASYNC); /* 开启当前进程异步通知功能 */

重点就是通过 fcntl 函数设置进程状态为 FASYNC,经过这一步,驱动程序中的 fasync 函数就会执行:

驱动程序编写

先要在设备结构体中添加

struct imx6uirq_dev{dev_t devid;			/* 设备号 	 */	struct cdev cdev;		/* cdev 	*/                 struct class *class;	/* 类 		*/struct device *device;	/* 设备 	 */int major;				/* 主设备号	  */int minor;				/* 次设备号   */struct device_node	*nd; /* 设备节点 */	atomic_t keyvalue;		/* 有效的按键键值 */atomic_t releasekey;	/* 标记是否完成一次完成的按键,包括按下和释放 */struct timer_list timer;/* 定义一个定时器*/struct irq_keydesc irqkeydesc[KEY_NUM];	/* 按键init述数组 */unsigned char curkeynum;				/* 当前init按键号 */wait_queue_head_t r_wait;				/* 读等待队列头 */struct fasync_struct *async_queue;		/* 异步相关结构体 */
};

接下来就是处理异步通知函数:

/** @description     : fasync函数,用于处理异步通知* @param - fd		: 文件描述符* @param - filp    : 要打开的设备文件(文件描述符)* @param - on      : 模式* @return          : 负数表示函数执行失败*/
static int imx6uirq_fasync(int fd, struct file *filp, int on)
{struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;return fasync_helper(fd, filp, on, &dev->async_queue);
}
/** @description     : release函数,应用程序调用close关闭驱动文件的时候会执行* @param - inode	: inode节点* @param - filp    : 要打开的设备文件(文件描述符)* @return          : 负数表示函数执行失败*/
static int imx6uirq_release(struct inode *inode, struct file *filp)
{return imx6uirq_fasync(-1, filp, 0);
}

设备操作函数也要将这两个函数绑定:

/* 设备操作函数 */
static struct file_operations imx6uirq_fops = {.owner = THIS_MODULE,.open = imx6uirq_open,.read = imx6uirq_read,.poll = imx6uirq_poll,.fasync = imx6uirq_fasync,.release = imx6uirq_release,
};

接下来是测试APP函数:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "poll.h"
#include "sys/select.h"
#include "sys/time.h"
#include "linux/ioctl.h"
#include "signal.h"
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名		: asyncnotiApp.c
作者	  	: 左忠凯
版本	   	: V1.0
描述	   	: 异步通知测试APP
其他	   	: 无
使用方法	:./asyncnotiApp /dev/asyncnoti 打开测试App
论坛 	   	: www.openedv.com
日志	   	: 初版V1.0 2019/8/13 左忠凯创建
***************************************************************/static int fd = 0;	/* 文件描述符 *//** SIGIO信号处理函数* @param - signum 	: 信号值* @return 			: 无*/
static void sigio_signal_func(int signum)
{int err = 0;unsigned int keyvalue = 0;err = read(fd, &keyvalue, sizeof(keyvalue));if(err < 0) {/* 读取错误 */} else {printf("sigio signal! key value=%d\r\n", keyvalue);}
}/** @description		: main主程序* @param - argc 	: argv数组元素个数* @param - argv 	: 具体参数* @return 			: 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int flags = 0;char *filename;if (argc != 2) {printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR);if (fd < 0) {printf("Can't open file %s\r\n", filename);return -1;}/* 设置信号SIGIO的处理函数 */signal(SIGIO, sigio_signal_func);fcntl(fd, F_SETOWN, getpid());		/* 设置当前进程接收SIGIO信号 	*/flags = fcntl(fd, F_GETFL);			/* 获取当前的进程状态 			*/fcntl(fd, F_SETFL, flags | FASYNC);	/* 设置进程启用异步通知功能 	*/	while(1) {sleep(2);}close(fd);return 0;
}

编译测试即可。

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

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

相关文章

储能电池竞争出海分析

锂电池的激烈竞争进一步蔓延到储能行业。为保市场份额和现金流稳定&#xff0c;不少储能电池企业都开始大幅度降低报价只求中标储能项目。 随着6月的储能电芯的最高限价和系统报价都已经贴近成本价&#xff0c;一二三线的储能电池厂商将要如何应对&#xff1f; 1、储能规模快速…

三生随记——诡异的牙线

在小镇的角落&#xff0c;坐落着一间古老的牙医诊所。这所诊所早已荒废多年&#xff0c;窗户上爬满了藤蔓&#xff0c;门板上的油漆斑驳脱落&#xff0c;仿佛诉说着无尽的沉寂与孤独。然而&#xff0c;在午夜时分&#xff0c;偶尔会有低沉的呻吟声从紧闭的诊所里传出&#xff0…

Centos Stream9 和Centos Stream10的下载网址

Index of /https://mirror.stream.centos.org/

“迷你剧场新体验:探索短剧小程序系统的魅力!“

在移动互联网的时代背景下&#xff0c;短视频和短剧已经成为人们娱乐消费的新宠。短剧小程序系统&#xff0c;以其独特的互动形式、便捷的观看体验和丰富的内容生态&#xff0c;为迷你剧场带来了全新的体验。接下来将探讨这个系统的魅力所在&#xff0c;以及它如何改变着人们的…

ESP32-C3模组上跑通NVS(8)

接前一篇文章:ESP32-C3模组上跑通NVS(7) 上一回讲解了乐鑫技术支持提供的例程代码中的主函数app_main()。主函数在调用nvs_flash_init_partition函数,进行了test分区的初始化之后,接下来调用了save_blob_test函数。save_blob_test函数在同文件中,代码如下: esp_err_t s…

红队内网攻防渗透:内网渗透之内网对抗:隧道技术篇防火墙组策略HTTP反向SSH转发出网穿透CrossC2解决方案

红队内网攻防渗透 1. 内网隧道技术1.1 HTTP隧道搭建内网穿透:(解决目标出站限制做信息收集)1.1.1 项目Neo-reGeorg1.1.2 HTTP隧道搭建上线C2:(解决目标出站限制上线C2)1.1.3 哥斯拉&冰蝎自带http上线1.1.3.1 冰蝎1.1.3.2 哥斯拉1.1.3.3 CS上线http隧道1.1.3.3.1 reve…

二维码美化,修改二维码颜色样式,添加logo,文字描述

在如今的社会&#xff0c;二维码已成为我们日常生活中不可或缺的一部分&#xff0c;无论是支付、访问网站、社交媒体互动&#xff0c;还是分享信息&#xff0c;二维码都扮演着重要角色。然而&#xff0c;标准的黑白二维码有时可能显得过于单调&#xff0c;缺乏吸引力。为了提升…

EasyRecovery数据恢复软件2024免费版下载

EasyRecovery数据恢复软件&#xff0c;是我在电脑使用过程中遇到的神器&#xff01;它不仅功能强大&#xff0c;操作简便&#xff0c;还帮我找回了丢失的重要文件。今天&#xff0c;我就来给大家分享一下我的使用体验和心得。 让我来介绍一下EasyRecovery的功能。这款软件可以恢…

你只是重新发现了一些东西

指北君关于另外一条思维路径的发现。 "自以为是"的顿悟时刻 有很多时候&#xff0c;我会"自以为是"的发现/发明一些东西。这种"自以为是"的时刻通常还带有一些骄傲自豪的情绪。这种感觉特别像古希腊博学家阿基米德 在苦思冥想如何测量不规则物体…

数据库管理-第210期 HaloDB-Oracle兼容性测试02(20240622)

数据库管理209期 2024-06-22 数据库管理-第210期 HaloDB-Oracle兼容性测试02&#xff08;20240622&#xff09;1 表增加列2 约束3 自增列4 虚拟列5 表注释6 truncat表总结 数据库管理-第210期 HaloDB-Oracle兼容性测试02&#xff08;20240622&#xff09; 作者&#xff1a;胖头…

一种简单的图像分析

简介 一种简单的边界分析&#xff0c;通过相邻的像素的灰度进行判断&#xff0c;计算出边界。 测试1 原图 结果 测试2 原图 结果 代码说明 主要的技术在makeTable过程中&#xff0c;这个过程主要执行了以下几步 计算每个像素的灰度计算相邻多个像素的最大灰度差统计灰度差…

每日一题——冒泡排序

C语言——冒泡排序 冒泡排序练习 前言&#xff1a;CSDN的小伙伴们&#xff0c;大家好&#xff01;今天我来给大家分享一种解题思想——冒泡排序。 冒泡排序 冒泡法的核心思想&#xff1a;两两相邻的元素进行比较 2.冒泡排序的算法描述如下。 (1)比较相邻的元素。如果第一 个比…

如何用Go语言实现一个基于宏系统的解释器?

步骤1&#xff1a;定义语言的语法规则 首先&#xff0c;你需要定义你的宏系统语言的基本语法规则。这通常包括关键字、标识符、操作符、表达式等元素的定义。 步骤2&#xff1a;词法分析&#xff08;Lexical Analysis&#xff09; 词法分析是将源代码分解成一个个的词素&…

第6集《大乘起信论》

请大家打开《讲义》第十一页。 我们上一堂课程讲到马鸣菩萨造作本论的因缘&#xff0c;简单的说明马鸣菩萨之所以造作本论&#xff0c;是要成就大乘的信、解、行、证四种功德。这个信、解、行是大乘的因相&#xff0c;证是大乘的果相&#xff0c;所以说有如是等因缘&#xff0…

揭秘软件功能测试:如何精准锁定测试需求?

软件功能测试在确定测试需求时&#xff0c;可以遵循以下步骤和依据&#xff1a; 一、确定测试需求的步骤 了解测试背景和目的&#xff1a; 了解软件系统的应用领域、用户群体、使用场景等信息。明确测试的目的和测试范围&#xff0c;以便有针对性地进行测试。分析软件系统&am…

展讯-源码编译

1.硬件要求 编译主机要求&#xff0c;注意尽量不要使用虚拟机 CPU&#xff1a;Intel(R) Core(TM) i7-4790 CPU 3.60 GHz 内存&#xff1a;8G 硬盘&#xff1a;500G SSD Ubuntu 版本&#xff1a;Ubuntu 64-bit 14.04.5 LTS 这是最低版本要求&#xff0c;实际性能越高越好 2…

从零到一学FFmpeg:av_interleaved_write_frame 函数详析与实战

文章目录 前言一、函数原型二、功能描述三、使用场景四、使用实例 前言 av_interleaved_write_frame是FFmpeg库中的一个函数&#xff0c;用于将一个已编码的媒体数据包&#xff08;AVPacket&#xff09;写入到输出媒体文件或流中。 此函数特别关注于维护正确的交错&#xff08…

【机器学习 复习】 第1章 概述

一、概念 1.机器学习是一种通过先验信息来提升模型能力的方式。 即从数据中产生“模型”( model )的算法&#xff0c;然后对新的数据集进行预测。 2.数据集&#xff08;Dataset&#xff09;&#xff1a;所有数据的集合称为数据集。 训练集&#xff1a;用来训练出一个适合模…

【JavaScript脚本宇宙】终极对决:六大虚拟DOM库横评

深度剖析&#xff1a;六大虚拟DOM库的奥秘与应用场景 前言 虚拟DOM&#xff08;Document Object Model&#xff09;是用于表示和操作HTML文档的抽象数据结构。虚拟DOM库是构建用户界面的重要工具&#xff0c;它们提供了高效的更新机制、组件化开发等功能&#xff0c;使开发者…

wpfui:一个开源免费具有现代化设计趋势的WPF控件库

wpfui介绍 wpfui是一款开源免费&#xff08;MIT协议&#xff09;具有现代化设计趋势的WPF界面库。wpfui为wpf的界面开发提供了流畅的体验&#xff0c;提供了一个简单的方法&#xff0c;让使用WPF编写的应用程序跟上现代设计趋势。截止写这篇文章&#xff0c;该项目获得了6.7k …