目录
一、信号
二、同步IO、异步IO、阻塞IO、非阻塞IO之间的联系与区别
三、实现过程
1、应用程序
2、驱动程序
先来回顾一下“中断”,中断是处理器提供的一种异步机制,我们配置好中断以后就可以让处理器去处理其他的事情了,当中断发生以后会触发我们事先设置好的中断服务函数,在中断服务函数中做具体的处理。采用中断以后处理器就不需要时刻的去查看按键有没有被按下,因为按键按下以后会自动触发中断。
同样的, Linux应用程序可以通过阻塞或者非阻塞这两种方式来访问驱动设备
信号类似于我们硬件上使用的“中断”,只不过信号是软件层次上的。算是在软件层次上对中断的一种模拟,驱动可以通过主动向应用程序发送信号的方式来报告自己可以访问了, 应用程序获取到信号以后就可以从驱动设备中读取或者写入数据了。整个过程就相当于应用程序收到了驱动发送过来了的一个中断,然后应用程序去响应这个中断,在整个处理过程中应用程序并没有去查询驱动设备是否可以访问,一切都是由驱动设备自己告诉给应用程序的。
异步通知的核心就是信号,在 arch/xtensa/include/uapi/asm/signal.h文件中定义了Linux所支持的所有信号,这些信号如下所示:
#define SIGHUP 1
#define SIGINT 2
#define SIGQUIT 3
#define SIGILL 4
#define SIGTRAP 5
#define SIGABRT 6
#define SIGIOT 6
#define SIGBUS 7
#define SIGFPE 8
#define SIGKILL 9
#define SIGUSR1 10
#define SIGSEGV 11
#define SIGUSR2 12
#define SIGPIPE 13
#define SIGALRM 14
#define SIGTERM 15
#define SIGSTKFLT 16
#define SIGCHLD 17
#define SIGCONT 18
#define SIGSTOP 19
#define SIGTSTP 20
#define SIGTTIN 21
#define SIGTTOU 22
#define SIGURG 23
#define SIGXCPU 24
#define SIGXFSZ 25
#define SIGVTALRM 26
#define SIGPROF 27
#define SIGWINCH 28
#define SIGIO 29
#define SIGPOLL SIGIO
/*
#define SIGLOST 29
*/
#define SIGPWR 30
#define SIGSYS 31
#define SIGUNUSED 31
/* These should not be considered constants from userland. */
#define SIGRTMIN 32
#define SIGRTMAX _NSIG
#define SIGSWI 32
https://www.cnblogs.com/euphie/p/6376508.html
完整的驱动程序源码:https://github.com/denghengli/linux_driver/tree/master/17_fasync
在应用程序中要使用异步通知,需要添加内容:
(1)注册新号处理函数
(2)将本应用程序的进程号告诉内核
(3)开启异步通知
static void sigio_signal_func(int signum)
{
...
}
/*
*argc 应用程序参数个数
*argv 具体的参数内容,字符串形式
*/
int main(int argc, char *argv[])
{
...
signal(SIGIO, sigio_signal_func);/*设置sigio的处理函数*/
/*int fcntl (int __fd, int __cmd, ...);*/
fcntl(fd, F_SETOWN, getpid());/*设置当前进程接收SIGIO信号*/
flags = fcntl(fd, F_GETFL);/*获取当前文件标志*/
fcntl(fd, F_SETFL, flags | FASYNC);/*设置当前文件标志,开启异步通知*/
...
return 0;
}
(1)当设备可以访问的时候,驱动程序需要通过 kill_fasync 函数向应用程序发出信号,相当于产生“中断”
void kill_fasync(struct fasync_struct **fp, int sig, int band)
fp:要操作的 fasync_struct。
sig 要发送的信号。
band 可读时设置为 POLL_IN,可写时设置为 POLL_OUT。
返回值:无。
(2)编写fasync函数,当应用程序通过 fcntl(fd, F_SETFL, flags | FASYNC) 改变fasync标记的时候,驱动程序 file_operations操作集中的 fasync 函数就会执行,需要在该函数中初始化 fasync 控制结构体
/* key设备结构体 */
struct key_dev{
...
/*异步结构体 --- 异步通知*/
struct fasync_struct *fasync_queue;
};
/*定时器回调函数*/
static void timer_func(unsigned long arg)
{
struct key_dev *dev = (struct key_dev*)arg;
...
if (dev->fasync_queue){ /*有效的按键过程,相应层发送信号实现异步通知*/
/*SIFIO为发送的信号,POLL_IO表示可读,POLL_OUT表示可写*/
kill_fasync(&dev->fasync_queue, SIGIO, POLL_IN);
}
}
}
static int key_release(struct inode *inode, struct file *filp)
{
int i = 0;
struct key_dev *dev = filp->private_data;
...
/*删除fasync结构体*/
return fasync_helper(-1, filp, 0, &dev->fasync_queue);
}
static int key_fasync(int fd, struct file *filp, int on)
{
struct key_dev *dev = filp->private_data;
/*初始化fasync结构体*/
return fasync_helper(fd, filp, on, &dev->fasync_queue);
}
/* 设备操作函数 */
static struct file_operations key_fops = {
...
.fasync = key_fasync,
};