The ‘system-call’ interface We see how an application program can invoke privileged

advertisement
The ‘system-call’ interface
We see how an application
program can invoke privileged
kernel services
Recall our previous lesson
• First, we presented a (simplified) diagram
of the major components in a modern OS
• Then, we focused our attention on one of
the kernel’s interfaces, namely, its role in
controlling hardware devices (specifically
the IDE fixed disk). We saw typical code
for performing an actual device-command.
• Today we look at another kernel interface.
A Modern OS Design
Application
Application
Application
Shared Runtime Libraries
user-mode
supervisor-mode
System Call Interface
memory
manager
task
manager
file
manager
network
manager
Device Driver Components
OS Kernel
Hardware
Our demo ‘bypassed’ the OS
Application
(idnumber)
Application
Application
Shared Runtime Libraries
user-mode
supervisor-mode
System Call Interface
Required
IOPL==3
memory
manager
task
manager
file
manager
network
manager
Device Driver Components
OS Kernel
Hardware
Role of ‘runtime libraries’
• To understand what role is played by the
kernel’s interface with ‘runtime libraries,’
let’s see how we could go around them.
• We can create a demo-program that does
not need to use the standard C library – it
will perform all their work on its own.
• If you fire your janitor, you will very quickly
come to appreciate all that he did for you!
Demo will call kernel directly
Application
demo
Application
Application
Shared Runtime Libraries
user-mode
supervisor-mode
System Call Interface
memory
manager
task
manager
file
manager
network
manager
Device Driver Components
OS Kernel
Hardware
A normal C program example
# include <unistd.h>
# include <stdlib.h>
char
// for write()
// for exit()
message[ ] = “Hello!\n”;
int main( void )
{
write( 1, message, 7 );
exit( 0 );
}
Standard device-files
• Whenever a new program is launched, the
operating system will automatically ‘open’
three device-files, named ‘stdin’, ‘stdout’,
and ‘stderr’, using file-drescriptors 0, 1, 2.
– ‘stdin’ is the standard input device (keyboard)
– ‘stdout’ is the standard output device (screen)
– ‘stderr’ is the standard error device (screen)
Standard C library functions
• The functions ‘write()’ and ‘exit()’ were not
actually defined within our program-code
• They are examples of ‘external’ functions
• Header-files let the compiler know how to
generate assembler code that calls them
• Their actual definitions are part of the
standard GNU/C runtime-library (‘glibc’)
• The linker connects our code to ‘glibc’
The function ‘prototypes’
int write( int fd, char * buf, int count );
void exit( int status );
The C compiler needs this information to
correctly generate the machine-code for
calls to these ‘external’ library-functions.
C program example revised
(we’ve omitted the header-files)
extern int write( int, char *, int );
extern void exit( int );
char
message[ ] = “Hello!\n”;
int main( void )
{
write( 1, message, 7 );
exit( 0 );
}
How these calls get compiled
pushl
pushl
pushl
call
addl
$7
# parameter no. 3
buf
# parameter no. 2
$1
# parameter no. 1
write
# call to C library
$12, %esp # discard params
pushl
call
$0
exit
# parameter no. 1
# call to C library
Stack’s layout on entering ‘write’
32-bits
Parameter number 3 (count)
Parameter number 2 (buf)
parameter number 1 (fd)
Return-address (from EIP)
ESP
What ‘write’ function does
write: pushl %ebp
movl %esp, %ebp
# save caller’s EBP
# point EBP to stacktop
# copy the parameters into general registers
movl $4, %eax
# sys_WRITE ID-number
movl 8(%ebp), %ebx
# parameter no. 1 to EBX
movl 12(%ebp), %ecx
# parameter no. 2 to ECX
movl 16(%ebp), %edx
# parameter no. 3 to EDX
int
$0x80
# enter the linux kernel
popl
ret
%ebp
# restore previous EBP
# return to the caller
What ‘exit’ function does
exit:
pushl %ebp
movl %esp, %ebp
# save caller’s EBP
# point EBP to stacktop
# copy the parameter into a general registers
movl $1, %eax
# sys_EXIT ID-number
movl 8(%ebp), %ebx
# parameter no. 1 to EBX
int
$0x80
# enter the linux kernel
# Note: The ‘exit()’ function never returns to the application
So we don’t really need library
• If we are willing to rewrite our program using
assembly language (so that we can refer to cpu
registers by name), we can make system-calls
directly to the kernel (using the ‘int $0x80’ trapinstruction) without needing to link our code to
those shared C library functions
• Doing this helps us convince ourselves that we
do indeed understand what the role of the C
runtime function-library is – it’s a programming
convenience (puts a pretty face on ugly code).
Program ‘portability’
• If we want a program that can be compiled
and executed on machines with different
CPU designs (e.g., PCs versus MACs),
then we will need to avoid using registernames in our program-code (i.e., the PC’s
EAX register doesn’t exist on the MAC)
• The shared library ‘hides’ the differences
between CPUs, since all the CPU-specific
work is done by code within the library
POSIX
• The ‘Portable Operating System Interface’
• Defines a conformance ‘standard’ that lets
application programmers write code which
is ‘portable’ across differing architectures
• Implemented with header-files and shared
runtime libraries
• Documented as an ANSI standard
• Online reference available via ‘man’ pages
Frequently needed functions
• Here are a few of the POSIX standard functions
which are most frequently used by applications:
–
–
–
–
–
open()
read()
write()
close()
exit()
• It’s advisable to learn them! (will save you time)
Their C function-prototypes
void exit( int status );
int read( int fd, char *buf, int count );
int write( int fd, char *buf, int count );
int open( char *fname, int flags, … );
int close( int fd );
NOTE: These POSIX functions correspond
directly to Linux kernel system-calls
function semantics
• We will give a rough general description of
the actions taken by the kernel when these
five frequently needed functions are called
• Additional details about each function can
be found online, using the ‘man’ command
• Example:
$ man 2 read
int write( int fd, char *buf, int count )
• This function asks the kernel to transfer
‘count’ bytes from the array pointed to by
‘buf’ to the previously-opened file specified
by the file-descriptor ‘fd’.
• If the kernel cannot transfer any bytes, it
returns the value -1; otherwise it returns
the number of bytes that the kernel was
able to transfer successfully and advances
the file-pointer accordingly.
int read( int fd, char *buf, int count )
• This function asks the kernel to transfer ‘count’
bytes into the array pointed to by ‘buf’ from the
previously-opened file specified by ‘fd’.
• If the kernel cannot transfer any bytes, it returns
a value of -1 -- unless the file-pointer is already
at the end of the file; otherwise, it returns the
number of bytes successfully transferred and
advances the file-pointer accordingly.
int open( char *fname, int flags, … )
• This function asks the kernel to set up an
internel record establishing a connection
between the file named ‘fname’ and an unused
nonnegative integer known as a file-descriptor,
provided the file’s access permissions allow the
kind of access that is specified by the ‘flags’
argument
• The function returns -1 if it cannot perform this
operation; otherwise, it returns the file-descriptor
value.
• An additional argument when creating a new file
int close( int fd );
• This function asks the kernel to remove
the connection it had previously set up
between the file-descriptor ‘fd’ and the
internal kernel record of an associated file.
• The value -1 is returned if the kernel was
not succesful in performing this operation
(e.g., the file had already been closed);
otherwise, this function returns 0.
void exit( int status )
• This function asks the kernel to terminate
the current program, and place the value
of ‘status’ into the internal record that the
kernel had associated with this program
when it was first launched.
• This function will not return any value
(because it does not return at all!)
In-class exercises
• Modify the ‘mywrite.c’ demo (see class website)
so it that writes to device 2 instead of device 1
• Try using the debugging tool to single-step
through the execution of ‘filedemo’. Watch
which values the kernel returns (in EAX) after
each ‘int $0x80’ trap-instruction. Verify that it
indeed created the ‘testfile.dat’ file.
• Write a short program (named ‘myread.c’) which
uses standard C library functions to ‘read’ the
‘testfile.dat’ file and ‘write’ its contents to ‘stdout’
Download