Standard C Libraries Application Programmming Interface to System-Calls

advertisement
Standard C Libraries
Application Programmming
Interface to System-Calls
Important File-I/O Functions
int open( char *pathname, int flags, … );
int read( int fd, void *buf, size_t count );
int write( int fd, void *buf, size_t count );
int lseek( int fd, loff_t offset, int whence );
int close( int fd );
UNIX ‘man’ pages
• A convenient online guide to prototypes
and semantics of the C linrary functions
• Example of usage:
$ man 2 open
The ‘open’ function
•
•
•
•
•
•
#include <fcntl.h>
int open( char *pathname, int flags, … );
Converts a pathname to a file-descriptor
File-descriptor is a nonnegative integer
Used as a file-ID in subsequent functions
‘flags’ is a symbolic constant:
O_RDONLY, O_WRONLY, O_RDWR
The ‘close’ function
•
•
•
•
#include <unistd.h>
int close( int fd );
Breaks link between file and file-descriptor
Returns 0 on success, or -1 if an error
The ‘write’ function
•
•
•
•
•
•
•
#include <unistd.h>
int write( int fd, void *buf, size_t count );
Attempts to write up to ‘count’ bytes
Bytes are taken from ‘buf’ memory-buffer
Returns the number of bytes written
Or returns -1 if some error occurred
Return-value 0 means no data was written
The ‘read’ function
•
•
•
•
•
•
•
#include <unistd.h>
int read( int fd, void *buf, size_t count );
Attempts to read up to ‘count’ bytes
Bytes are placed in ‘buf’ memory-buffer
Returns the number of bytes read
Or returns -1 if some error occurred
Return-value 0 means ‘end-of-file’
Notes on ‘read()’ and ‘write()’
• These functions have (as a “side-effect”)
the advancement of a file-pointer variable
• They return a negative function-value of -1
if an error occurs, indicating that no actual
data could be transferred; otherwise, they
return the number of bytes read or written
• The ‘read()’ function normally does not
return 0, unless ‘end-of-file’ is reached
The ‘lseek’ function
• #include <unistd.h>
• off_t lseek( int fd, off_t offset, int whence );
• Modifies the file-pointer variable, based on
the value of whence:
enum { SEEK_SET, SEEK_CUR, SEEK_END };
• Returns the new value of the file-pointer
(or returns -1 if any error occurred)
Getting the size of a file
• For normal files, your application can find
out how many bytes belong to a file using
the ‘lseek()’ function:
int filesize = lseek( fd, 0, SEEK_END );
• But afterward you need to ‘rewind’ the file
if you want to read its data:
lseek( fd, 0, SEEK_SET );
What about ‘pseudo’ files?
• You can use standard library functions to
open, read, and close a ‘/proc’ pseudo-file
• You can use ‘lseek’ (except SEEK_END)
• An example is our ‘howfast.cpp’ program
• It measures how fast ‘jiffies’ increments
• It opens, reads, and closes ‘/proc/jiffies’
• And it also uses ‘lseek’ (to rewind this file)
How these system-calls work
C Runtime Library
Operating System
Kernel
Application Program
Module ‘methods’
User-space
Kernel-space
Special ‘device’ files
• UNIX systems treat hardware-devices as
special files, so that familiar functions can
be used by application programmers to
access these devices (open, read, close)
• But a System Administrator has to create
these device-files (in the ‘/dev’ directory)
• There are two categories of device files:
‘character’ devices, and ‘block’ devices
The ‘mknod’ command
• To create the device-node for a character
device, an Administrator executes ‘mknod’
root# mknod /dev/scull c 48 0
• Here ‘/dev/scull’ is the file’s pathname, ‘c’
indicates that it’s a character-mode device,
48 is its (unique) ‘major’ ID-number, and 0
is its (unique) ‘minor’ ID-number
• Default access-privileges: r w - r - - r - • Can be modified using ‘chmod’ command
What’s new in 2.6?
• Earlier Linux kernels stored the ‘/dev’ files
on the hard disk (so they were persistent)
• The 2.6 kernel stores them in a ram-disk
• So they ‘disappear’ during every shutdown
• You need ‘root’ privileges to re-build them!
• (Fortunately this step can be automated if
device-nodes are in ‘/etc/udev/devices’ )
A useful device-driver
• We can create a character-mode driver for
the processor’s physical memory (i.e. ram)
• (Our machines have 1-GB of physical ram)
• But another device-file is named ‘/dev/ram’
so ours will be: ‘/dev/dram’ (dynamic ram)
• We’ve picked 253 as its ‘major’ ID-number
• Our SysAdmin setup a device-node using:
root# mknod /dev/dram c 253 0
Device knowledge
• Before you can write a device-driver, you
must understand how the hardware works
• Usually this means you need to obtain the
programmer manual (from manufacturer)
• Nowdays this can often be an obstacle
• But some equipment is standardized, or is
well understood (because of its simplicity)
1-GB RAM has ‘zones’
ZONE_HIGH
1024-MB
(= 1GB)
128-MB
ZONE_NORMAL
ZONE_LOW
16-MB
Legacy DMA
• Various older devices rely on the PC/AT’s
DMA controller to perform data-transfers
• This chip could only use 24-bit addresses
• Only the lowest 16-megabytes of physical
memory are ‘visible’ to these devices:
224 = 0x01000000 (16-megabytes)
• Linux tries to conserve its use of memory
from this ZONE_LOW region for anything
except DMA (so it will available if needed)
‘HIGH’ memory
• Linux traditionally tried to ‘map’ as much
physical memory as possible into virtual
addresses allocated to the kernel-space
• Before the days when systems had 1-GB
or more of installed memory, Linux could
linearly map ALL of the physical memory
into the 1-GB kernel-region:
0xC0000000 – 0xFFFFFFFF
• But with 1GB there’s not enough room!
The 896-MB limit
HIGH MEMORY
896-MB
not-mapped
DRAM
(1GB)
Kernel space
(1GB)
Physical address-space linearly mapped
A special pair of kernel-functions
named ‘kmap()’ and ‘kunmap()’
can be called by device-drivers to
temporarily map pages of physical
memory into ‘vacant’ areas within
the kernel’s virtual address-space
User space
(3GB)
Virtual address-space
‘copy_to_user()’
• With kernel 2.6, it is possible to configure
the user-space versus kernel-space ‘split’
so that nearly 4GB of physical memory is
always linearly mapped into kernel-space
• The configuration-option is CONFIG_4GB
• With this option enabled, the user-space
and kernel-space use two different maps
• So device-drivers need a special function
to transfer kernel-data to a user’s buffer
Driver-module structure
• We will need three kernel header-files:
– #include <linux/module>
// for printk(), register_chrdev(), unregister_chrdev()
– #include <linux/highmem.h>
// for kmap(), kunmap(), and ‘num_physpages’
– #include <asm/uaccess.h>
// for copy_to_user()
Our ‘dram_size’ global
• Our ‘init_module()’ function will compute
the size of the installed physical memory
• It will be stored in a global variable, so it
can be accessed by our driver ‘methods’
• It is computed from a kernel global using
the PAGE_SIZE constant (=4096 for x86)
dram_size = num_physpages * PAGE_SIZE
‘major’ ID-number
• Our ‘major’ device ID-number is needed
when we ‘register’ our device-driver with
the kernel (during initialization) and later
when we ‘unregister’ our device-driver
(during the cleanup procedure):
int my_major = 253; // static ID-assignment
Our ‘file_operations’
• Our ‘dram’ device-driver does not need to
implement special ‘methods’ for doing the
‘open()’, ‘write()’, or ‘release()’ operations
(the kernel ‘default’ operations will suffice)
• But we DO need to implement ‘read()’ and
‘llseek()’ methods
• Our ‘llseek()’ code here is very standard
• But ‘read()’ is specially crafted for DRAM
Using our driver
• We have provided a development tool on
the class website (named ‘fileview.cpp’)
which can be used to display the contents
of files (including device-files)
• The data is shown in hex and ascii formats
• The arrow-keys can be used for navigation
• The enter-key allows an offset to be typed
• Keys ‘b’, ‘w’, ‘d’ and ‘q’ adjust data-widths
In-class exercise #1
• Install the ‘dram.ko’ device-driver module;
then use ‘fileview’ to browse the contents
of the processor’s physical memory:
$ fileview /dev/dram
Control Register CR3
• Register CR3 holds the physical-address
of the system’s current ‘page-directory’
• The page-directory is an array of 1024
entries, showing how ‘virtual addresses’
are currently ‘mapped’ to physical pages
• With ‘fileview’ you can find and examine
this important kernel data-structure – but
you must know the value in register CR3
In-class exercise #2
• Use the ‘newinfo’ wizard to quickly create
a pseudo-file (named ‘/proc/cr3’) that will
allow user-programs to obtain the current
value of the Pentium’s CR3 register
• Write a tool (named ‘seepgdir.cpp’) that
will read ‘/proc/cr3’ to get the address of
the page-directory, then read it from the
‘/dev/dram’ device and print it onscreen
Download