信号
Linux可用信号如下:
接收信号
为了捕捉信号,我们需要通过signal
函数来设置信号的处理方法。signal函数原型如下:
void(* signal(int signum, void(*handler)(int)))(int)
我们来分析一下该函数的结构:
- 头文件:#include <signal.h>
- 函数名:signal
- 参数:①signum,说明操作的是哪个信号;②void(*handler)(int),该参数为一个函数指针,指向参数①信号的处理函数
- 返回值:void(*)(int),该返回值也是一个函数指针,指向参数①信号更改前的处理函数。成功返回更改前的处理函数指针,失败返回SIG_ERR。
参数②有三种选择
- SIG_IGN:忽略该信号。
- SIG_DFL:捕捉到信号后使用默认的处理方法。
- 自定义函数:捕捉到信号后使用用户自定义的处理方法。
下面编写一个简单的程序演示该函数的使用
#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
,结果为:
释放信号
一般情况下,我们在驱动中释放信号,在应用程序中进行信号的捕捉和处理,要在驱动中实现异步通知功能,需要实现下面三项
- 支持F_SETOWN命令,设置filp->f_owner为对应的进程ID;
- 支持F_SETFL命令,驱动中实现fasync()函数。当FASYNC标志发生改变时,会执行驱动程序中的fasync()函数;
- 在设备可用时,利用kill_fasync()函数激发相应的信号。
这三项中,第一项工作由linux内核完成,我们无需关心。
驱动中的程序实现
也非常的简单,分为4步:
- 定义异步结构体指针
struct fasync_struct *async_queue;
- 编写异步通知的函数fasync()
static int xxx_fasync(int fd, struct file *filp, int mode) { return fasync_helper(fd, filp, mode, &led_char.async_queue); }
- 在设备资源可用时,调用
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); }
- 在文件关闭的时候,将文件从异步通知的列表中删除。在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()
函数。