Though the combination of blocking and nonblocking operations and the select method are sufficient for querying the device most of the time, some situations aren’t efficiently managed by the techniques we’ve seen so far. Let’s imagine, for example, a process that executes a long computational loop at low priority, but needs to process incoming data as soon as possible. If the input channel is the keyboard, you are allowed to send a signal to the application (using the ``INTR'' character, usually Ctrl-C), but this signalling ability is part of the tty layer, which isn’t attached to general char devices. What we need for asynchronous notification is something different. Furthermore, any input data should generate an interrupt, not just Ctrl-C.
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. The user ID of a file’s
owner is stored in filp->f_owner
by the fcntl system call
when an application invokes the F_SETOWN
command.
Additionally, the user programs
must set the FASYNC
flag in the device by
means of another fcntl in order to actually enable asynchronous
notification.
After these two calls have been executed, the input file
generates a SIGIO
signal whenever new data arrives. The
signal is sent to the process (or process group, if the value is negative)
stored in filp->f_owner
.
For example, the following lines enable asynchronous notification
to the current process for the stdin
input file:
signal(SIGIO, &input_handler); /* the dirty way; /* /* sigaction() is better */ fcntl(0, F_SETOWN, getpid()); oflags=fcntl(0, F_GETFL); fcntl(0, F_SETFL, oflags | FASYNC);
The program named asynctest in the sources is a simple
program that reads stdin
as shown. It can be used to
test the asynchronous capabilities of scullpipe. The
program is similar to cat, but doesn’t terminate on end-of-file; it responds only to input, not to 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. For example, pipes and FIFOs don’t support it, at least in
the current kernels. Mice offer asynchronous notification (although
not in 1.2), because some programs expect a mouse to be able to
send SIGIO
like a tty does.
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 select to find out what happened.
A more relevant topic for us is how the device driver can implement asynchronous signalling. The following list details the sequence of operations from the kernel’s point of view:
When F_SETOWN
is invoked, nothing happens,
except that a value is assigned to filp->f_owner
.
When F_SETFL
is executed to turn on
FASYNC
, the driver’s fasync method is
called. This method is called whenever the value of
FASYNC
is changed in filp->f_flags
, to notify
the driver of the change so it can respond properly. The flag
is zeroed by default when the file is opened. We’ll
look at the standard implementation of the driver method
soon.
When data arrives, all the processes registered for
asynchronous notification must be sent a SIGIO
signal.
While implementing the first step is trivial--there’s nothing to do on the driver’s part--the other steps involve maintaining a dynamic data structure to keep track of the different asynchronous readers; there might be several of these readers. This dynamic data structure, however, doesn’t depend on the particular device involved, and the kernel offers a suitable general-purpose implementation so you don’t have to rewrite the same code in every driver.
Unfortunately, such an implementation is not included in 1.2 kernels. It’s not easy to implement asynchronous notification in a module for older kernel versions, as you have to create your own data structure. The scull module, for simplicity, doesn’t offer asynchronous notification for older kernels.
The general implementation offered by Linux is based on one data
structure and two functions (to be called in the steps described
above). The header that declares related material is
<linux/fs.h>
--nothing new--and the data structure is
called struct fasync_struct
. As we did with wait
queues, we need to insert a pointer to the structure
in the device-specific data structure. Actually, we’ve
already seen such a field in the section Section 5.2.5.
The two functions to call correspond to the following prototypes:
int fasync_helper(struct inode *inode, struct file *filp, int mode, struct fasync_struct **fa); void kill_fasync(struct fasync_struct *fa, int sig);
The former is invoked to add or remove files to the list of interested
processes when the FASYNC
flag changes for an open
file, while the latter should be called when data arrives.
Here’s how scullpipe implements the fasync method:
int scull_p_fasync (struct inode *inode, struct file *filp, int mode) { Scull_Pipe *dev = filp->private_data; return fasync_helper(inode, 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 struct fasync_struct *
(here
&dev->async_queue
) and only the driver can provide that
information.
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 statement appears in the write method of scullpipe.
if (dev->async_queue) kill_fasync (dev->async_queue, SIGIO); /* asynchronous readers */
It might appear that we’re done, but
there’s still one thing missing. We must invoke our fasync
method upon file close to remove the file being closed from the list of
active asynchronous readers. While this call is required only if
filp->f_flags
has FASYNC
set, calling the function
anyway doesn’t hurt and is the usual implementation. The
following lines, for example, are part of the close method for
scullpipe:
/* remove this filp from the asynchronously notified filp's */ scull_p_fasync(inode, filp, 0);
The data structure underlying asynchronous notification is almost
identical to the structure struct wait_queue
, because both
situations involve waiting on an event. The difference is that struct file
is used in place of struct task_struct
.
The struct file
in the queue is then used to retrieve
f_owner
, in order to signal the process.
3.133.87.156