Linux异步通知

信号

Linux可用信号如下:
在这里插入图片描述在这里插入图片描述

接收信号

为了捕捉信号,我们需要通过signal函数来设置信号的处理方法。signal函数原型如下:
void(* signal(int signum, void(*handler)(int)))(int)
我们来分析一下该函数的结构:

  • 头文件:#include <signal.h>
  • 函数名:signal
  • 参数:①signum,说明操作的是哪个信号;②void(*handler)(int),该参数为一个函数指针,指向参数①信号的处理函数
  • 返回值:void(*)(int),该返回值也是一个函数指针,指向参数①信号更改前的处理函数。成功返回更改前的处理函数指针,失败返回SIG_ERR。

参数②有三种选择

  1. SIG_IGN:忽略该信号。
  2. SIG_DFL:捕捉到信号后使用默认的处理方法。
  3. 自定义函数:捕捉到信号后使用用户自定义的处理方法。

下面编写一个简单的程序演示该函数的使用

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

/* 信号处理函数 */
static void sigterm_handler(int signo)
{
	printf("Have caught sig N.O. %d\r\n", signo);
	exit(0);
}

int main(void)
{
	signal(SIGINT, sigterm_handler);	/* Ctrl + c */
	signal(SIGTERM, sigterm_handler);	/* kill命令 */
	while(1); 
	return 0;
}

编译运行之后结果如下:
按下Ctrl+c结果
在这里插入图片描述
执行命令killall 可执行文件名,我的为killall a.out,结果为:
在这里插入图片描述

释放信号

一般情况下,我们在驱动中释放信号,在应用程序中进行信号的捕捉和处理,要在驱动中实现异步通知功能,需要实现下面三项

  1. 支持F_SETOWN命令,设置filp->f_owner为对应的进程ID;
  2. 支持F_SETFL命令,驱动中实现fasync()函数。当FASYNC标志发生改变时,会执行驱动程序中的fasync()函数;
  3. 在设备可用时,利用kill_fasync()函数激发相应的信号。

这三项中,第一项工作由linux内核完成,我们无需关心。

驱动中的程序实现

也非常的简单,分为4步:

  1. 定义异步结构体指针
    struct fasync_struct *async_queue;
  2. 编写异步通知的函数fasync()
    static int xxx_fasync(int fd, struct file *filp, int mode)
    {
    	return fasync_helper(fd, filp, mode, &led_char.async_queue);
    }
  3. 在设备资源可用时,调用kill_fasync()函数来释放SIGIO信号,这里的例子为当可读时触发信号。
    static ssize_t xxx_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
    {
    	if (async_queue)
    		kill_fasync(&async_queue, SIGIO, POLL_IN);
    }
  4. 在文件关闭的时候,将文件从异步通知的列表中删除。在release()函数中调用xxx_fasync(-1, filp, 0)即可。
    static int xxx_release(struct inode *inode, struct file *filp)
    {
    	xxx_fasync(-1, filp, 0); 
    	return 0;
    }

下方是我在驱动程序中的简单实现

#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/timer.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/gpio/consumer.h>
#include <linux/string.h>  
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/atomic.h>
#include <asm/unaligned.h>

#define NEWCHRDEV_CNT  1
#define NEWCHRDEV_NAME  "led_dev"  /* 设备名称 */

static struct gpio_emio{
	struct gpio_desc *gpiod;	
	struct device *gpio_dev;
	struct device_node *device_node;
	int gpio;
}S_gpio_emio_dev;

struct led_dev{
	dev_t devid;    /* 设备号 */
	struct cdev cdev;   /* 设备 */
 
	struct class *class;  /* 自动生成设备节点的类 */
	struct device *device; 
 
	int major;
	int minor;
 
	atomic_t atomic;   /* 原子操作 */
	spinlock_t spinlock;   /* 自旋锁 */
 
	struct timer_list timer;    /* 定时器 */
 
	struct fasync_struct *async_queue;  /* 异步结构体指针 */
}led_char;

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

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

static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int ret;
 	unsigned char val;

	ret = copy_from_user(&val,buf,1);
 	if (ret != 0) {
  		return -1;
 	}

	if (led_char.async_queue)
		kill_fasync(&led_char.async_queue, SIGIO, POLL_IN);
}

static int led_fasync(int fd, struct file *filp, int mode)
{
	printk("Enter led_fasync\r\n");
	return fasync_helper(fd, filp, mode, &led_char.async_queue);
}

static int led_release(struct inode *inode, struct file *filp)
{
	led_fasync(-1, filp, 0); 
	return 0;
}

static struct file_operations chrdevbase_fops = {
	.owner = THIS_MODULE,
 	.open = led_open,
 	.read = led_read,
 	.write = led_write,
 	.fasync = led_fasync,
 	.release = led_release,
};

static int led_probe(struct platform_device *dev)
{
	printk("Enter led_probe!\r\n");
	
	/* 找到设备树emio节点 */
 	S_gpio_emio_dev.device_node = of_find_node_by_path("/ch_emio");
 	if (S_gpio_emio_dev.device_node == NULL) {
 		printk("Not find ch_emio node !\r\n");
  		return -EINVAL;
 	}

	/* 获取子节点 */
 	S_gpio_emio_dev.device_node = of_get_next_child(S_gpio_emio_dev.device_node, NULL);
 
 	S_gpio_emio_dev.gpio = of_get_named_gpio(S_gpio_emio_dev.device_node, "gpios", 0);
 	if (!gpio_is_valid(S_gpio_emio_dev.gpio)) {
     		printk("gpios: %d is invalid\n", S_gpio_emio_dev.gpio); 
  		return -ENODEV;
    	}

	//请求控制获取的gpio
 	if (gpio_request(S_gpio_emio_dev.gpio, "gpios")) { 
        	printk("gpio %d request failed!\n", S_gpio_emio_dev.gpio); 
        	gpio_free(S_gpio_emio_dev.gpio); 
        	return -ENODEV;
    	}

	gpio_direction_output(S_gpio_emio_dev.gpio, 0);

	/* 获取设备号 */
 	if (led_char.major) {
  		led_char.devid = MKDEV(led_char.major, 0);
  		register_chrdev_region(led_char.devid, NEWCHRDEV_CNT, NEWCHRDEV_NAME);
 	}
 	else {	
 		alloc_chrdev_region(&led_char.devid, 0, NEWCHRDEV_CNT, NEWCHRDEV_NAME);
		led_char.major = MAJOR(led_char.devid);
		led_char.minor = MINOR(led_char.devid);
	}
 	printk("major = %d, minor= %d\r\n", led_char.major, led_char.minor);

	/* 初始化设备 */
 	led_char.cdev.owner = THIS_MODULE;
 	cdev_init(&led_char.cdev, &chrdevbase_fops);

	/* 向系统中添加设备 */
 	cdev_add(&led_char.cdev, led_char.devid, NEWCHRDEV_CNT);

	/* 生成设备节点 */
 	/* 创建类 */
 	led_char.class = class_create(THIS_MODULE, NEWCHRDEV_NAME);
 	if (IS_ERR(led_char.class)) {
  		return PTR_ERR(led_char.class);
 	}

	/* 创建设备 */
 	led_char.device = device_create(led_char.class, NULL, led_char.devid, NULL, NEWCHRDEV_NAME);
 	if (IS_ERR(led_char.device)) {
  		return PTR_ERR(led_char.device);
 	}

	return 0;
}

static int led_remove(struct platform_device *dev)
{
	/* 释放gpio资源 */
 	gpio_free(S_gpio_emio_dev.gpio);
 
 	/* 注销设备 */
 	cdev_del(&led_char.cdev);
 
 	/* 释放设备号 */
 	unregister_chrdev_region(led_char.devid, NEWCHRDEV_CNT);
 
 	/* 注销设备和类 */
 	device_destroy(led_char.class, led_char.devid);
 	class_destroy(led_char.class);
 
 	return 0;
}

static const struct of_device_id S_led_of_match[] = {
 	{.compatible = "zynq,xiao_led"},
 	{},
};

static struct platform_driver S_led_driver = {
 	.probe = led_probe,
 	.remove = led_remove,
 	.driver = {
  		.name = "xxx",
  		.of_match_table = S_led_of_match,
	}, 
};

/* 模块加载函数 */
static int __init led_module_init(void)
{
 	printk("Module enter!\r\n");
 
 	/* 平台驱动注册 */
 	platform_driver_register(&S_led_driver);
 
 	return 0;
}

/* 模块卸载函数 */
static void __exit led_module_exit(void)
{
 	/* 平台驱动卸载 */
 	platform_driver_unregister(&S_led_driver);
 
 	printk("Module exit!\r\n");
}

/* 指定驱动的入口和出口函数 */
module_init(led_module_init);
module_exit(led_module_exit);
MODULE_LICENSE("GPL");
应用测试

程序一:启动异步通知功能。

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>

#define DEV_NAME  "/dev/led_dev"
int g_fd = 0;

void led_handler(int num)
{
 	printf("Enter led_handler\n");
}

int main(void)
{
	int oflags;

	g_fd = open(DEV_NAME, O_RDWR);
 	if (g_fd < 0)
 	{
  		perror("Open filed!\r\n");
  		exit(0);
 	}

	signal(SIGIO, led_handler);
 	fcntl(g_fd, F_SETOWN, getpid());
 	oflags = fcntl(g_fd, F_GETFL);
 	fcntl(g_fd, F_SETFL, oflags | FASYNC);

	while (1);

	return 0}

程序二:往/dev/led_dev设备中写入数据,触发SIGIO信号

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>

#define DEV_NAME  "/dev/led_dev"

int main(void)
{
	int fd = open(DEV_NAME, O_RDWR);
	if (g_fd < 0)
 	{
  		perror("Open filed!\r\n");
  		exit(0);
 	}
	write(g_fd, "1", strlen("1"));
	close(fd);

	return 0;
}
测试结果

在这里插入图片描述
第一条打印表示程序二执行后触发SIGIO信号,程序一捕捉到信号后进入led_handler(),第二条打印表示程序二关闭文件后进入驱动程序的led_release()函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值