table of Contents
Three, the realization process
1. Signal
Let’s review the "interrupt" first. Interrupt is an asynchronous mechanism provided by the processor. After we configure the interrupt, we can let the processor handle other things. When the interrupt occurs, it will trigger the interrupt service function we set in advance. , Do specific processing in the interrupt service function. After the interrupt is used, the processor does not need to check whether the button is pressed at all times, because the interrupt will be automatically triggered after the button is pressed.
Similarly, Linux applications can access the drive device in two ways: blocking or non-blocking
- In blocking mode, the application will be in a dormant state, waiting for the drive device to be available for use
- In non-blocking mode, it will continuously poll through the poll function to check whether the drive device file can be used
The signal is similar to the "interrupt" used on our hardware, but the signal is at the software level. It can be regarded as a simulation of interruption at the software level. The driver can report that it is accessible by actively sending a signal to the application program. After the application program obtains the signal, it can read or write data from the drive device . The whole process is equivalent to that the application program receives an interrupt sent by the driver, and then the application program responds to the interrupt. During the entire process, the application program does not query whether the drive device is accessible, everything is told by the drive device itself For the application.
The core of asynchronous notification is the signal. All the signals supported by Linux are defined in the arch/xtensa/include/uapi/asm/signal.h file. These signals are as follows:
#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
Second, the connection and difference between synchronous IO, asynchronous IO, blocking IO, and non-blocking IO
https://www.cnblogs.com/euphie/p/6376508.html
Three, the realization process
Complete driver source code: https://github.com/denghengli/linux_driver/tree/master/17_fasync
1. Application
To use asynchronous notification in your application, you need to add content:
(1) Register new number processing function
(2) Tell the kernel the process number of this application
(3) Turn on asynchronous notification
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;
}
2. Driver
(1) When the device is accessible, the driver needs to send a signal to the application through the kill_fasync function, which is equivalent to generating an "interrupt"
void kill_fasync(struct fasync_struct **fp, int sig, int band)
fp : The fasync_struct to be operated.
sig the signal to be sent.
Set to POLL_IN when the band is readable, and set to POLL_OUT when it can be written.
Return value : None.
(2) Write the fasync function. When the application changes the fasync flag through fcntl(fd, F_SETFL, flags | FASYNC) , the fasync function in the file_operations operation set of the driver will be executed, and the fasync control structure needs to be initialized in this function
/* 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,
};