System Calls: A Kernel Project in Linux

advertisement
System Calls: A Kernel Project in
Linux
CS-550-3: Operating Systems
Fall 2003
Hunter Bell
2
Table of Contents
Main Body of Text (pages 3 –5)
1. Introduction - page 3
2. Description of trap-table and user space interaction –
page 3
3. Creating a new kernel function – pages 3-4
4. Revision of system call table – page 4 - 5
5. Rebuilding the kernel – page 5-6
6. Creation of user-space program – page 6
7. Conclusion – page 6
8. Bibliography – page 7
Appendices
1. Appendix
2. Appendix
3. Appendix
4. Appendix
A: alterations to time.c – page 8
B: alterations to entry.S – page 9
C: user-space program (exercise-5.c) –page 10
D: alteration to unistd.h – page 11
3
Introduction
The system call linkage within any operating system is
an important piece of functionality for the computer on
which it is running. It provides an interface through
which software (in user space) can request an action by the
operating system itself. Gary Nutt’s Exercise Five
requires the alteration of the system call linkage in
Linux. Since the book is based on Linux Mandrake version
2.2.12, Red Hat Linux was not used in this project.
Instead, the version of Linux Mandrake (2.2.14) provided
with the book was used. On the following pages, the
process for successfully adding a new function and system
call to the Linux Mandrake kernel is examined and explained
in detail.
Description of Trap table and User Space Interaction
Kernel functions appearing in the system call
interface cannot be called directly from user space, but
must be called through the trap table, also known as the
system call table. This is accomplished by creating a new
entry in the kernel trap table, which references the newly
created function. Once created, the entry in the trap
table can be called through a macro-created-stub function
from user space. This stub function includes a trap
instruction that changes the CPU from user to supervisor
(administrative) mode so it can run the kernel function.
In Linux, the trap instruction included here causes an
interrupt (0x80), and uses the arguments passed through the
stub/macro to access the system call table at the correct
location. Once identified, the system call function
(syscall() ) checks to ensure the requested function
(passed as an argument from user space) is acceptable and
then calls the kernel function, which executes according to
instructions in its body. This will only occur, however,
if the compiler agrees that the arguments passed from the
stub in user space are correct in number and type with
regard to the kernel function (Nutt, 2001).
Creating a New Kernel Function
Creating the new kernel function, named pedagogictime,
was a required task in this programming exercise. The body
of the function needed to reside in the
/usr/src/linux/arch/i386/kernel/time.c (see Appendix A)
file within the kernel of the operating system. This
4
location enabled the new code to be compiled along with the
pre-existing kernel functions when the kernel was rebuilt.
(If the function resided outside of this file (within its
own file), it would have been necessary to edit the
Makefile within that same directory by adding the new file
name to O_OBJS list.) In order to correctly implement the
function, a call-by-reference argument was necessary in the
function header/signature. This enabled the kernel to
write information into a user-space address. This can only
be accomplished, however, if the address has been correctly
identified in user space (Nutt, 2001).
There exists a kernel function, verify_area, that can
ensure the validity of a read/write operation into or out
of user space. The return from this function is either a
true or false value that identifies whether or not the
kernel can access the variable from user space. This
function was used as a fail-safe check to exit the kernel
function if reading or writing was not possible. If the
result was valid, the function would continue operation and
call two additional kernel-level functions: copy_to_user
and copy_from_user. The suggested functions, memcpy_fromfs
and memcpy_tofs, suggested in the exercise description did
not work as anticipated, and would not compile. The grep
command was used, and searched through the kernel until the
copy functions were identified. The copy_to_user function
allows the kernel to write new values into user space. In
this case, the values contained within the kernel variable
xtime were written to the variable passed by reference
through calling the function. To ensure the correct values
were sent, it was necessary to disable interrupts on the
operating system as the values were copied by calling the
system function cli(). This guaranteed the values stored
in xtime would not change during this process. Once copied
into user space, the interrupts were re-enabled using the
function sti(). Pedagogictime then called, back from user
space, the values copied into the argument by reference.
If the argument “flag” (sent as another argument from user
space) is true, the function then prints the resulting
values using the printk function. Printk is almost
identical to the printf function in user space, but it just
adds the ability to print to stdout from kernel space (Nutt
2001).
Revision of System Call Table
After having completed the new kernel function, it was
necessary to revise the sys_call_table so the operating
5
system would recognize calls and stubs to this function.
This was accomplished by editing the table in the
/usr/src/linux/arch/i386/kernel/entry.S file, and the table
in the /usr/src/linux/asm/unistd.h file.
There were
already 190 entries in the system call table, so the new
function would be identified as number 191 (see appendix
B). It was also necessary to update the total number of
system calls recognized by the system to 191, and this was
done in the same file. In the unistd.h file, an entry was
added as definition number 191 so the system would be able
to recognize stubs for this function generated by Macros in
user space. With these new additions, whenever a
trap/interrupt 0x80 occurs with an argument of 191, the new
function will be invoked (assuming the kernel has been
rebuilt with the new code included) (Nutt, 2001).
Rebuilding the Kernel
The last stage of the adding the function to the
kernel project was to rebuild the kernel itself, and this
proved to be one of the more challenging aspects of the
entire assignment. The process can be time-consuming and
very frustrating if your code does not compile (because you
are not notified until step 4 of 5 in the process). All of
the instructions for rebuilding the kernel in its various
phases are found within the Makefile for each directory
inside the kernel. That is, each directory has its own
specific Makefile that hooks into the bigger Makefile in
the top-level kernel directory. This ensures all pieces of
the kernel are affected in the proper way when each step of
the rebuild process is executed (Nutt, 2001).
The first phase of rebuilding the kernel is to issue
the “make clean” statement from the command line prompt.
This causes the operating system to remove old object files
so they may be replaced during the rebuild process. After
this statement executes, the “make <config_opt>” command is
issued from the command line. The <config_opt> is replaced
by “oldconfig” and creates the kernel configuration based
upon the old configuration that was present before the make
clean statement was issued. After execution of this step,
the command “make depend” is given. This command ensures
that certain files within the kernel are scheduled for
compilation before others, making certain all dependencies
between various files are maintained. The fourth step in
the process is issuing the “make” statement. This compiles
all of the kernel source code, and results in the
executable file version of the kernel. It is in this step
6
where the new code created in the kernel source files is
compiled, and where the programmer is notified of
successful or unsuccessful compilation of that code. If
the code does not compile here, he/she will have to return
and make adjustments based upon the errors encountered
during compilation. This can prove to be a daunting task,
because once the changes are made, all of the previous
steps in rebuilding the kernel must be run to ensure
integrity throughout the rebuild process. The last step of
rebuilding the kernel includes issuing the “make
<boot_opt>” command. The <boot_opt> is replaced, in this
exercise, by bzImage. This creates a bootable kernel image
and installs it within the kernel boot directory. If all
five steps complete successfully, the new kernel function
has been implemented correctly as far as compilation. The
true test will be calling the function from a macro in user
space(Nutt, 2001).
Creation of User Space Program
Finally, after the function was added correctly to the
kernel, the user-space program was created (see Appendix
C). This was an easy task, as compilation and running of
the program is all done within user space. A timeval
variable in which the values from xtime would be passed to
and from the kernel was created, along with an int (flag)
for notifying the kernel function to print the results.
The next step was generating a stub that allowed the
program to call the kernel function from user space. This
was accomplished by utilizing a macro. The macro
automatically creates a system call stub based upon the
number of arguments passed into the macro. The stub then
causes the interrupt 0x80 to occur, and having passed the
argument 191 into the macro, the interrupt then calls the
kernel function defined before rebuilding the kernel (Nutt,
2001)
Conclusion
The successful completion of a change to the system
call interface coupled with creating a user space function
is a time-consuming process. The end product, however, is
more than just a change to the operation of an operating
system. It includes attaining hands-on experience, and
knowledge of a computer at a level not attainable unless
each step of the process is worked through and completed.
7
Bibliography
Nutt, Gary (2001). Kernel Projects For Linux. New York, NY:
Addison Wesley Longman, Inc. ISBN 0-201-61243-7.
8
Appendix A: Alterations to time.c
(unaltered code not included)
/*****************************************
* Hunter Bell - CS550 Fall2003
* Term Project - Nutt Exercise-5
* **************************************/
asmlinkage int sys_pedagogictime(int flag,struct timeval *thetime)
{
/*declarations*/
int write_failed;
struct timeval *temp;
/*verify we can write to user space*/
write_failed = verify_area(VERIFY_WRITE,thetime,sizeof(thetime));
if(!write_failed)
{
printk("skelcall: Cannot write into user space");
return 0;
}/*end if*/
cli(); /*disable interrupts*/
/* write into user space the values in xtime */
__copy_to_user(thetimec,xtime,sizeof(xtime);
__copy_to_user(thetime,xtime,sizeof(xtime);
sti(); /*enable interrupts*/
/* verify we can read from user space*/
write_failed = verify_area(VERIFY_READ,thetime,sizeof(thetime));
if(!write_failed)
{
printk("skelcall: Cannot read from user space");
return 0;
}
/*copy back from user space into temporary variable for printing*/
temp = NULL;
__copy_from_user(temp,thetime,sizeof(thetime);
__copy_from_user(temp,thetime,sizeof(thetime);
/*Print time if flag is true*/
if(flag)
printk("%s",ctime(temp->tv_sec));
return 1;
}//end pedagogic time
9
Appendix B: Alterations to entry.S
(unchanged code not included)
ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_ni_syscall)
/* 0 - old "setup()" system call*/
.long SYMBOL_NAME(sys_exit)
.long SYMBOL_NAME(sys_fork)
.long SYMBOL_NAME(sys_read)
.long SYMBOL_NAME(sys_write)
.long SYMBOL_NAME(sys_open)
/* 5 */
…
.long SYMBOL_NAME(sys_ni_syscall)
/* streams2 */
.long SYMBOL_NAME(sys_vfork)
/* 190 */
.long SYMBOL_NAME(sys_pedagogictime) /*191 - Hunter Bell*/
/*
* NOTE!! This doesn't have to be exact - we just have
* to make sure we have _enough_ of the "sys_ni_syscall"
* entries. Don't panic if you notice that this hasn't
* been shrunk every time we add a new system call.
*/
.rept NR_syscalls-191
.long SYMBOL_NAME(sys_ni_syscall)
10
Appendix C: User-Space Program
/* ****************************************
* Hunter Bell - CS550
* exercise-5 (term project)
* this program executes in user space and
* makes a system call to kernel space
***************************************** */
#include <linux/unistd.h>
#include <sys/syscall.h>
#include <stdio.h>
#include <time.h>
int main(int argc, char * argv[])
{
struct timeval *thetime = (struct timeval *) malloc(sizeof(struct timeval *));
int flag = 1;
/* generate a stub for System call for int pedagogictime through a Macro*/
_syscall2(int,pedagogictime,int,flag,struct timeval *,thetime);
return 0;
}//end main
11
Appendix D: Alteration to unistd.h
#define __NR_exit
#define __NR_fork
…
#define __NR_vfork
#define __NR_pedagogictime
1
2
190
191
Download