Linux Device Drivers (3rd Edition) [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Linux Device Drivers (3rd Edition) [Electronic resources] - نسخه متنی

Jonathan Corbet, Greg Kroah-Hartman, Alessandro Rubini

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








3.6. scull's Memory Usage


Before introducing the read and


write operations, we'd better
look at how and why scull performs memory
allocation. "How" is needed to
thoroughly understand the code, and
"why" demonstrates the kind of
choices a driver writer needs to make, although
scull is definitely not typical as a device.

This section deals only with the memory allocation policy in
scull and doesn't show the
hardware management skills you need to write real drivers. These
skills are introduced in Chapter 9 and Chapter 10. Therefore, you can skip this
section if you're not interested in understanding
the inner workings of the memory-oriented scull
driver.

The region of memory used by scull, also called
a device, is variable in length. The more you
write, the more it grows; trimming is performed by overwriting the
device with a shorter file.

The scull driver introduces two core functions
used to manage memory in the Linux kernel. These functions, defined
in <linux/slab.h>, are:

void *kmalloc(size_t size, int flags);
void kfree(void *ptr);

A call to kmalloc attempts to allocate
size bytes of memory; the return value is a
pointer to that memory or NULL if the allocation
fails. The flags argument is used to describe how
the memory should be allocated; we examine those flags in detail in
Chapter 8. For now, we always
use GFP_KERNEL. Allocated memory should be freed
with kfree. You should never pass anything to
kfree that was not obtained from
kmalloc. It is, however, legal to pass a
NULL pointer to kfree.


kmalloc
is not the most efficient way to allocate large areas of memory (see
Chapter 8), so the
implementation chosen for scull is not a
particularly smart one. The source code for a smart implementation
would be more difficult to read, and the aim of this section is to
show read and write, not
memory management. That's why the code just uses
kmalloc and kfree without
resorting to allocation of whole pages, although that approach would
be more efficient.

On the flip side, we
didn't want to limit the size of the
"device" area, for both a
philosophical reason and a practical one. Philosophically,
it's always a bad idea to put arbitrary limits on
data items being managed. Practically, scull can
be used to temporarily eat up your system's memory
in order to run tests under low-memory conditions. Running such tests
might help you understand the system's internals.
You can use the command cp /dev/zero /dev/scull0
to eat all the real RAM with scull, and you can
use the dd utility to choose how much data is
copied to the scull device.

In scull, each device is a linked list of

pointers,
each of which points to a scull_dev structure.
Each such structure can refer, by default, to at most four million
bytes, through an array of intermediate pointers. The released source
uses an array of 1000 pointers to areas of 4000 bytes. We call each
memory area a quantum and the array (or its
length) a quantum
set

. A scull device
and its memory areas are shown in Figure 3-1.


Figure 3-1. The layout of a scull device

The chosen numbers are such that writing a single byte in
scull consumes 8000 or 12,000 thousand bytes of
memory: 4000 for the quantum and 4000 or 8000 for the quantum set
(according to whether a pointer is represented in 32 bits or 64 bits
on the target platform). If, instead, you write a huge amount of
data, the overhead of the linked list is not too bad. There is only
one list element for every four megabytes of data, and the maximum
size of the device is limited by the computer's
memory size.

Choosing the appropriate values for the quantum and the quantum set
is a question of policy, rather than mechanism, and the optimal sizes
depend on how the device is used. Thus, the
scull driver should not force the use of any
particular values for the quantum and quantum set sizes. In
scull, the user can change the values in charge
in several ways: by changing the macros
SCULL_QUANTUM and SCULL_QSET in
scull.h at compile time, by setting the integer
values scull_quantum and
scull_qset at module load time, or by changing
both the current and default values using ioctl
at runtime.

Using a macro and an integer value to allow both compile-time and
load-time configuration is reminiscent of how the major number is
selected. We use this technique for whatever value in the driver is
arbitrary or related to policy.

The only question left is how the default numbers have been chosen.
In this particular case, the problem is finding the best balance
between the waste of memory resulting from half-filled quanta and
quantum sets and the overhead of allocation, deallocation, and
pointer chaining that occurs if quanta and sets are small.
Additionally, the internal design of kmalloc
should be taken into account. (We won't pursue the
point now, though; the innards of kmalloc are
explored in Chapter 8.) The
choice of default numbers comes from the assumption that massive
amounts of data are likely to be written to
scull while testing it, although normal use of
the device will most likely transfer just a few kilobytes of data.

We have already seen the scull_dev structure that
represents our device internally. That structure's
quantum and qset fields hold
the device's quantum and quantum set sizes,
respectively. The actual data, however, is
tracked by a different structure,
which we call struct
scull_qset

:

struct scull_qset {
void **data;
struct scull_qset *next;
};

The next code fragment shows in practice how
struct scull_dev and
struct scull_qset are used to
hold data. The function scull_trim is in charge
of freeing the whole data area and is invoked by
scull_open when the file is opened for writing.
It simply walks through the list and frees any quantum and quantum
set it finds.

int scull_trim(struct scull_dev *dev)
{
struct scull_qset *next, *dptr;
int qset = dev->qset; /* "dev" is not-null */
int i;
for (dptr = dev->data; dptr; dptr = next) { /* all the list items */
if (dptr->data) {
for (i = 0; i < qset; i++)
kfree(dptr->data[i]);
kfree(dptr->data);
dptr->data = NULL;
}
next = dptr->next;
kfree(dptr);
}
dev->size = 0;
dev->quantum = scull_quantum;
dev->qset = scull_qset;
dev->data = NULL;
return 0;
}

scull_trim is also used in the module cleanup
function to return memory used by scull to the
system.


    / 202