Linux Device Drivers 2nd Edition (779877), страница 39
Текст из файла (страница 39)
When you compilethe driver, you can enable messaging to make it easier to follow the interaction ofdifferent processes.Note also, once again, the use of semaphores to protect critical regions of thecode. The scull code has to be careful to avoid going to sleep when it holds asemaphore—otherwise, writers would never be able to add data, and the wholething would deadlock. This code uses wait_event_interruptible to wait for data ifneed be; it has to check for available data again after the wait, though. Somebodyelse could grab the data between when we wake up and when we get thesemaphore back.It’s worth repeating that a process can go to sleep both when it calls schedule,either directly or indirectly, and when it copies data to or from user space.
In thelatter case the process may sleep if the user array is not currently present in mainmemory. If scull sleeps while copying data between kernel and user space, it willsleep with the device semaphore held. Holding the semaphore in this case is justified since it will not deadlock the system, and since it is important that the devicememory array not change while the driver sleeps.The if statement that follows interruptible_sleep_on takes care of signal handling.This statement ensures the proper and expected reaction to signals, which couldhave been responsible for waking up the process (since we were in an interruptible sleep).
If a signal has arrived and it has not been blocked by the process, theproper behavior is to let upper layers of the kernel handle the event. To this aim,the driver returns -ERESTARTSYS to the caller; this value is used internally by the15122 June 2001 16:36http://openlib.org.uaChapter 5: Enhanced Char Driver Operationsvirtual filesystem (VFS) layer, which either restarts the system call or returns-EINTR to user space. We’ll use the same statement to deal with signal handlingfor every read and write implementation.
Because signal_ pending was introducedonly in version 2.1.57 of the kernel, sysdep.h defines it for earlier kernels to preserve portability of source code.The implementation for write is quite similar to that for read (and, again, its firstline will be explained later). Its only ‘‘peculiar’’ feature is that it never completelyfills the buffer, always leaving a hole of at least one byte. Thus, when the buffer isempty, wp and rp are equal; when there is data there, they are always different.static inline int spacefree(Scull_Pipe *dev){if (dev->rp == dev->wp)return dev->buffersize - 1;return ((dev->rp + dev->buffersize - dev->wp) % dev->buffersize) - 1;}ssize_t scull_p_write(struct file *filp, const char *buf, size_t count,loff_t *f_pos){Scull_Pipe *dev = filp->private_data;if (f_pos != &filp->f_pos) return -ESPIPE;if (down_interruptible(&dev->sem))return -ERESTARTSYS;/* Make sure there’s space to write */while (spacefree(dev) == 0) { /* full */up(&dev->sem);if (filp->f_flags & O_NONBLOCK)return -EAGAIN;PDEBUG("\"%s\" writing: going to sleep\n",current->comm);if (wait_event_interruptible(dev->outq, spacefree(dev) > 0))return -ERESTARTSYS; /* signal: tell the fs layer to handle it */if (down_interruptible(&dev->sem))return -ERESTARTSYS;}/* ok, space is there, accept something */count = min(count, spacefree(dev));if (dev->wp >= dev->rp)count = min(count, dev->end - dev->wp); /* up to end-of-buffer */else /* the write pointer has wrapped, fill up to rp-1 */count = min(count, dev->rp - dev->wp - 1);PDEBUG("Going to accept %li bytes to %p from %p\n",(long)count, dev->wp, buf);if (copy_from_user(dev->wp, buf, count)) {up (&dev->sem);return -EFAULT;}15222 June 2001 16:36http://openlib.org.uaBlocking I/Odev->wp += count;if (dev->wp == dev->end)dev->wp = dev->buffer; /* wrapped */up(&dev->sem);/* finally, awaken any reader */wake_up_interruptible(&dev->inq); /* blocked in read() and select() *//* and signal asynchronous readers, explained later in Chapter 5 */if (dev->async_queue)kill_fasync(&dev->async_queue, SIGIO, POLL_IN);PDEBUG("\"%s\" did write %li bytes\n",current->comm, (long)count);return count;}The device, as we conceived it, doesn’t implement blocking open and is simplerthan a real FIFO.
If you want to look at the real thing, you can find it in fs/pipe.c,in the kernel sources.To test the blocking operation of the scullpipe device, you can run some programson it, using input/output redirection as usual. Testing nonblocking activity is trickier, because the conventional programs don’t perform nonblocking operations.The misc-pr ogs source directory contains the following simple program, callednbtest, for testing nonblocking operations. All it does is copy its input to its output,using nonblocking I/O and delaying between retrials. The delay time is passed onthe command line and is one second by default.int main(int argc, char **argv){int delay=1, n, m=0;if (argc>1) delay=atoi(argv[1]);fcntl(0, F_SETFL, fcntl(0,F_GETFL) | O_NONBLOCK); /* stdin */fcntl(1, F_SETFL, fcntl(1,F_GETFL) | O_NONBLOCK); /* stdout */while (1) {n=read(0, buffer, 4096);if (n>=0)m=write(1, buffer, n);if ((n<0 || m<0) && (errno != EAGAIN))break;sleep(delay);}perror( n<0 ? "stdin" : "stdout");exit(1);}15322 June 2001 16:36http://openlib.org.uaChapter 5: Enhanced Char Driver Operationspoll and selectApplications that use nonblocking I/O often use the poll and select system calls aswell.
poll and select have essentially the same functionality: both allow a processto determine whether it can read from or write to one or more open files withoutblocking. They are thus often used in applications that must use multiple input oroutput streams without blocking on any one of them. The same functionality isoffered by two separate functions because they were implemented in Unix almostat the same time by two different groups: select was introduced in BSD Unix,whereas poll was the System V solution.Support for either system call requires support from the device driver to function.In version 2.0 of the kernel the device method was modeled on select (and no pollwas available to user programs); from version 2.1.23 onward both were offered,and the device method was based on the newly introduced poll system callbecause poll offered more detailed control than select.Implementations of the poll method, implementing both the poll and select systemcalls, have the following prototype:unsigned int (*poll) (struct file *, poll_table *);The driver’s method will be called whenever the user-space program performs apoll or select system call involving a file descriptor associated with the driver.
Thedevice method is in charge of these two steps:1.Call poll_wait on one or more wait queues that could indicate a change in thepoll status.2.Return a bit mask describing operations that could be immediately performedwithout blocking.Both of these operations are usually straightforward, and tend to look very similarfrom one driver to the next. They rely, however, on information that only thedriver can provide, and thus must be implemented individually by each driver.The poll_table structure, the second argument to the poll method, is usedwithin the kernel to implement the poll and select calls; it is declared in<linux/poll.h>, which must be included by the driver source. Driver writersneed know nothing about its internals and must use it as an opaque object; it ispassed to the driver method so that every event queue that could wake up theprocess and change the status of the poll operation can be added to thepoll_table structure by calling the function poll_wait:void poll_wait (struct file *, wait_queue_head_t *, poll_table *);15422 June 2001 16:36http://openlib.org.uapoll and selectThe second task performed by the poll method is returning the bit mask describing which operations could be completed immediately; this is also straightforward.For example, if the device has data available, a read would complete withoutsleeping; the poll method should indicate this state of affairs.
Several flags (definedin <linux/poll.h>) are used to indicate the possible operations:POLLINThis bit must be set if the device can be read without blocking.POLLRDNORMThis bit must be set if ‘‘normal’’ data is available for reading. A readable devicereturns (POLLIN | POLLRDNORM).POLLRDBANDThis bit indicates that out-of-band data is available for reading from thedevice. It is currently used only in one place in the Linux kernel (the DECnetcode) and is not generally applicable to device drivers.POLLPRIHigh-priority data (out-of-band) can be read without blocking. This bit causesselect to report that an exception condition occurred on the file, because selectreports out-of-band data as an exception condition.POLLHUPWhen a process reading this device sees end-of-file, the driver must set POLLHUP (hang-up).
A process calling select will be told that the device is readable,as dictated by the select functionality.POLLERRAn error condition has occurred on the device. When poll is invoked, thedevice is reported as both readable and writable, since both read and writewill return an error code without blocking.POLLOUTThis bit is set in the return value if the device can be written to without blocking.POLLWRNORMThis bit has the same meaning as POLLOUT, and sometimes it actually is thesame number. A writable device returns (POLLOUT | POLLWRNORM).POLLWRBANDLike POLLRDBAND, this bit means that data with nonzero priority can be written to the device. Only the datagram implementation of poll uses this bit, sincea datagram can transmit out of band data.It’s worth noting that POLLRDBAND and POLLWRBAND are meaningful only withfile descriptors associated with sockets: device drivers won’t normally use theseflags.15522 June 2001 16:36http://openlib.org.uaChapter 5: Enhanced Char Driver OperationsThe description of poll takes up a lot of space for something that is relatively simple to use in practice.