Clemson University -- CPSC 231 -- Fall 2009 Circular buffer good for character I/O #define CBUFSIZE 8 /* size should be power of two */ #define CBUFMASK 0x7 /* wrap around to top by anding mask */ char cbuf[CBUFSIZE]; int in, /* in points to next open location */ out; /* out points to oldest entry */ void initbuf(void){ /* in == out => empty */ in = out = 0; /* (in+1)%buf_size == out => full */ } /* we instead use (in+1)&mask */ /* when buf_size is a power of 2 */ /* and mask = buf_size -1 */ void put(char val){ if(((in+1)&CBUFMASK)==out){ /* or use synchronization primitives */ fprintf(stderr,"full\n"); /* so calls to put() are blocked */ }else{ /* until buffer is no longer full */ cbuf[in] = val; in = (in+1)&CBUFMASK; } } char get(void){ register char val; if(in==out){ /* or use synchronization primitives */ fprintf(stderr,"empty\n"); /* so calls to get() are blocked */ return('\0'); /* until buffer is no longer empty */ }else{ val = cbuf[out]; out = (out+1)&CBUFMASK; return val; } } Double buffering want to reduce or eliminate delay caused by I/O device timeline read data into buffer . process data in buffer P read data into buffer . process data in buffer P read data into buffer . process data in buffer P ... overlap I/O into one buffer with processing of data in separate buffer timeline read data into buffer 1 . read data into buffer 2 process data in buffer 1 P1 process data in buffer 2 read data into buffer 1 P2 read data into buffer 2 process data in buffer 1 P1 process data in buffer 2 read data into buffer 1 P2 ... code structure for N blocks of data: i = 1; read data into buffer 1 while( i < N ){ read data into buffer 1 + (i&1) process data in buffer 2 - (i&1) i++; } process data in buffer 2 - (i&1) or j = 1; k = 2; read data into buffer j for( i = 1; i < N; i++ ){ read data into buffer k process data in buffer j swap(j,k) } process data in buffer j Files request operations by calls to buffered functions from C stdio library, or by calls to functions from the low-level Unix I/O library, or directly invoke the OS by intentional interrupts (traps on SPARC) buffered file operations - use file pointers - fopen - pass symbolic name, checks permissions, returns file pointer - fclose - release file (e.g., release from exclusive write access) - fgetc - read single character - fputc - write single character - fgets - read string - fputs - write string - fscanf - formatted read - fprintf - formatted write - fread - binary read - fwrite - binary write - ftell - return file position - fseek - set file position low-level file operations and OS calls - use integer file descriptors - open - pass symbolic name, checks permissions, returns file descriptor - close - release file - read - pass file descriptor, buffer address, and byte count - write - pass file descriptor, buffer address, and byte count standard files in Unix (already open) stdin (file descriptor = 0) stdout (file descriptor = 1) stderr (file descriptor = 2) file structure typedef struct{ int _cnt; /* number of characters in buffer */ unsigned char * _ptr; /* next character from/to in buffer */ unsigned char * _base ; /* address of the buffer */ unsigned char _flag; /* state of the stream (e.g., r/w, EOF) */ unsigned char _file; /* file descriptor */ unsigned char _opts; /* other options (e.g., seekable) */ } FILE; stored as __iob[] array stdin is &__iob[0], stdout is &__iob[1], stderr is &__iob[2] fopen() returns &__iob[n] for some file descriptor value n hierarchy of wrappers -- all leading to invoking the OS scanf(...) // stdin already open user program statement +-----------------------------------------+ v | fscanf( stdin, ... ) | buffered formatted read | +------------------------------------+ | v | | read( fd, buffer, BUFFER_LENGTH ) | | low-level read | | +------------------------+ | | | | | | mov %fd_r,%o0 | | | | | | | set buffer,%o1 | | | | | | | mov BUFFER_LENGTH,%o2 | | | | | | | mov READ,%g1 | | | v | | | ta 0 | | | trap to OS | | +------------------------+ | | | +------------------------------------+ | +-----------------------------------------+ e.g., here is a stack trace of routines being called for a scanf() (edited from pstack output) ff31f800 read ff310a18 __doscan_u ff3103b8 _doscan ff31651c vscanf ff315314 scanf 0001067c main 000104e8 _start example of getchar excerpts from "man getchar" Standard C Library Functions fgetc(3C) SYNOPSIS int fgetc(FILE *stream); int getc(FILE *stream); int getchar(void); DESCRIPTION The fgetc() function obtains the next byte (if present) as an unsigned char converted to an int, from the input stream pointed to by stream, and advances the associated file posi- tion indicator for the stream (if defined). ... The getc() routine is functionally identical to fgetc(), except that it is implemented as a macro. ... The getchar() routine is equivalent to getc(stdin). It is implemented as a macro. ... RETURN VALUES Upon successful completion, fgetc(), getc(), getc_unlocked(), getchar(), getchar_unlocked(), and getw() return the next byte from the input stream pointed to by stream. If the stream is at end-of-file, the end-of-file indicator for the stream is set and these functions return EOF. If a read error occurs, the error indicator for the stream is set, EOF is returned, and errno is set to indicate the error. C source program c01 #include c02 int c; c03 c04 int main(){ c05 c = getchar(); c06 return 0; c07 } [edited] last few lines of "gcc -E" output ... e220 int c; e221 e222 int main(){ e223 c = (--((&__iob[0]))->_cnt < 0 ? __filbuf((&__iob[0])) e224 : (int)*((&__iob[0]))->_ptr++); e225 return 0; e226 } [edited] output of gdb a.out (a.out was compiled using "gcc -static") (gdb) b __filbuf Breakpoint 1 at 0x14c28 (gdb) b _read Breakpoint 2 at 0x14d30 ... Breakpoint 1, 0x00014c28 in _filbuf () ... (gdb) x/4x __iob 0x5726c <_iob>: 0x00000000 0x00000000 0x00000000 0x01000000 _cnt _ptr _base _flag ... (gdb) disassemble 0x00014c24 <_filbuf+0>: save %sp, -96, %sp ... 0x00014cd8 <_filbuf+180>: call 0x14d30 <_read> ... Breakpoint 2, 0x00014d30 in _read () (gdb) disassemble 0x00014d30 <_read+0>: st %o0, [ %sp + 0x44 ] 0x00014d34 <_read+4>: mov 3, %g1 0x00014d38 <_read+8>: ta 8 ... (gdb) x/4x __iob 0x5726c <_iob>: 0x00000001 0x000580e1 0x000580e0 0x41000000 _cnt _ptr _base _flag directory - map filename to disk location(s) of data; also stores file protection information blocking - logical record is application's unit of transfer - physical record is I/O system's unit of transfer - multiple logical records per physical record is more efficient (since on read/write which accesses a logical record already in a buffer there is no physical I/O needed) consider an older Seagate ST31401N hard drive * requires 11 msec (milliseconds) on average to seek * 5.5 msec for average rotational delay * 0.11 msec to transfer one sector of 512 bytes during the actual transfer of a sector, the data transfer rate is 512 bytes / 0.11 msec = 4.65 Mbytes/sec but effective transfer rate for one sector w/ avg. seek and rot. delay is 512 bytes / (11 msec + 5.5 msec + 0.11 msec) = 30.82 Kbytes/sec [avg. of 16.61 msec per sector including the overhead] if you transfer a physical record of 10 sectors at a time 5120 bytes / (11 msec + 5.5 msec + 1.1 msec) = 290.9 Kbytes/sec [avg. of 1.76 msec per sector, reducing the overhead per sector] example file access programs at various levels /* Clemson University -- CPSC 231 -- Fall 2000 * example program 4 * * opening and printing a file - low-level Unix calls * * macro processing and assembly commands: * m4 ex4.s * gcc ex4.s * a.out * * input file is named "ex4.in" and it contains * This is * a test. * * you should see: * char 1 in file is 'T' (0x54) * char 2 in file is 'h' (0x68) * char 3 in file is 'i' (0x69) * char 4 in file is 's' (0x73) * char 5 in file is ' ' (0x20) * char 6 in file is 'i' (0x69) * char 7 in file is 's' (0x73) * char 8 in file is ' * ' (0xa) * char 9 in file is 'a' (0x61) * char 10 in file is ' ' (0x20) * char 11 in file is 't' (0x74) * char 12 in file is 'e' (0x65) * char 13 in file is 's' (0x73) * char 14 in file is 't' (0x74) * char 15 in file is '.' (0x2e) * char 16 in file is ' * ' (0xa) * * local register usage: * %fd_r - %l0 - file descriptor * %cnt_r - %l1 - count of characters in file * %char_r - %l2 - character read from file */ define(`fd_r', l0) define(`cnt_r', l1) define(`char_r', l2) .global main main: save %sp,-96,%sp set filename,%o0 clr %o1 clr %o2 call open ! fd = open( char *filename, o_flag, mode ) nop mov %o0,%fd_r cmp %fd_r,-1 ! if -1 returned then file was not opened bne success nop failure: ! print an error message and exit set errmsg,%o0 set filename,%o1 call printf nop ret restore success: ! read through file character by character clr %cnt_r loop: mov %fd_r,%o0 set buf,%o1 mov 1,%o2 call read ! n_read = read( fd, &buf, 1 ) nop cmp %o0,0 ! if 0 returned then there are no characters be done ! left in the file => EOF nop set buf,%o1 ! load character that was just read ldub [%o1],%char_r inc %cnt_r set msg,%o0 ! print the character and its hex value mov %cnt_r,%o1 mov %char_r,%o2 mov %char_r,%o3 call printf nop ba loop nop done: mov %fd_r,%o0 call close ! close( fd ) nop ret restore .section ".data" filename:.asciz "ex4.in" msg: .asciz "char %4d in file is '%c' (0x%x)\n" errmsg: .asciz "cannot open %s\n" .section ".bss" buf: .skip 1 /* opening and printing a file - trap version */ ... define(READ, 3) define(OPEN, 5) ... set filename,%o0 clr %o1 clr %o2 mov OPEN,%g1 ta 0 ! fd = open( char *filename, o_flag, mode ) mov %o0,%fd_r cmp %fd_r,-1 ! if -1 returned then file was not opened ... mov %fd_r,%o0 set buf,%o1 mov 1,%o2 mov READ,%g1 ta 0 ! n_read = read( fd, &buf, 1 ) cmp %o0,0 ! if 0 returned then there are no characters be done ! left in the file => EOF nop set buf,%o1 ! load character that was just read ldub [%o1],%char_r ... /* Clemson University -- CPSC 231 -- Fall 2000 * example program 5 * * reading from stdin (standard input) and writing to stderr (standard error) * using stdio functions accessed with __iob[] addresses * * macro processing and assembly commands: * m4 ex5.s * gcc ex5.s * a.out * * local register usage: * %fp_in_r - %l0 - file pointer for stdin * %fp_err_r - %l1 - file pointer for stderr * %char_r - %l2 - character read from file */ define(`fp_in_r', l0) define(`fp_err_r', l1) define(`char_r', l2) .global main main: save %sp,-96,%sp set __iob,%fp_in_r ! __iob is file pointer for stdin ! __iob+16 is file pointer for stdout set __iob+32,%fp_err_r ! __iob+32 is file pointer for stderr mov %fp_in_r,%o0 call fgetc nop mov %o0,%char_r mov %fp_err_r,%o0 set msg,%o1 mov %char_r,%o2 mov %char_r,%o3 call fprintf nop ret restore .section ".data" msg: .asciz "first char from stdin is '%c' (0x%x)\n"