Linux Device Drivers 2nd Edition (779877), страница 27
Текст из файла (страница 27)
The PDEBUGG symbol, on the other hand, does nothing; it can be used to easily “comment” printstatements without removing them entirely.To simplify the process further, add the following lines to your makefile:# Comment/uncomment the following line to disable/enable debuggingDEBUG = y# Add your debugging flag (or not) to CFLAGSifeq ($(DEBUG),y)DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlineselseDEBFLAGS = -O2endifCFLAGS += $(DEBFLAGS)The macros shown in this section depend on a gcc extension to the ANSI C preprocessor that supports macros with a variable number of arguments. This gccdependency shouldn’t be a problem because the kernel proper depends heavilyon gcc features anyway.
In addition, the makefile depends on GNU’s version ofmake ; once again, the kernel already depends on GNU make, so this dependencyis not a problem.If you’re familiar with the C preprocessor, you can expand on the given definitionsto implement the concept of a “debug level,” defining different levels and assigning an integer (or bit mask) value to each level to determine how verbose itshould be.But every driver has its own features and monitoring needs. The art of good programming is in choosing the best trade-off between flexibility and efficiency, andwe can’t tell what is the best for you. Remember that preprocessor conditionals (aswell as constant expressions in the code) are executed at compile time, so youmust recompile to turn messages on or off. A possible alternative is to use C10222 June 2001 16:35http://openlib.org.uaDebugging by Queryingconditionals, which are executed at runtime and therefore permit you to turn messaging on and off during program execution.
This is a nice feature, but it requiresadditional processing every time the code is executed, which can affect performance even when the messages are disabled. Sometimes this performance hit isunacceptable.The macros shown in this section have proven themselves useful in a number ofsituations, with the only disadvantage being the requirement to recompile a module after any changes to its messages.Debugging by QueryingThe previous section described how printk works and how it can be used. What itdidn’t talk about are its disadvantages.A massive use of printk can slow down the system noticeably, because syslogdkeeps syncing its output files; thus, every line that is printed causes a disk operation. This is the right implementation from syslogd ’s perspective.
It tries to writeeverything to disk in case the system crashes right after printing the message; however, you don’t want to slow down your system just for the sake of debuggingmessages. This problem can be solved by prefixing the name of your log file as itappears in /etc/syslogd.conf with a minus.* The problem with changing the configuration file is that the modification will likely remain there after you are donedebugging, even though during normal system operation you do want messages tobe flushed to disk as soon as possible.
An alternative to such a permanent changeis running a program other than klogd (such as cat /proc/kmsg, as suggested earlier), but this may not provide a suitable environment for normal system operation.More often than not, the best way to get relevant information is to query the system when you need the information, instead of continually producing data. In fact,every Unix system provides many tools for obtaining system information: ps, netstat, vmstat, and so on.Two main techniques are available to driver developers for querying the system:creating a file in the /pr oc filesystem and using the ioctl driver method.
You mayuse devfs as an alternative to /pr oc, but /pr oc is an easier tool to use for information retrieval.Using the /proc FilesystemThe /pr oc filesystem is a special, software-created filesystem that is used by thekernel to export information to the world. Each file under /pr oc is tied to a kernelfunction that generates the file’s “contents” on the fly when the file is read. We* The minus is a “magic” marker to prevent syslogd from flushing the file to disk at everynew message, documented in syslog.conf(5), a manual page worth reading.10322 June 2001 16:35http://openlib.org.uaChapter 4: Debugging Techniqueshave already seen some of these files in action; /pr oc/modules, for example,always returns a list of the currently loaded modules./pr oc is heavily used in the Linux system.
Many utilities on a modern Linux distribution, such as ps, top, and uptime, get their information from /pr oc. Some devicedrivers also export information via /pr oc, and yours can do so as well. The /pr ocfilesystem is dynamic, so your module can add or remove entries at any time.Fully featured /pr oc entries can be complicated beasts; among other things, theycan be written to as well as read from. Most of the time, however, /pr oc entries areread-only files.
This section will concern itself with the simple read-only case.Those who are interested in implementing something more complicated can lookhere for the basics; the kernel source may then be consulted for the full picture.All modules that work with /pr oc should include <linux/proc_fs.h> to definethe proper functions.To create a read-only /pr oc file, your driver must implement a function to producethe data when the file is read. When some process reads the file (using the readsystem call), the request will reach your module by means of one of two differentinterfaces, according to what you registered. We’ll leave registration for later in thissection and jump directly to the description of the reading interfaces.In both cases the kernel allocates a page of memory (i.e., PAGE_SIZE bytes)where the driver can write data to be returned to user space.The recommended interface is read_ proc, but an older interface named get_infoalso exists.int (*read_proc)(char *page, char **start, off_t offset, intcount, int *eof, void *data);The page pointer is the buffer where you’ll write your data; start is used bythe function to say where the interesting data has been written in page (moreon this later); offset and count have the same meaning as in the readimplementation.
The eof argument points to an integer that must be set bythe driver to signal that it has no more data to return, while data is a driverspecific data pointer you can use for internal bookkeeping.* The function isavailable in version 2.4 of the kernel, and 2.2 as well if you use our sysdep.hheader.int (*get_info)(char *page, char **start, off_t offset, intcount);get_info is an older interface used to read from a /pr oc file. The arguments allhave the same meaning as for read_ proc. What it lacks is the pointer to reportend-of-file and the object-oriented flavor brought in by the data pointer. The* We’ll find several of these pointers throughout the book; they represent the “object”involved in this action and correspond somewhat to this in C++.10422 June 2001 16:35http://openlib.org.uaDebugging by Queryingfunction is available in all the kernel versions we are interested in (although ithad an extra unused argument in its 2.0 implementation).Both functions should return the number of bytes of data actually placed in thepage buffer, just like the read implementation does for other files.
Other outputvalues are *eof and *start. eof is a simple flag, but the use of the startvalue is somewhat more complicated.The main problem with the original implementation of user extensions to the /pr ocfilesystem was use of a single memory page for data transfer. This limited the totalsize of a user file to 4 KB (or whatever was appropriate for the host platform). Thestart argument is there to implement large data files, but it can be ignored.If your pr oc_read function does not set the *start pointer (it starts out NULL),the kernel assumes that the offset parameter has been ignored and that the datapage contains the whole file you want to return to user space.
If, on the otherhand, you need to build a bigger file from pieces, you can set *start to be equalto page so that the caller knows your new data is placed at the beginning of thebuffer. You should then, of course, skip the first offset bytes of data, which willhave already been returned in a previous call.There has long been another major issue with /pr oc files, which start is meantto solve as well. Sometimes the ASCII representation of kernel data structureschanges between successive calls to read, so the reader process could find inconsistent data from one call to the next. If *start is set to a small integer value, thecaller will use it to increment filp->f_pos independently of the amount of datayou return, thus making f_pos an internal record number of your read_ proc orget_info procedure.
If, for example, your read_ proc function is returning information from a big array of structures, and five of those structures were returned inthe first call, start could be set to 5. The next call will provide that same valueas the offset; the driver then knows to start returning data from the sixth structurein the array. This is defined as a “hack” by its authors and can be seen infs/pr oc/generic.c.Time for an example.
Here is a simple read_ proc implementation for the sculldevice:int scull_read_procmem(char *buf, char **start, off_t offset,int count, int *eof, void *data){int i, j, len = 0;int limit = count - 80; /* Don’t print more than this */for (i = 0; i < scull_nr_devs && len <= limit; i++) {Scull_Dev *d = &scull_devices[i];if (down_interruptible(&d->sem))return -ERESTARTSYS;len += sprintf(buf+len,"\nDevice %i: qset %i, q %i, sz %li\n",i, d->qset, d->quantum, d->size);for (; d && len <= limit; d = d->next) { /* scan the list */10522 June 2001 16:35http://openlib.org.uaChapter 4: Debugging Techniqueslen += sprintf(buf+len, " item at %p, qset at %p\n", d,d->data);if (d->data && !d->next) /* dump only the last item- save space */for (j = 0; j < d->qset; j++) {if (d->data[j])len += sprintf(buf+len,"% 4i: %8p\n",j,d->data[j]);}}up(&scull_devices[i].sem);}*eof = 1;return len;}This is a fairly typical read_ proc implementation.