Linux 提供了异步通知这个机制来完成:驱动程序能主动向应用程序发出通知,报告自己可以访问,然后应用程序在从驱动程序中读取或写入数据。
阻塞非阻塞与异步通知的区别:
阻塞/非阻塞:需要应用程序主动的去查询设备的使用情况。通过阻塞方式访问的话应用程序会处于休眠态,等待驱动设备可以使用,非阻塞方式的话会通过 poll 函数来不断的轮询,查看驱动设备文件是否可以使用。
异步通知:通过主动向应用程序发送信号的方式来报告自己可以访问了,应用程序获取到信号以后就可以从驱动设备中读取或者写入数据了。
异步通知的核心就是信号,在 arch/xtensa/include/uapi/asm/signal.h 文件中定义了 Linux 所支持的所有信号。
在应用程序中可以使用 signal 函数来设置指定信号的处理,使用函数信号处理函数来关闭应用程序。
//signal 函数
sighandler_t signal(int signum, sighandler_t handler)
//信号处理函数
typedef void (*sighandler_t)(int)
1、驱动中的信号处理:
(1)在驱动程序中定义一个 fasync_struct 结构体指针变量;
(2)设备驱动中实现 file_operations 操作集中的 fasync 函数,fasync还需要借助fasync_helper函数;
int (*fasync) (int fd, struct file *filp, int on);
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp);
(3)驱动里面调用fasync向应用发送信号;
void kill_fasync(struct fasync_struct **fp, int sig, int band)
(4)关闭驱动的时候要删除信号;
2、应用程序对异步处理的通知:
(1)注册信号处理函数,应用程序根据驱动程序所使用的信号来设置信号的处理函数;
(2)使用fcntl(fd, F_SETOWN, getpid())将本应用程序的进程号告诉给内核,;
(3)开启异步通知;
flags = fcntl(fd, F_GETFL); /* 获取当前的进程状态 */
fcntl(fd, F_SETFL, flags | FASYNC); /* 开启当前进程异步通知功能 */
1、驱动程序编写
cd /liunx/IMX6ULL/Linux_Driver/
mkdir 16_asyncnoti
cp 14_imx6uirq/ * 16_asyncnoti/ -rf
cp 14_imx6uirq/.vscode 16_asyncnoti/ -rf
cd 15_blockio/
ls
rm tasklet.c work.c
rm imx6uirqAPP
mv imx6uirqAPP.c asyncnotiAPP.c
在设备结构体中添加一个struct fasync_struct* fasync_queue;
编写驱动代码Linux内核源码中的joydev.c这个文件。
找到file_operations
static int imx6uirq_fasync(int fd, struct file *filp, int on)
{
struct imx6uirq_dev *dev = filp->private_data;
return fasync_helper(fd,filp,on,&dev->fasync_queue);
}
int imx6uirq_release(struct inode *inode, struct file *filp)
{
return = imx6uirq_fasync(-1, filp, 0);
}
//操作集
struct const struct file_operations imx6uirq_fops = {
......
.fasync = imx6uirq_fasync,
.release = imx6uirq_release,
};
当按键有效的时候,需要向应用程序通知,在timer_func()中按键处理完了之后添加:
static void timer_func(unsigned long arg)
{
.......
if(atomic_read(&dev->releasekey))//为真,表示一个完整的按键过程,接下来向应用程序发送信号
{
if(dev->fasync_queue)
kill_fasync(&dev->fasync_queue, SIGIO, POLL_IN);
}
}
编译报错,添加头文件:
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/ide.h>
2、应用程序编写
添加头文件:
#include <signal.h>
不需要while()里面的内容:
我们需要设置信号处理函数:
signal(SIGIO, sigio_signal_func);
当驱动发送SIGIO时,这个sigio_signal_func()就会执行,
下面实现sigio_signal_func();
int fd;
static void sigio_signal_func(int num)
{
int err = 0;
unsigned int keyvalue = 0;
err = read(fd, &keyvalue, sizeof(keyvalue));
if(err < 0) {
} else {
printf("sigio signal! key value=%d", keyvalue);
}
}
驱动怎么知道向哪一个应用发送呢?或者这个应用怎么知道开启异步通知呢?使用fcntl()函数。
设置一个状态位来获取当前进程状态;
fcntl(fd, F_SETOWN, getpid()); /* 将当前进程的进程号告诉给内核 */
flags = fcntl(fd, F_GETFD); /* 获取当前的进程状态 */
fcntl(fd, F_SETFL, flags | FASYNC);/* 设置进程启用异步通知功能 */
编译:
make
arm-linux-gnueabihf-gcc asyncnotiAPP.c -o asyncnotiAPP
sudo cp imx6uirq.ko asyncnotiAPP /home/yang/nfs/rootfs/lib/modules/4.1.15/ -f
开发板:
depmod
modprobe imx6uirq.ko
./asyncnotiAPP /dev/imx6uirq
直接退出了,应用程序有问题,在设置完了之后,需要一个死循环。
while(1){
sleep(2);
}
编译验证:按下按键没有任何反应
在驱动程序中,在有效的按键按下后面打印一个标记:
if(atomic_read(&dev->releasekey))//为真,表示一个完整的按键过程,接下来向应用程序发送信号
{
if(dev->fasync_queue){
kill_fasync(&dev->fasync_queue, SIGIO, POLL_IN);
printk("kill_fasync\r\n");}
}
检查按键中断函数有没有执行?打印一条消息。----->有反应
检查定时器中断?打印一条消息。----->有反应
检查timer_func()value的值,有没有效?
if(atomic_read(&dev->releasekey))//为真,表示一个完整的按键过程,接下来向应用程序发送信号
{
//if(dev->fasync_queue){
kill_fasync(&dev->fasync_queue, SIGIO, POLL_IN);
printk("kill_fasync\r\n");}
}
也可以执行,这说明驱动没有问题。
最终发现在应用程序打打印中没有添加\r\n。然后在终端上并没有打印出来。其实打印了,只是存到缓存区了。
参考:https://blog.csdn.net/qq_37284607/article/details/112470262