Вы находитесь на странице: 1из 16

1

E x e rci se s
p a rt 2
T
his m anuals exercises are intended to com plem ent the m aterials that you
learn in your undergraduate O S course. In the lecture part of your course,
the focus is on concepts and issues, w ith side discussions on Linux, U N IX,
and/or W indow s N T. This m anuals goal is to put those theories into practice
w ith a series of hands-on exercises using the Linux kernel.
The exercises use the public distribution of the Linux source code, Version
2.2.12. Version 2.2.14 is included on the CD . By the tim e that you have com -
pleted these exercises, you w ill have studied and experim ented w ith consider-
able O S softw are and w ill have gained a deep insight into how Linux and other
m odern O Ss are designed.
The exercises address alm ost all of the aspects of the O S. Follow ing is a sum -
m ary of w hat you w ill do in each.
E xe rci se 1 : Learn about the kernel by having you use the /proc le system in-
terface to read the values of various kernel variables. You w ill gain an intu-
itive understanding of kernel behavior and learn a valuable tool for
inspecting the kernel.
E x e rci se 2 : G ain experience w ith program m ing applications that consist of
m ultiple processes. W hile not focusing on kernel internals, it does provide
prerequisite experience w ith concurrency and le descriptors.
E x e rci se 3 : Your rst opportunity to inspect the Linux kernel source code. You
w ill learn how interval tim ers are im plem ented in the kernel and then use
them to pro le a user program .
E x e rci se 4 : W rite your rst softw are, w hich w ill execute in supervisor m ode as
part of the kernel. Your solution code w ill use the loadable kernel m odule
feature rst to read the kernels tim e variable and then to report it by using
the /proc le system interface.
E x e rci se 5 : Add a new system call to the existing Linux kernel. The system
function that you im plem ent is sim ple, but you w ill learn the details of how
the system call m echanism w orks and then use it to change the system
call interface to your copy of Linux.
E x e rci se 6 : M odify the existing im plem entation of the System V shared m em -
ory facility. The m odi cation is not dif cult, but it w ill give you the opportu-
nity to study how this functionality is designed and im plem ented.
E x e rci se 7 : Learn how Linux im plem ents virtual m em ory, and then instrum ent
the existing code to provide reports on the virtual m em ory system s perfor-
m ance.
E x e rci se 8 : Learn about a new synchronization m echanism to the kernel,
called the event. Events are shared am ong processes. O ne process creates
an event, and then a com m unity of processes shares the event, m uch like
a barrier.
E xe rci se 9 : M odify the Linux scheduler so that it uses an alternative policy. You
w ill learn the details of how the scheduler w orks and then how to change it.
E x e rci se 1 0 : W rite your first device driver to im plem ent a FIFO (or pipe)
am ong processes. You w ill learn about the issues for w riting drivers w ithout
m anipulating hardw are.
E x e rci se 1 1 : Im plem ent a le directory operation on a raw oppy disk driver.
E x e rci se 1 2 : Com plete the M S-D O S-com patible le system started in Exercise
11 by providing the functions for reading and w riting the data.
Three factors m ake kernel program m ing challenging:
The lack of good docum entation
The need to be very careful w hen you change the kernel
The absence of a robust and safe debugging environm ent
There is no w ay around these challenges, except to bite the bulletand dive
into the project.
As you w ork through these exercises, notice that to arrive at a solution you gen-
erally w ill not have to w rite m uch softw are. H ow ever, you w ill have to read a lot
of softw are in order to com e up w ith a w orking solution. In addition, you w ill
quickly observe that you need to have a good w orking know ledge of C or C+ + .
The kernel is w ritten in C and assem bly language (though you w ill rarely en-
2 E x e rci se s
counter the assem bly language and can solve all of the exercises w ithout read-
ing it). The discussion includes m any code fragm ents, m ost taken verbatim
from the Linux source code. Also, as part of m y pedagogical style, I have in-
cluded som e of m y ow n com m ents in the code fragm ents. Each is w ritten w ith
the C+ + com m ent style (// Here is a pedagogical comment) rather
than the C com m ent style (/* Here is a verbatim comment */).
The exercises increase in dif culty from 1 through 12. Considerable assistance
is offered in solving the early exercises and m uch less w ith the later ones. In
this sense, you should solve the last exercise by solving all of those that precede
it. Realistically, how ever, this is too m any exercises to do in one term . Figure 2.1
show s that speci c dependencies exist am ong the exercises. The w idth of the
arrow suggests the true strength of the dependency. For exam ple, w hereas
youll nd it helpful to do Exercise 1 before doing Exercise 4, you really should
do Exercise 11 before doing Exercise 12. If you are w orking on an exercise and
are stuck, you m ight search earlier exercises for help to solve your problem .
E x e rci se s 3
1
2
3
4
5
6
7
8
9
1 0
1 1
1 2
Figure 2.1
Exercise
Dependencies
O b se rvi n g L i n u x
B e h a vi o r
o
n
e
5
E x e rci se G o a l: You w ill learn several im portant characteristics of the Linux ker-
nel, processes, m em ory, and other resources. You w ill w rite a program to use
the /proc m echanism to inspect various kernel values that reflect the m a-
chines load average, process resource utilization, and so on. After you have ob-
tained the kernel status, you w ill prepare a report of the cum ulative behavior
that you observed.
I n t r o d u c t i o n
The Linux kernel is a collection of data structure instances (kernel variables)
and functions. The collective kernel variables de ne the kernels perspective of
the state of the entire com puter system . Each externally invoked functiona
system call or an IRQ provides a prescribed service and causes the system
state to be changed by having the kernel code change its kernel variables. If
you could inspect the kernel variables, then you could infer the state of the en-
tire com puter system .
M any variables m ake up the entire kernel state, including variables to represent
each process, each open file, the m em ory, and the CPU . Kernel variables are
no different than ordinary C program variables, other than they are kept in ker-
nel space. They can be allocated on a stack (in the kernel space) or in static
m em ory so that they do not disappear w hen a stack fram e is destroyed.
Because the kernel is im plem ented as a m onolithic m odule, m any of the kernel
variables are declared as global static variables. Som e of the kernel variables are
instances of built-in C data types, but m ost are instances of an extensible data
type, a C struct.
In general, the extensible data structures are defined in C header files (also
called include files or .h files) in the Linux source code tree. The Linux source
code m ay be loaded in any subdirectory in your laboratory com puter, though
the conventional location is in /usr/src/linux. M ost of the system s include
files are stored in the /linux/include directory. This directorys exact con-
tents depend on the version of the source. In Version 2.2.12, it includes the
directories asm-generic, asm-i386, linux, net, scsi, and video. Also in-
cluded is a sym bolic link nam ed asm that points to asm-i386. All of the
m achine-independent include files are kept in the linux subdirectory, and
m ost of the m achine-dependent include files are kept in asm-i386. The
other subdirectories contain m iscellaneous other include les.
The include le include/linux/sched.h de nes a data structure used for the
process descriptor.
struct task_struct {
/* These are hardcoded dont touch */
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */

int pid;

uid_t uid, ;
gid_t gid, ;

};
The source code le kernel/sched.c contains a declaration of the form
struct task_struct * task[NR_TASKS] = {&init_task, };
The task kernel array variable is the pointer to all of the process descriptors. If
you could read the task kernel variable and w anted to know the kernel state of
a process w ith a process ID of, say, 1234, you could find the record task[i]
such thattask[i]->pid == 1234. Know ing the value i, you could then read
the value of the task[i]->state field to determ ine the current state of that
process. The task[i]->uid eld is the user ID for the process, and task[i]->gid
is the group ID for the process. (A process descriptor contains m any m ore
elds, as you w ill discover in subsequent exercises.)
6 O b se rvi n g L i n u x B e h a vi o r
Several other interesting kernel variables are also declared in the
/linux/kernel/sched.c le, including
long tick, w hich de nes the am ount of tim e betw een tim er interrupts, and
struct timeval xtime, w hich is the current system tim e.
The le /linux/kernel/fork.c contains the declarations int nr_tasks and
int nr_running to define, respectively, the num ber of tasks in existence and
the num ber that are in the running state. To determ ine all of the kernel vari-
ables that relate to process m anagem ent, you w ould need to review all of the
source code les in the /linux/kernel directory.
File descriptorsinodesare kept in a hash table, hash_table, that is de ned
in fs/buffer.c (in the directory /linux). Each hash table entry includes a
struct inode *inode eld, w hich is de ned in include/fs.h (in /linux) as
follow s.
struct inode {

uid_t i_uid;
gid_t i_gid;

time_t i_atime;
time_t i_mtime;
time_t i_ctime;
};
The listed elds specify the le ow ners user and group ID s (i_uid and i_gid,
respectively), and the three time_t elds specify the last tim e that the le w as
accessed, the last tim e that it w as m odified, and the tim e at w hich it w as cre-
ated. The kernel variable, static struct file * inuse_filps (declared in
fs/le_table.c), points to a list of all le descriptors in the system . The struct
le de nition appears in include/fs.h.
struct le {
struct le *f_next, *f_prev;

mode_t f_mode;

struct dentry *f_dentry;


struct le_operations *f_op;

};
I n tro d u cti o n 7
1
By inspecting the inuse_lps kernel list, you can system atically inspect the le
descriptor for each file in the system (by follow ing the f_next pointer). Each
descriptor keeps the protection m ode (f_mode), a pointer to the inode (via
the f_dentry pointer; see include/linux/dcache.h for the definition of a
struct dentry), and the list of routines to operate on the le as described in
Part 1, Section 7 (f_op). Sim ilarly, you can study the les in the linux/mm di-
rectory to determ ine the kernel variables that are used to im plem ent the m em -
ory m anager, the linux/fs directory to determ ine the kernel variables used to
im plem ent the le m anager, and so on.
A useful rst step to understanding the details of Linux im plem entation is to ex-
plore the kernel variables and data structure definitions that are used to w rite
the code. M uch of the strategy for solving the problem s in the rem aining exer-
cises is to focus on a particular aspect of the kernel, to study its data structures
and functions, and then to solve the problem . The exercise introduction w ill
help you to determ ine w hich parts of the source code tree to focus on for that
exercise.
You have not learned enough to be able use a kernel debugger (or to w rite
your ow n kernel extensions) to read the values stored in kernel variables.
H ow ever, you can begin to exam ine som e of those values by using existing
tools. This exercise takes this latter approach to let you inspect the kernel state
and to give you som e intuition about how the kernel behaves.
P r o b l e m S t a t e m e n t
This exercise is to study som e aspects of the organization and behavior of a
Linux system by observing values stored in kernel variables.
Part A
Answ er the follow ing questions about the Linux m achine that you w ill be using
to do these exercises. If your schools lab has several m achines, then choose
one on w hich to base your answ ers.
W hat is the CPU type and m odel?
W hat version of the Linux kernel is being used?
H ow long (in days, hours, and m inutes) has it been since the system w as
last booted?
H ow m uch of the total CPU tim e has been spent executing in user m ode?
System m ode? Idle?
H ow m uch m em ory is con gured into it?
8 O b se rvi n g L i n u x B e h a vi o r
H ow m uch m em ory is currently available on it?
H ow m any disk read/w rite requests have been m ade?
H ow m any context sw itches has the kernel perform ed?
H ow m any processes have been created since the system w as booted?
Part B
W rite a default version of program to report the behavior of the Linux kernel by
inspecting kernel state. The program should print the follow ing values on std-
out:
CPU type and m odel
Kernel version
Am ount of tim e since the system w as last booted, in the form
dd:hh:m m :ss (for exam ple, 3 days 13 hours 46 m inutes 32 seconds
w ould be form atted as 03:13:46:32)
Part C
W rite a second version of the program in Part A that prints the sam e inform a-
tion as the default version plus the follow ing:
The am ount of tim e that the CPU has spent in user m ode, in system
m ode, and idle
The num ber of disk requests m ade on the system
The num ber of context sw itches that the kernel has perform ed
The tim e w hen the system w as last booted
The num ber of processes that have been created since the system w as
booted
Part D
W rite a third version of the program in Part A that prints the sam e inform ation
as the second version plus the follow ing:
The am ount of m em ory con gured into this com puter
The am ount of m em ory currently available
A list of load averages (each averaged over the last m inute)
This inform ation w ill allow another program to plot these values against tim e so
that a user can see how the load average varied over som e tim e interval. For
this version of the program , you need to provide tw o additional param eters:
P ro b le m S ta te m e n t 9
1
O ne to indicate how often the load average should be read from the
kernel
O ne to indicate the tim e interval over w hich the load average should be
read
The first version of your program m ight be called by ksamp and the second
version by ksamp s. Then the third version could be called by ksamp l 2
60, w hereby the load average observation w ould run for 60 seconds, sam pling
the kernel table about once every 2 seconds. To observe the load on the sys-
tem , you need to ensure that the com puter is doing som e w ork rather than
sim ply running your program . For exam ple, open and close w indow s, m ove
w indow s around, and even run som e program s in other w indow s.
A t t a c k i n g t h e P r o b l e m
Linux, Solaris, and other versions of U N IX provide a very useful m echanism for
inspecting the kernel state, called the /proc le system . This is the key m echa-
nism that you can use to do this exercise.
The /proc File System
According to M cKusick, et al. [1998], the /proc le system com es from U N IX
Eighth Edition and has been used w ith 4.4 BSD . In the /proc system , the ad-
dress space of another process can be accessed w ith read and write system
calls, w hich allow s a debugger to access a process being debugged w ith m uch
greater efficiency. The page (or pages) of interest in the child process is
m apped into the kernel address space. The requested data can then be copied
directly from the kernel to the parent address space.(page 113) In addition,
/proc can be used to collect inform ation about processes in the system , even
though that is not done in 4.4 BSD .
The /proc le system is an O S m echanism w hose interface appears as a direc-
tory in the conventional U N IX le system (in the root directory). You can change
to /proc just as you change to any other directory. For exam ple,
bash$ cd /proc
m akes /proc the current directory. O nce you have m ade /proc the current di-
rectory, you can list its contents by using the ls com m and. The contents appear
to be ordinary les and directories. H ow ever, a le in /proc or one of its subdi-
rectories is actually a program that reads kernel variables and reports them as
ASCII strings. Som e of these routines read the kernel tables only w hen the
pseudo le is opened, w hereas others read the tables each tim e that the le is
1 0 O b se rvi n g L i n u x B e h a vi o r
read. (You w ill learn m ore details of this behavior in Exercise 4.) Thus the vari-
ous read functions m ight behave differently than you expect, since they are not
really operating on les at all.
The /proc im plem entation provided w ith Linux can read m any different kernel
tables. Several directories as w ell as files are contained in /proc. Each file
reads one or m ore kernel variables, and the subdirectories w ith num eric nam es
contain m ore pseudo files to read inform ation about the process w hose
process ID is the sam e as the directory nam e. The directory self contains
process-speci c inform ation for the process that is using /proc. The exact con-
tents of the /proc directory tree vary am ong different Linux versions, so you
m ust experim ent w ith the pseudo les to view the inform ation provided.
Files in /proc are read just like ordinary ASCII les. For exam ple, w hen you type
to the shell a com m and such as
bash$ cat /proc/version
you w ill get a m essage printed to stdout that resem bles the follow ing.
Linux version 2.2.12 (gcc version egcs-2.91.66
19990314/Linux (egcs-1.1.2 release)) #1 Mon Sep 27 10:40:35
EDT 1999
To read a /proc pseudo les contents, you open the le and then use stdio
library routines such as fgets() or fscanf() to read the le. The exact les (and
tables) read depend on the speci c Linux version that you are using. To nd out
exactly w hich le interfaces are available to you through /proc, read the proc
m an page on the system .
Using argc and argv
If you do Part C or D in addition to Part B, you w ill need to pass param eters to
your program from the shell. For exam ple, suppose that your solution program
is nam ed observer. To solve Part B, you could call it w ith
bash$ observer
w hereas to solve Part C or D , you could call w ith
bash$ observer -s
If the program is providing the inform ation required for Parts B, C, and D , it
m ight be called by
bash$ observer l 10 600
A tta ck i n g th e P ro b le m 1 1
1
w here l m eans to produce the long report and 10 600 m eans that the load
averages are to be com puted once every 10 seconds until 600 seconds have
elapsed.
The follow ing code segm ent is an exam ple for handling the three different w ays
that observer can be called. A C m ain program m ay have a header of the
form
int main(int argc, char *argv[])
You m ay om it the tw o argum ents, argc and argv, if no param eters are to be
passed to the program by the shell. Alternatively, they can be initialized so that
argc is the num ber of sym bols on the com m and line and argv is an array of
pointers to character strings sym bols on the com m and line. For exam ple, if the
observer program calls w ith no param eters (the first exam ple previously),
then argc w ill be set to 1 and argv[0] w ill point to the string observer. In the
second exam ple, argc w ill be set to 2, w ith argv[0] pointing to observer and
argv[1] pointing to the string -s. In the third exam ple shell com m and line, you
could have argc == 4 and argv[0] point to observer, argv[1] to -l, argv[2]
to 10, and argv[3] to 600.
The C m ain program can now reference these argum ents as follow s.
#include <stdio.h>

int main(int argc, char *argv[])


char repTypeName[16];

// Determine report type


reportType = STANDARD;
strcpy(repTypeName, Standard);
if(argc > 1) {
sscanf(argv[1], %c%c, &c1, &c2);
if(c1 != -) {
fprintf(stderr, usage: observer [-s][-l int dur]\n);
exit(1);
}
if(c2 == s) {
reportType = SHORT;
strcpy(repTypeName, Short);
}
if(c2 == l) {
reportType = LONG;
1 2 O b se rvi n g L i n u x B e h a vi o r
strcpy(repTypeName, Long);
interval = atoi(argv[2]);
duration = atoi(argv[3]);
}
}

}
Organizing a Solution
For Parts C and D , your program s m ust have different argum ents on the com -
m and line. Therefore one of your rst actions should be to parse the com m and
line w ith w hich the program is called so as to determ ine the shell param eters
being passed to it via the argv array (see the code on page 62, 63).
You finish initializing by getting the current tim e of day and printing a greeting
that includes the nam e of the m achine that you are inspecting.
#include <sys/time.h>

// Finish initializing
gettimeofday(&now, NULL); // Get the time of day
printf(Status report type %s at %s\n,
repTypeName, ctime(&(now.tv_sec)));
// Get the host lename and print it
thisProcFile = fopen(/proc/sys/kernel/hostname, r);
fgets(lineBuf, LB_SIZE+1, thisProcFile);
printf(Machine hostname: %s, lineBuf);
fclose(thisProcFile);
N ow you are ready to do the w ork, that is, to start reading kernel variables by
using various /proc les. The previous code segm ent contains an exam ple of
how to read the /proc/sys/kernel/hostname le. You can use it as a proto-
type for solving the exercise by reading other pseudo files. This w ill require
som e exploration of /proc and inspection of the various pseudo les as you in-
vestigate different directories.
In Part D , you are to com pute a load average. For this, your code needs to
sleep for a w hile, w ake up, sam ple the current load average, and then go back
to sleep. H ere is a code fragm ent that w ill accom plish that w ork.
while(iteration < duration) {
sleep(interval);
sampleLoadAvg();
A tta ck i n g th e P ro b le m 1 3
1
iteration += interval;
}
N ow you are ready to create the entire solution. H ere is all of the code exam -
ples that you have seen, put into a single program .
#include <stdio.h>
#include <sys/time.h>

int main(int argc, char *argv[]) {

char repTypeName[16];

/* Determine report type */


reportType = STANDARD;
strcpy(repTypeName, Standard);
if(argc > 1) {
sscanf(argv[1], %c%c, &c1, &c2);
if(c1 != -) {
fprintf(stderr, usage: ksamp [-s][-l int dur]\n);
exit(1);
}
if(c2 == s) {
reportType = SHORT;
strcpy(repTypeName, Short);
}
if(c2 == l) {
reportType = LONG;
strcpy(repTypeName, Long);
interval = atoi(argv[2]);
duration = atoi(argv[3]);
}
}
/* Get the current time */
gettimeofday(&now, NULL);
printf(Status report type %s at %s\n,
repTypeName, ctime(&(now.tv_sec)));
thisProcFile = fopen(/proc/sys/kernel/hostname, r);
1 4 O b se rvi n g L i n u x B e h a vi o r
fgets(lineBuf, LB_SIZE+1, thisProcFile);
printf(Machine hostname: %s, lineBuf);
fclose(thisProcFile);
/* Code to read the relevant /proc les */

while(iteration<duration) {
sleep(interval);
sampleLoadAvg();
iteration += interval;
}
exit(0);
}
Saving Your Work in a Shared Laboratory
You should keep a copy of all of your source code on a oppy disk so that you
have a backup version of your code and can m ove am ong different lab m a-
chines for different w ork sessions. To do this, use mtools (see the mcopy
shell com m and). Because the oppy disk is a shared device in a Linux system ,
you w ill need superuser perm ission before you can successfully w rite to the
oppy disk drive.
A tta ck i n g th e P ro b le m 1 5
1

Вам также может понравиться