220 likes | 258 Views
Learn how an application can view both ASCII text and binary data by using our device drivers. Enhance file navigation and readability with the 'curses' library in Linux. Explore functions like 'clear()', 'move()', and more.
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!