最全Linux驱动开发八股文(十四)
你好,我是拉依达。
这是我的Linux驱动开发八股文详细解析系列。
本系列最开始是我在csdn上更新的文章,目前已经是csdn搜索“linux驱动”综合推荐第一名,累计阅读次数4w次。
全文总字数近8w字,是目前全网最全面,最清晰的入门linux驱动学习资料。
现重新对内容进行整理,希望可以帮助到更多学习嵌入式的同学。
【下面是拉依达推荐学习相关专栏:】
一、Linux驱动学习专栏:拉依达的Linux驱动八股文 - 牛客网
二、Linux应用学习专栏:拉依达的Linux应用八股文 - 牛客网
【我的嵌入式学习和校招经验】 拉依达的嵌入式学习和秋招经验-CSDN博客
嵌入式学习规划/就业经验指导,可私信咨询
———————————————————————————————————————————————————
十四、异步通知机制(信号通知)
阻塞IO和非阻塞IO都需要应用程序主动去查询设备的使用情况。Linux 提供了异步通知机制,驱动程序能主动向应用程序发出通知。
信号是在软件层次上对中断的一种模拟,驱动可以通过主动向应用程序发送信号的方式通知可以访问,应用程序获取到信号后就可以从驱动设备中读取或者写入数据。
14.1 异步通知应用程序
-
注册信号处理函数
应用程序根据驱动程序所使用的信号来设置信号的处理函数,应用程序使用 signal 函数来设置信号的处理函数。
sighandler_t signal(int signum, sighandler_t handler)
-
signum:要设置处理函数的信号
-
handler: 信号的处理函数
-
返回值: 设置成功返回信号的前一个处理函数,设置失败的话返回 SIG_ERR。
信号中断处理函数
typedef void (*sighandler_t)(int)
在处理函数执行相应的操作即可。
-
-
将本应用程序的进程号告诉给内核
fcntl系统调用可以用来对已打开的文件描述符进行各种控制操作以改变已打开文件的的各种属性
#include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd); int fcntl(int fd, int cmd, long arg); int fcntl(int fd, int cmd, struct flock *lock);
使用 fcntl(fd, F_SETOWN, getpid())将本应用程序的进程号告诉给内核。
-
开启异步通知
主要是通过 fcntl 函数设置进程状态为 FASYNC,经过这一步,驱动程序中的 fasync 函数就会执行。
flags = fcntl(fd, F_GETFL); /* 获取当前的进程状态 */ fcntl(fd, F_SETFL, flags | FASYNC); /* 开启当前进程异步通知功能 */
-
应用程序模板
static int fd = 0; /* 文件描述符 */ 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); } } 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; }
14.2 异步通知驱动程序
-
内核要使用异步通知需要在驱动程序中定义一个 fasync_struct 结构体的指针变量
一般将 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; }; struct fasync_struct *async_queue; /* 异步相关结构体 */
-
然后需要在设备驱动中实现 file_operations 操作集中的 fasync 函数。
int (*fasync) (int fd, struct file *filp, int on)
-
fasync 函数里面一般通过调用 fasync_helper 函数来初始化前面定义的 fasync_struct 结构体指针
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 函数就会执行。
-
当设备可以访问的时候,驱动程序需要向应用程序发出信号,相当于产生中断。 kill_fasync函数负责发送指定的信号
void kill_fasync(struct fasync_struct **fp, int sig, int band)
- fp:要操作的 fasync_struct
- sig: 要发送的信号
- band: 可读时设置为 POLL_IN,可写时设置为 POLL_OUT
-
最后,在关闭驱动文件的时候需要在 file_operations 操作集中的 release 函数中释放 fasync_struct,fasync_struct 的释放函数为 fasync_helper。
xxx_fasync(-1, filp, 0); /* 删除异步通知 */
xxx_fasync 函数就是file_operations 操作集中的 fasync 函数。
-
驱动程序模板
/* imx6uirq设备结构体 */ struct imx6uirq_dev{ ...... struct fasync_struct *async_queue; /* 异步相关结构体 */ }; struct imx6uirq_dev imx6uirq; /* irq设备 */ void timer_function(unsigned long arg) { ...... if(atomic_read(&dev->releasekey)) { /* 一次完整的按键过程 */ if(dev->async_queue) kill_fasync(&dev->async_queue, SIGIO, POLL_IN); /* 释放SIGIO信号 */ } ...... } /* * @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); /* 初始化fasync_struct 结构体指针*/ } static int imx6uirq_release(struct inode *inode, struct file *filp) { return imx6uirq_fasync(-1, filp, 0); /* 删除异步通知 */ } /* 设备操作函数 */ static struct file_operations imx6uirq_fops = { ...... .poll = imx6uirq_poll, .fasync = imx6uirq_fasync, .release = imx6uirq_release, };
你好,我是拉依达。 这是我的Linux驱动开发八股文详细解析系列。 本系列最开始是我在csdn上更新的文章,目前已经是csdn搜索“linux驱动”综合推荐第一名,累计阅读次数4w次。 全文总字数近8w字,是目前全网最全面,最清晰的入门linux驱动学习资料。 现在我重新对内容进行整理,已专栏的形式发布在牛客上,希望可以帮助到更多学习嵌入式的同学。