UNIXs grand illusion - PowerPoint PPT Presentation

About This Presentation
Title:

UNIXs grand illusion

Description:

But a System Administrator has to create these device-files (in the /dev' directory) ... So we can create a simple char driver that lets application-programs ... – PowerPoint PPT presentation

Number of Views:67
Avg rating:3.0/5.0
Slides: 35
Provided by: ProfessorA
Learn more at: https://www.cs.usfca.edu
Category:
Tags: create | grand | illusion | unixs

less

Transcript and Presenter's Notes

Title: UNIXs grand illusion


1
UNIXs grand illusion
  • How Linux makes a hardware device appear to be a
    file

2
Basic char-driver components
Device-driver LKM layout
modules payload is a collection of
callback-functions having prescribed
prototypes
function
function
function
AND a package of
function-pointers
fops
. . .
init
the usual pair of module-administration
functions
registers the fops
exit
unregisters the fops
3
Background
  • To appreciate the considerations that have
    motivated the over-all Linux drivers design
    requires an understanding of how normal
    application-programs get their access to services
    that the operating system offers
  • This access is indirect through specially
    protected interfaces (i.e., system calls)
    usually implemented as library functions

4
Standard 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 )
  • (and other less-often-used file-I/O functions)

5
Our elfcheck.cpp example
include ltfcntl.hgt for open() include
ltstdio.hgt for perror(), printf() include
ltunistd.hgt for read(), close() include
ltstring.hgt for strncpy() char buf4 int
main( int argc, char argv ) if ( argc 1
) return -1 // command-line argument is
required int fd open( argv1, O_RDONLY ) //
open specified file if ( fd lt 0 ) perror(
argv1 ) return -1 // quit if open
failed int nbytes read( fd, buf, 4 ) // read
first 4 bytes if ( nbytes lt 0 ) perror(
argv1 ) return -1 // quit if read failed if
( strncmp( buf, \177ELF, 4 ) 0 ) // check
for ELF id printf( File \s\ has ELF
signature \n, argv1 ) else printf( File
\s\ is not an ELF file \n, argv1 ) close(
fd ) // close the file
6
Special device files
  • UNIX systems treat hardware-devices as special
    files, so that familiar functions can be used by
    application programmers to access devices (e.g.,
    open, read, close)
  • But a System Administrator has to create these
    device-files (in the /dev directory)
  • Or alternatively (as weve seen), an LKM could
    create these necessary device-files

7
UNIX man pages
  • A convenient online guide to prototypes and
    semantics of the C library functions
  • Example of usage
  • man 2 open

8
The open function
  • include ltfcntl.hgt
  • 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

9
The close function
  • include ltunistd.hgt
  • int close( int fd )
  • Breaks link between file and file-descriptor
  • Returns 0 on success, or -1 if an error

10
The write function
  • include ltunistd.hgt
  • 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

11
The read function
  • include ltunistd.hgt
  • 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

12
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

13
The lseek function
  • include ltunistd.hgt
  • 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)

14
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 )

15
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)

16
Our RTC example
  • We previously learned how the Real-Time Clock
    device can be accessed by module code, using the
    inb() and outb() macros
  • So we can create a simple char driver that lets
    application-programs treat the RTCs memory as if
    it were in an ordinary file

outb( addr, 0x70 ) outb( data, 0x71 )
outb( addr, 0x70 ) data inb( 0x71 )
17
How device-access works
Application program
user space (unprivileged)
call
int 0x80
ret
Standard runtime library
int 0x80
iret
iret
Operating system (software)
call
supervisor space (privileged)
ret
Device-driver module (software)
out
in
Physical peripheral device (hardware)
18
Our cmosram.c driver
  • We implement three callback functions
  • llseek // sets file-pointers position
  • read // inputs a byte from CMOS
  • write // outputs a byte to CMOS
  • We omit other callback functions, such as
  • open // we leave this function-pointer NULL
  • release // we leave this function-pointer NULL
  • The kernel has its own default implementation
    for any function with NULL as its pointer-value

19
The fops syntax
  • The GNU C-compiler supports a syntax for struct
    field-initializations that lets you give your
    field-values in any convenient order

struct file_operations my_fops
llseek my_llseek, write my_write,
read my_read,
20
init and exit
  • The modules initialization function has to take
    care of registering the drivers fops
  • register_chrdev( major, devname, fops )
  • Then the modules cleanup function must make sure
    to unregister the drivers fops
  • unregister_chrdev( major, devname )
  • (These are prototyped in ltlinux/fs.hgt)

21
Our dump.cpp utility
  • We have written an application that lets users
    display the contents of any file in both
    hexadecimal and ascii formats
  • It also works on device files!
  • With our driver-module installed, you can use it
    to view the CMOS memory-values
  • ./dump /dev/cmos

22
Now for a useful char-driver
  • We can create a character-mode driver for the
    processors physical memory (i.e. ram)
  • (Our machines have 2-GB of physical ram)
  • But another device-file is named /dev/ram so
    ours will be /dev/dram (dynamic ram)
  • Weve picked 85 as its major ID-number
  • Our SysAdmin setup a device-node using root
    mknod /dev/dram c 85 0

23
2-GB RAM has zones
ZONE_HIGH
1024128-MB
2048-MB ( 2GB)
ZONE_NORMAL
896-MB
16-MB
ZONE_LOW
Installed physical memory
24
Legacy DMA
  • Various older devices rely on the PC/ATs 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 (so devices will find free
    DMA memory if its needed)

25
Normal memory zone
  • This zone extends from 16MB to 896MB
  • Linux uses the lower portion of this zone for an
    important data-structure that tracks how all the
    physical memory is being used
  • Its an array of records mem_map
  • (We will soon be studying this structure)
  • The remainder of ZONE_NORMAL is free for
    dynamic allocations by the OS kernel

26
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 virtual
    kernel-region
  • 0xC0000000 0xFFFFFFFF
  • But with 2-GB theres not enough room!

27
The 896-MB limit
HIGH MEMORY
temporary mappings using kmap()
Installed ram (2-GB)
HIGH ADDRESSES
Kernel space (1-GB)
896-MB
896-MB
always mapped
application programs code and data goes here
Physical address-space
User space (3-GB)
A special pair of kernel-functions named
kmap() and kunmap() can be called by
device-drivers to temporarily map vacant areas
in the kernels high address-space to pages of
actual physical memory
Virtual address-space
28
dram.c module-structure
  • We will need three kernel header-files
  • include ltlinux/module.hgt
  • // for printk(), register_chrdev(),
    unregister_chrdev()
  • include ltlinux/highmem.hgt
  • // for kmap(), kunmap(), and num_physpages
  • include ltasm/uaccess.hgt
  • // for copy_to_user()

29
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

30
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 85 // static ID-assignment

31
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

32
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 arbitrary 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

33
In-class exercise 1
  • Install the dram.ko device-driver module then
    use fileview to browse the contents of the
    processors physical memory
  • ./fileview /dev/dram
  • Be sure to try the b, w, d, q, ltENTERgt
    and ltESCAPEgt keys
  • Also try ./fileview /dev/cmos

34
In-class exercise 2
  • The read() and write() callback functions in our
    cmosram.c device-driver only transfer a single
    byte of data for each time called, so it takes
    128 system-calls to read all of the RTC
    storage-locations!
  • Can you improve this drivers efficiency, by
    modifying its read() and write() functions, so
    theyll transfer as many valid bytes as the
    supplied buffers space could hold?
Write a Comment
User Comments (0)
About PowerShow.com