Asynchronous notification and asynchronous I/O in device driver

Keywords: Linux Programming

Using asynchronous notification in the device driver can make the driver inform the user program to access the device. In this way, applications using non blocking I/O do not need polling mechanism to query whether the device is accessible, and blocking access can also be replaced by asynchronous notification similar to "interrupt". In addition to asynchronous notification, applications can return immediately after I/O requests are initiated. After that, I/O completion status is queried, or I/O completion is returned. This process is asynchronous I/O.

Blocking and non blocking access, poll function provide a better mechanism to solve device access, but if there is asynchronous notification, the whole mechanism is more complete. Asynchronous notification means that once the device is ready, the user program will be notified actively, so that the user program does not need to query the device status, which is very similar to the concept of "interrupt" on the hardware, more accurately called "signal driven asynchronous I/O". Signal is a kind of simulation of terminal mechanism at the software level. In principle, a process receives a signal by hand and the processor receives an interrupt request, which can be said to be the same. The signal is asynchronous. A process does not need any operation to wait for the signal to arrive. In fact, the process does not know when the signal will arrive.

Blocking I/O means waiting for the device to access all the time. Using poll function in non blocking I/O means querying whether the device is accessible or not. Asynchronous notification means that the device notifies the user that the program itself is accessible, and then the user is processing I/O. Thus, these I/O modes can complement each other. Blocking, non blocking and asynchronous notifications have no advantages or disadvantages, so they should be selected according to different scenarios.

Asynchronous notification is realized by signals, which is a mechanism of interprocess communication (IPC). There are more than 30 signals available in linux, which can be queried by Baidu. In addition to SIGSYOP and SIGKILL signals, the process can ignore or acquire all other signals. The awareness that a signal is captured is that when a signal arrives there is a corresponding code to process it. If a signal is not captured by this process, the kernel will take default behavior processing.

Reception function of signal

void (*singal(int signum,void(* handler))(int))(int);
/ / decomposed into
typedef void (* sighsndler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
//The first parameter specifies the value of the signal. The second parameter specifies the processing function for the previous signal value. If SIG_IGN, the signal is ignored. If SIG_DFL, the signal is ignored
 The signal is processed by default. If it is a user-defined function, the function will be executed after the signal is captured. If the signal() function call succeeds, it returns the last
 Handler value of handler function bound by signal sign, SIG_ERR returned in case of failure.

Get "Ctrl+C" and print the corresponding signal value

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void sigtem_handler(int signo)
{
    printf("Have change sig N.O %d\n",signo);
    exit(0);
}

int main()
{
    signal(SIGINT,sigtem_handler);
 //   signal(SIGTERM,sigtem_handler);
    while(1);
    return 0;
}

In addition to the signal function, the sigaction() function can be used to change the behavior of a process after receiving a signal. Its prototype is:

int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact);
//The first parameter is the value of the signal, which can be any specific valid signal except SIGKILL and SIGSTOP. The second parameter is a pointer to an instance of the structure sigaction,
In the instance of structure sigaction, the processing function for a specific signal is specified. If it is empty, the process will process the signal by default. The third parameter, oldcat, is used to
 Save the original processing function for the corresponding signal, and specify oldact as NULL. If the second and third parameters are set to NULL, the rigid function can be used to detect the validity of the signal.

In order to enable the device to support asynchronous notification mechanism, three tasks are involved in the driver

  1. It supports the f ﹣ setown command, and can set FILP - > F ﹣ owner as the corresponding process ID in this control command processing. However, Xixiang project has completed the kernel, and the device driver does not need to be processed. ,
  2. It supports the processing of F? Setfl command. Whenever the FASYNC flag changes, the fasync() function in the driver will be executed. Therefore, the fasync() function should be implemented in the driver.
  3. When the device resource is available, call the kill_fasync() function to fire the corresponding signal.

Asynchronous notification programming in device driver is relatively simple, mainly using a data structure and two functions. The data structure is fasync struct structure, and the two functions are respectively:

//Functions for handling changes in the FASYNC flag
int fasync_helper(int fd,struct file *filp,int mode,struct fasync_struct **fa);
//Function for signal release
void kill_fasync(struct fasync_struct **fa,int sig,int band);

Asynchronous notification function template

1.Add asynchronous structure to device structure
struct xxx_dev{
    struct cdev cdve;
    ...
    struct fasync_struct *async_queue; //Asynchronous notification structure
};
2.file_operations Add in structure fasync
static struct file_operations xxx_fops{
    ...
    .fasync = xxx_fasync,
};
3.Device driver fasync()Function template
static int xxx_fasync(int fd,struct file *filp,int mode)
{
    struct xxx_dev *dev = filp->private_data;
    return fasync_helper(fd,filp,mode,&dev->async_queue);
} //That is to say, you only need to return the asynchronous structure as the fourth parameter of fasync? Helper
4.stay write Add asynchronous signal reading in function
static ssize_t xxx_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_pos)
{
    struct xxx_dev *dev = filp->private_data;
    ...
    if(dev->async_queue) //Generate asynchronous read signal
        kill_fasync(&dev->async_queue,SIGIO,POLL_IN);//Call kill? Fasync to release the signal
}
5.When the file is closed, i.e release Application function in function fasync Remove files from asynchronous communication list
static int xxx_release(struct inode *inode,struct file *filp)
{
    xxx_fasync(-1,filp,0);
    ...
    return 0;
}

Amendment Blocking I/O posts Driver code in, compile load, test code

#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>

static void signalio_handler(int signum)
{
    printf("receive a signal from mymodules,signalnum:%d\n",signum);
}

int main()
{
    int fd,oflags;
    fd = open("/dev/mymodules",O_RDWR,S_IRUSR | S_IWUSR);
    if(fd != -1){
        signal(SIGIO,signalio_handler);
        fcntl(fd,F_SETOWN,getpid());
        oflags = fcntl(fd,F_GETFL);
        fcntl(fd,F_SETFL,oflags | FASYNC);
        while(1){
            sleep(100);
        }
    }else
        printf("open device error\n");
    return 0;
}

Post compilation test

Published 45 original articles, won praise 7, visited 10000+
Private letter follow

Posted by jack_indigo on Thu, 12 Mar 2020 03:36:36 -0700