1. Concept:
Asynchronous notification mechanism: Once the device is ready, the application is actively notified so that the application does not need to query the device state at all, and is a "signal-driven asynchronous I/O". The signal is a simulation of the interrupt mechanism at the software level, in principle, a process receives a signal that the processor receives an interrupt request can be said to be the same. The signal is asynchronous, and a process does not have to wait for the signal to arrive by any action, in fact, the process does not know when the signal will arrive.
2. We tried to analyze the asynchronous notification mechanism in two ways:
From the perspective of the user program: In order to start the asynchronous notification mechanism of the file, the user program must perform two steps. First, they specify a process as the owner of the file. When the process uses the FCNTL system call to execute
F_setown the command, the process ID number of the main process is saved in Filp->f_owner. This step is necessary to let the kernel know which process to notify. Then in order to really start the asynchronous notification mechanism, the user program must also
Set the FASYNC flag in the device, which is done through the Fcntl F_SETFL command. After performing these two steps, the input file can request a Sigio signal to be sent when the new data arrives. The signal is sent to the store at filp-
The process in >f_owner (if a negative value is a process group).
In the user program, in order to capture the signal, you can use the signal () function to set the corresponding signal processing function:
1 void (*signal (intvoid (*handler)))) (int);
This function prototype is more difficult to understand, it can be decomposed into:
1 void (*sighandler_t) (int); // message-handling functions 2 sighandler_t signal (int Signum, sighandler_t handler)); // connection signal and message processing functions
The first parameter specifies the value of the signal, and the second parameter specifies the processing function for the preceding signal value, if sig_ign, which indicates that the signal is ignored and, if SIG_DFL, indicates that the signal is processed by the system default;
Number, the function will be executed after the signal is captured. If the signal () call succeeds, it returns the handler value of the last handler that was bound to the signal signum, and the failure returns SIG_ERR.
1 fcntl (Stdin_fileno, F_setown, Getpid ()); // set this process to the owner of the Stdin_fileno file, without this step the kernel will not know which process to send the signal to 2 oflags = Fcntl (Stdin_fileno, F_GETFL); // get the f_flags of a device file 3 // To enable the asynchronous notification mechanism, you also need to set the FASYNC flag for the device
We first through the kernel source code, analyze the above implementation principle.
1 app:fcntl ()2 kernel:sys_fcntl ()3 do_fcntl ()4 Switch(cmd) {5 ...6 CaseF_GETFL:7Err = filp->f_flags;//Return file Flags8 Break; 9 CaseF_SETFL:TenErr = SETFL (FD, FILP, ARG);//go to call the SETFL function One Break; A ... - CaseF_setown: -Err = F_setown (Filp, ARG,1);//go to call the F_setown function the Break; - ... - default: - Break; + } - returnErr
Let's take a look at the internal implementation of the F_setown function: Set the main process of the file
1 intF_setown (structFile *filp, unsignedLongArgintForce )2 {3 ...4PID = Find_pid (WHO);//gets the PID of the current process5result = __f_setown (FILP, PID, type, force);//Internal main call F_modown function6 ...7 }8 Static voidF_modown (structFile *filp,structPID *pid,enumPid_type type,uid_t uid, uid_t Euid,intForce )9 {Ten ... One if(Force | |!filp->f_owner.pid) {//set the corresponding Pid,uid,euid APut_pid (filp->f_owner.pid); -Filp->f_owner.pid =get_pid (PID); -Filp->f_owner.pid_type =type; theFilp->f_owner.uid =uid; -Filp->f_owner.euid =Euid; - } - ... +}
Let's take a look at the internal implementation of the SETFL function:
1 Static intSETFL (intFdstructFile * Filp, unsignedLongArg)2 {3 ...4 if(arg ^ filp->f_flags) & Fasync) {//that is to say, the FASYNC flag changed from 0 to 1, only to be true. 5 if(Filp->f_op && filp->f_op->Fasync) {6Error = Filp->f_op->fasync (FD, FILP, (ARG & fasync)! =0);//the Fasync () function of the driver is called7 if(Error <0)8 Goto out;9 }Ten } One ... A}
Consider from the driver perspective:
The Fasync method that invokes the driver when the application executes F_SETFL enable Fasync. As long as the Fasync identity in the filp->f_flags changes, the method is called to notify the driver of the change so that it can
Respond correctly. When the file is opened, the FASYNC flag is implicitly considered clear. When the data arrives, all processes registered as asynchronous notifications are sent a sigio signal.
This general approach to Linux is based on a data structure and two functions:
1 extern intFasync_helper (int,structFile *,int,structFasync_struct * *);2 //When the fasync flag of an open file is modified, the Fasync method that invokes the driver calls the Fasync_helper function indirectly to join the current process to the driver's asynchronous notification waiting queue. 3 extern voidKill_fasync (structFasync_struct * *,int,int);4 //When the device is accessible, you can use the Kill_fasync function to signal all related processes. The process then invokes the bound message-handler function.
Analysis of the internal implementation of Fasync_helper
1 intFasync_helper (intFdstructFile * Filp,intOnstructFasync_struct * *Fapp)2 {3 structFasync_struct *FA, * *FP;4 structFasync_struct *New=NULL;5 intresult =0;6 if(ON) {7 New= Kmem_cache_alloc (Fasync_cache, Slab_kernel);//Create object, slab allocator8 if(!New)9 return-Enomem;Ten } OneWRITE_LOCK_IRQ (&fasync_lock); A //traverse the entire asynchronous notification queue to see if a corresponding file pointer exists - for(fp = Fapp; (FA = *FP)! = NULL; FP = &fa->Fa_next) { - if(Fa->fa_file = = FILP) {//already exists the if(ON) { -FA->FA_FD = FD;//file Description assigned value//Note: It is not clear why only the file descriptor needs to be updated, without updating the file pointer -Kmem_cache_free (Fasync_cache,New);//destroying the object you just created -}Else { +*FP = fa->fa_next;//Continue traversal -Kmem_cache_free (Fasync_cache, FA);//remove non-target objects This is used for application masking asynchronous notifications. +result =1; A } at Goto out;//found the - } - } - //see below to know that the so-called process is added to the asynchronous notification queue - //The fact is that you associate a file pointer to an asynchronous struct object and then mount the object in the asynchronous notification queue (which is also the principle of waiting for the queue) - //So how do you know which process to send the last signal? Let's look at the Kill_fasync function behind it. in if(ON) {//does not exist - New->magic =fasync_magic; to New->fa_file = FILP;//Specify the file pointer + New->FA_FD = FD;//specifying file descriptors - New->fa_next = *fapp;//mount in the asynchronous notification queue the*fapp =New;//Mount *result =1; $ }Panax Notoginseng out: -WRITE_UNLOCK_IRQ (&fasync_lock); the returnresult; +}
See how the Kill_fasync function notifies the specified process of a signal:
1 void__kill_fasync (structFasync_struct *fa,intSigintband)2 {3 while(FA) {4 ...5Fown = &fa->fa_file->f_owner;//This is the answer to the above question, if you know which process, through the asynchronous object's file pointer to know its owner process6 /*Don ' t send Sigurg to processes which had not set a queued Signum:sigurg had its own default signallingmechanism.
*/7 if(! (sig = = Sigurg && Fown->signum = =0))8Send_sigio (Fown, FA->FA_FD, band);//Send Signal9FA = fa->Fa_next;Ten ... One } A}
Summary: The application uses Fcntl () to set the PID and Fasync flags for the current process. The driver's Fasync () is then called, which is Fasync_helper (). Then apply and set the FASYNC_STRUCT structure to mount the structure to the driver
FASYNC_STRUCT structure linked list. When the device is available, the driver uses Kill_fasync (), finds all the wait processes from the Fasync_struct list, and then calls Send_sigio to send the corresponding message to the process. When a process receives a message, it jumps to the message-handler function that is bound to the message.
This article source code based on the kernel source version for linux-2.6.22.6
Reference: https://www.cnblogs.com/tureno/articles/6059711.html
Analysis of asynchronous notification mechanism of Linux