220 likes | 313 Views
Using our device-drivers. How can an application display the data in a device special file?. The ‘fileview.cpp’ application. Purpose: a tool for viewing arbitrary files Some files have ascii text Other files have binary data So display in both hexadecimal and ascii
E N D
Using our device-drivers How can an application display the data in a device special file?
The ‘fileview.cpp’ application • Purpose: a tool for viewing arbitrary files • Some files have ascii text • Other files have binary data • So display in both hexadecimal and ascii • Some ascii characters are ‘control codes’ • Some bytes are not ascii characters at all • So show a substitute mark as necessary
Easy file navigation • Files may be quite large • Can only fit small pieces on one screen • And only some parts may be of interest • Also some hex formats can be confusing • Need a way to find what we want to see • Need it to remain visible while we read it • Need flexibility to adjust display format
The ‘curses’ library • Offers users an ‘interactive’ interface • Allows use of normal keyboard input • Allows use of a mouse • Allows use of ‘windows’ and ‘colors’ • Allows use cursor-keys for navigation • Allows use of ‘unbuffered’ keystrokes • Offers instant refresh of display screen
Concept behind ‘curses’ • Works on screens, windows, subwindows • Maintains several internal data-structures • Two of these are ‘stdscr’ and ‘curscr’ • Both are ‘maps’ of your physical screen • Drawing is done to the ‘stdscr’ window • But it doesn’t show up immediately • ‘refresh()’: compares ‘stdscr’ to ‘curscr’ and then only copies the parts that are ‘new’
Using ‘ncurses’ with Linux • #include <curses.h> • ‘initscr()’ initializes library data-structures • ‘noecho()’ turns off keystroke echoing • ‘raw()’ and ‘cbreak()’ set special behaviors • ‘refresh()’ updates the display screen • ‘endwin()’ restores normal tty-behavior
The typical ‘curses’ program #include <unistd.h> #include <stdlib.h> #include <curses.h> int main( int argc, char **argv ) { initscr(); … endwin(); }
Compiling ‘ncurses’ programs • Must use the ‘-l’ command-line switch: • Example: $ g++ fileview.cpp –lncurses –o fileview • This is lowercase ‘L’ (not uppercase ‘i’) • It means link with ‘libncurses.so’ library • Object libraries are in directory: ‘/usr/lib’
Some frequent functions • ‘clear()’ • ‘move()’ • ‘printw()’ • ‘mvprintw()’ • ‘refresh()’ • ‘newwin()’ • ‘box()’
Reference • Good introductory tutorial appears in: Richard Stones and Neil Matthew, “Beginning Linux Programming (2nd Ed.)” (WROX Press Ltd, 1999), Chapter 6.
Unusual goal of ‘fileview.cpp’ • Wanted to look at VERY large device-files • Eample: /dev/hda • This device file represents the hard disk • Size of today’s hard disk could be 180GB! • So file-positions could not be of type ‘long’ • Linux introduces a 64-bit type: loff_t • Kernel offers ‘sys_llseek()’ system-call
Linux uses ‘glibc’ C-library • The Gnu version of Standard C Library • ‘glibc’ implements system-call interfaces for the standard UNIX C functions, like open(), close(), read(), write(), and lseek() • But Gnu C Library omitted ‘llseek()’ • So can’t do seek-operations on big files!
Calling ‘llseek()’ anyway • Programmers can call the kernel directly • Can bypass the ‘glibc’ Standard C Library • But need to obey system-call conventions • Transition from user-mode to kernel-mode • Requires use of a special CPU instruction • This instruction is ‘architecture-specific’ • For Pentium CPU: asm(“ int $0x80 “);
Some ‘macros’ make it easier • #include <asm/unistd.h> • Calling ‘sys_llseek()’ requires 5 arguments • Arguments must go into CPU registers • ‘sys_call_table[ ]’ array-index goes in EAX • The macro to use is named ‘_syscall5’
A programming ‘bug’ • Standard C ‘read()’ function returns ‘int’ • Meaning of the ‘read()’ return-value: retval > 0 : ‘retval’ bytes successfully read retval = 0 : end-of-file reached, so no data retval < 0 : some error prevented reading • Return-value wasn’t checked in ‘fileview’!
Why wasn’t ‘bug’ found? • ‘fileview’ always tried to read 256 bytes • Never tried to read beyond ‘end-of-file’ • Never tried to read data that ‘wasn’t there’ • So no reason why retval wouldn’t be 256
But project #3 is different • ‘ram.c’ must read ALL physical pages • ‘high memory’ pages not always ‘mapped’ • And pages are not ‘mapped’ contiguously • 256 bytes could cross a page-boundary • Starting address: 0x00000F80 • Ending address: 0x00001080 • So some ‘read()’ errors could easily occur
How can ‘bug’ be fixed? • Several solutions are possible • Best to try minimizing the code-changes • Should focus on correct ‘fix’ for all drivers • Obey rules for driver ‘read()’ functions
Some pseudo-code int my_read( int fd, char *cp, int count ) { int more = count; while ( more ) { int n = read( fd, cp, more ); if ( n <= 0 ) return n; cp += n; more -= n; } return count; }
Recommendations • Consult “Linux Device Drivers” text • ‘read()’ method is described on page 80 • Do everything that’s actually necessary • But keep your code as simple as possible • During development: use ‘printk()’ output • For ‘release version’: omit ‘printk()’ output
Exercise • Write a ‘hello world’ program in C • But don’t use ANY header-files!! • (i.e., bypass the Standard C Library) • How? • 1) setup static message-string • 2) setup registers with parameters • 3) use ‘int 0x80’ to enter the kernel
‘sys_write( int fd, void *msg, int n)’ • The system-call number is 4 • The STDOUT file-descriptor is 1 • The message-string address is $msg • The message-length (you count the bytes) • These 4 arguments go in CPU registers: EAX, EBX, ECX, EDX (in that order) • So your ‘main()’ needs only 5 instructions!