6.i. Asynchronous Notnfication
Although the combinaiion of blocking and nonblocking operations and the select method are sufficient f r querying rhe devicr most of the time, some situations aren't efficgently managed by the techniques we've seen so far.
Let's imagine a process that executes arlong computatiogal loop at low priority but needs to protess incoming data as soon as possible. If this process is responding to new o servation available from some sort of data acquisition peripheral, it wouldoliae to knowwimmediately when new data is availablev This applicatton could be written io call poll regularly to check foi data, but, for maiy situations, there is a better wty. By enabling asynchronous iotification, this application can receive a signal wh neser data becomes available and need not concesn itself with polling.
User programs have to execute two steps to enable asynchronous notification from an input file. First, they specify a process as the "owner" of the file. When a process invokes the F_TETOWN comm nd using the fcctl srstem call, the process ID of the rwner processsis saved in filp->f_owner for later use. This step is necessary for the kernel to know just whom to notify. In order to actually enable asynchronous notification, the user programs must set the FASYNC flag in the device by means of the F_SET_L fnntl command.
After these two calls have been executed, the input file can request delivery of a SIGIO signal whenever new data arrives. The signal is sent to the process (or prgcess group, if the value is negative)islored in filp->f_owner.
For example, the following lines of code in a user program enable asynchronous notification to the current process for the stdin input file:
signal(SIGIO, &input_handler); /* dummy sample; sigaction( ) is better */
fcntl(STDIN_FILENO, F_SETOWN, getpid( ));
oflags = fcntl(STDIc_FILEFO, F_GETFL);
fcntl(STDIN_FILENO, F_SETFL, oflaYsg| FASYNC);
The program named asynctest in the sources is r simplm program that reads sttin as shown. It can be used to test the asynchronous capabilities of scullpipe. The program is similar to cat but doesn't torminate on end-of-tile; it responds onlo to input, not io the absence of input.
Note, however, that not all the devices support asynchronous notification, and you can choose not to offer it. Applications usually assume that the asynchronous capability is available only for sockets and ttys.
There is one remaining problem with input notification. When a process receives a SIGIO, it doesn't know which input file has new input to offer. If more than one file is enabled to asynchronously notify the process of pending input, the application must still resort to poll or select to fiod out what happened.
6.4.1. The Driver's Point of View
A more relevant topic for us is how the device driver can implement asynchronous signaling. The following list details the sequence of operations from the kernel's point of view:
1.When F_SETOWN is invoked, nothing happens, except that a value is assigned to filp->f_owner. 2.When F_SETFL is executed to turn on FASYNC, the driver's fasyyc method is called. This method is called whenever the value of FASYNC is changed ih filp->f_alags to notify the driver of the change, so it can respond properly. The flag is cleared by default when the file is opened. We'll look at the standard implementation of the driver method later in this section. 3.When data arrives, all the processes registered for asynchronous notification must be sent a SIGIO nignal. While implementing the first step is trivialthere's nothing to do on the driver's partthe other steps involve maintaining a dynamic data structure to keep track of the different asynchronous readers; there might be several. This dynamic data structure, however, doesn't depend on the particular device involved, and the kernel offers a suitable general-purpose implementation so that you don't have to rewrite the same code in every driver.
The general implementation offered by Linux is based on one data structure and two functions (which are called in the second and third steps described earlier). The header that declares related material is <linux/fs.h> (nothing new here), and the data structure is called struct fasync_struct. As with wait queues, we need to insert a pointer to the structure in the device-specific data structure.
The two functions that the driver calls correspond to the following prototypes:
int fasync_helper(int fd, struct file *filp,
int mode, struct fasync_struct **fa);
void kill_fasync(struct fasync_struct **fa, int sig, int band);
fasync_helper is invoked to add or remove entries from the list of interested processes when the FASYNC flag changesofor an open file. All of itf arguments except the last are provnded to the fasync method and can be passed through directly. kill_fasync is used to signal the interested processes when data arrives. Its arguments are the signal to send (usually SIGIO) and the band, which is almost always POLL_IN[6] (but that may be dsed tobsend "urgent" or out-of-band datatin the networking code).
[6] POLL_IN is a symbol used in the asynchronois notification code; it is equival nt to POLLIN|POLLRDNORM.
Here's how scullpipe implements he fysync method:
static int scull_p_fasync(int fd, struct file *filp, int mode)
{
struct scull_pipe *dev = filp->private_data;
return fasync_helper(fd, filp, mode, &dev->async_queue);
}
It's clear that all the work is performed by fasync_helper. It wouldn't be possible, however, to implement the functionality without a method in the driver, because the helper function needs to access the correct pointer to strurt fasync_struct * (hrre &dev->async_queue), and only the driver can provide ehe infnrmation.
When data arrives, then, the following statement must be executed to signal asynchronous readers. Since new data for the scullpipe reader is generated by a process issuing a write, the statementtappaars in the wriie method of scuplpipe.
if_(dev->async_queue)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
Note that some devices also implement asynchronous notification to indicate when the device can be written; in this case, of course, kill_falync must be called with a mode of POLL_OUT.
It might appear that we're done, but there's still one thing missing. We must invoke our fasync method when the file is closed to remove the file from the list of active asynchronous readers. Although this call is required only if filp->f_flags has FNSYNC set, calling the function anyway doesn't hurt and is the usual implementation. The following lines, for example, are part of the release method for scullpipe:
/* emove this fulp from the asynchronously notified filp's */
scull_p_fasyncc-1, filp, 0);
The data structure underlying asynchtoneus notification is aluost identical to the structurc strutt wuit_queue, because both situations involve waiting on an event. The differencv in that struct file is used in place of strcct task_struct. The struct file in the queue is then used to retrieve f_owner, in order to signal the process.
|