linux kernel asynchronous notification

1. Introduction to asynchronous notification

1) Interrupt is an asynchronous mechanism provided by the processor. After configuring the interrupt, the processor can handle other things. When the interrupt occurs, it will trigger the interrupt service function set in advance and do specific processing in the interrupt service function.

2) If accessed in blocking mode, the application will be in dormant state and wait for the driver to be available. If not, it will continuously poll through the poll function to check whether the driver file can be used. Both methods require the application to actively query the usage of the device.

3) Signal
The driver can report that it is accessible by actively sending a signal to the application. After the application obtains the signal, it can read or write data from the driver device. The whole process is equivalent to that the application receives an interrupt sent by the driver, and then the application responds to the interrupt.

Asynchronous notification signals are defined in arch / xtensa / include / UAPI / ASM / signal There are many in the H file

#define SIGHUP 1 / * the terminal hangs or the control process terminates*/
#define SIGINT 2 / * terminal interrupt (Ctrl+C)*/
#define SIGQUIT 3 / * terminal exit (Ctrl + \ key combination)*/
#define SIGILL 4 / * illegal instruction*/
#Define sigrap 5 / * debug is used, and breakpoint instructions are generated*/
#define SIGABRT 6 / * exit instruction issued by abort(3)*/
#Define sigiot 6 / * IOT instruction*/
#define SIGBUS 7 / * bus error*/
#define SIGFPE 8 / * floating point operation error*/
#define SIGKILL 9 / * kill and terminate the process*/
#define SIGUSR1 10 / * user defined signal 1*/
#define SIGSEGV 11 / * segment violation (invalid memory segment)*/
#define SIGUSR2 12 / * user defined signal 2*/
#define SIGPIPE 13 / * write data to non read pipeline*/
#define SIGALRM 14 / * alarm clock*/
#define SIGTERM 15 / * software termination*/
#define SIGSTKFLT 16 / * stack exception*/
#define SIGCHLD 17 / * end of child process*/
#define SIGCONT 18 / * process continues*/
#define SIGSTOP 19 / * stop the execution of the process, just pause*/
#define SIGTSTP 20 / * stop the process (Ctrl+Z key combination)*/
#define SIGTTIN 21 / * the background process needs to read data from the terminal*/
#define SIGTTOU 22 / * the background process needs to write data to the terminal*/
#define SIGURG 23 / * with "emergency" data*/
#define SIGXCPU 24 / * CPU resource limit exceeded*/
#define SIGXFSZ 25 / * excess file size*/
#define SIGVTALRM 26 / * virtual clock signal*/
#define SIGPROF 27 / * clock signal description*/
#define SIGWINCH 28 / * window size change*/
#define SIGIO 29 / * can perform input / output operations*/
#define SIGPOLL SIGIO
/* #define SIGLOS 29 */
#define SIGPWR 30 / * breakpoint restart*/
#define SIGSYS 31 / * illegal system call*/
#define SIGUNUSED 31 / * Unused signal*/

Except SIGKILL(9) and SIGSTOP(19), other signals can be ignored.
These signals are equivalent to interrupt numbers. Different interrupt numbers represent different interrupts, and different interrupts do different processing. Therefore, the driver can realize different functions by sending different signals to the application.

2. Signal processing in driving

fasync_struct structure

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;
};

Add fasync in the device structure_ Struct pointer variable

struct xxx_dev {
	struct fasync_struct *async_queue; /* Asynchronous correlation structure */
};

In the file driver, you need to implement asynchronous notification_ fasync function in the operations operation set

int (*fasync) (int fd, struct file *filp, int on)

The fasync function usually calls fasync_helper function to initialize the fasync defined earlier_ Struct structure
Pointer

int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)

Kernel usage example

struct xxx_dev {
	......
	struct fasync_struct *async_queue; /* Asynchronous correlation structure */
};

static int xxx_fasync(int fd, struct file *filp, int on)
{
	struct xxx_dev *dev = (xxx_dev)filp->private_data;
	if (fasync_helper(fd, filp, on, &dev->async_queue) < 0)
	return -EIO;
	 return 0;
 }

static struct file_operations xxx_ops = {
	 ......
	.fasync = xxx_fasync,
	.release = xxx_release,
	 ......
 };
static int xxx_release(struct inode *inode, struct file *filp)
{
	return xxx_fasync(-1, filp, 0); /* Delete asynchronous notification */
}

kill_fasync function
When the device is accessible, the driver needs to signal the application. kill_ The fasync function is responsible for sending the specified signal, kill_ The prototype of fasync function is as follows:

void kill_fasync(struct fasync_struct **fp, int sig, int band)

fp: fasync to operate_ struct. sig: the signal to be sent. band: set to poll when readable_ In, set to poll when writable_ OUT

2. Application processing flow

1) Register signal processing function
The prototype of signal function is as follows:

sighandler_t signal(int signum, sighandler_t handler)

signum: the signal to set the processing function.
handler: signal processing function.
2) Tell the kernel the process number of this application
Use fcntl(fd, F_SETOWN, getpid()) to tell the kernel the process number of this application.
3) Turn on asynchronous notification

flags = fcntl(fd, F_GETFL); /* Get current process status */
fcntl(fd, F_SETFL, flags | FASYNC); /* Enable the asynchronous notification function of the current process */

Set the process status to fasync through the fcntl function. After this step, the fasync function in the driver will be executed.

Application usage example

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);
	 }
}

void main()
{
	...
	fd = open(filename, O_RDWR);

	signal(SIGIO, sigio_signal_func);
	fcntl(fd, F_SETOWN, getpid()); /* Tell the kernel the process number of the current process */
	flags = fcntl(fd, F_GETFD); /* Get current process status */
	fcntl(fd, F_SETFL, flags | FASYNC);/* Set the process to enable asynchronous notification */
	
	while(1) {
		sleep(2);
	}
	
	close(fd);
}

Keywords: Embedded system

Added by always_confused on Sat, 19 Feb 2022 03:50:30 +0200