16.7 Unbuffered IO

I l @ ve RuBoard

16.7 Unbuffered I/O

In buffered I/O, data is buffered and then sent to the file. In unbuffered I/O, the data is immediately sent to the file.

If you drop a number of paperclips on the floor, you can pick them up in buffered or unbuffered mode. In buffered mode, you use your right hand to pick up a paper clip and transfer it to your left hand. The process is repeated until your left hand is full, then you dump a handful of paperclips into the box on your desk.

In unbuffered mode, you pick up a paperclip and dump it into the box. There is no left-hand buffer.

In most cases, buffered I/O should be used instead of unbuffered. In unbuffered I/O, each read or write requires a system call. Any call to the operating system is expensive. Buffered I/O minimizes these calls.

Unbuffered I/O should be used only when reading or writing large amounts of binary data or when direct control of a device or file is required.

Back to the paperclip example ”if you were picking up small items like paperclips, you would probably use a left-hand buffer. But if you were picking up cannon balls (which are much larger), no buffer would be used.

The open system call is used for opening an unbuffered file. The macro definitions used by this call differ from system to system. Since the examples have to work for both Unix and MS-DOS/Windows, conditional compilation ( #ifdef / #endif ) is used to bring in the correct files:

 #include <sys/types.h>   #include <sys/stat.h> #include <fcntl.h> #ifdef _  _MSDOS_  _    // If we are MS-DOS  #include <io.h>         // Get the MS-DOS include file for raw I/O #else /* _  _MSDOS_  _ */ #include <unistd.h>     // Get the Unix include file for raw I/O  #endif /* _  _MSDOS_  _ */ 

The syntax for an open call is:

 int file_descriptor = open(   name   ,   flags   );    // Existing file file_descriptor = open(   name   ,   flags   ,   mode   );  //New file 
file_descriptor

An integer that is used to identify the file for the read, write, and close calls. If file_descriptor is less than 0, an error occurred.

name

Name of the file.

flags

Defined in the fcntl.h header file. Open flags are described in Table 16-6.

mode

Protection mode for the file. Normally this is 0644.

Table 16-6. Open flags

Flag

Meaning

O_RDONLY

Open for reading only.

O_WRONLY

Open for writing only.

O_RDWR

Open for reading and writing.

O_APPEND

Append new data at the end of the file.

O_CREAT

Create file (the file mode parameter required when this flag is present).

O_TRUNC

If the file exists, truncate it to 0 length.

O_EXCL

Fail if file exists.

O_BINARY

Open in binary mode (older Unix systems may not have this flag).

For example, to open the existing file data.txt in text mode for reading, you use the following:

 data_fd = open("data.txt", O_RDONLY); 

The next example shows how to create a file called output.dat for writing only:

 out_fd = open("output.dat", O_CREATO_WRONLY, 0666); 

Notice that you combined flags using the OR ( ) operator. This is a quick and easy way of merging multiple flags.

When any program is initially run, three files are already opened. These are described in Table 16-7.

Table 16-7. Standard unbuffered files

File number

Description

Standard in

1

Standard out

2

Standard error

The format of the read call is:

   read_size   = read(   file_descriptor   ,   buffer   ,   size   ); 
read_size

The actual number of bytes read. A 0 indicates end-of-file, and a negative number indicates an error.

file_descriptor

File descriptor of an open file.

buffer

Pointer to a place to put the data that is read from the file.

size

Size of the data to be read. This is the size of the request. The actual number of bytes read may be less than this. (For example, you may run out of data.)

The format of a write call is:

 write_size = write(file_descriptor, buffer, size); 
write_size

Actual number of bytes written. A negative number indicates an error.

file_descriptor

File descriptor of an open file.

buffer

Pointer to the data to be written.

size

Size of the data to be written. The system will try to write this many bytes, but if the device is full or there is some other problem, a smaller number of bytes may be written.

Finally, the close call closes the file:

 flag = close(file_descriptor) 
flag

0 for success, negative for error.

file_descriptor

File descriptor of an open file.

Example 16-5 copies a file. Unbuffered I/O is used because of the large buffer size. It makes no sense to use buffered I/O to read 1K of data into a buffer (using an std:: ifstream ) and then transfer it into a 16K buffer.

Example 16-5. copy2/copy2.cpp
 /****************************************  * copy -- copy one file to another.    *  *                                      *  * Usage                                *  *      copy <from> <to>                *  *                                      *  * <from> -- the file to copy from      *  * <to>   -- the file to copy into      *  ****************************************/ #include <iostream> #include <cstdlib>       #include <sys/types.h>   #include <sys/stat.h> #include <fcntl.h> #ifdef _  _WIN32_  _    // if we are Windows32 #include <io.h>         // Get the Windows32 include file for raw i/o #else /* _  _WIN32_  _ */ #include <unistd.h>     // Get the Unix include file for raw i/o  #endif /* _  _WIN32_  _ */ const int BUFFER_SIZE = (16 * 1024); // use 16k buffers  int main(int argc, char *argv[]) {     char  buffer[BUFFER_SIZE];  // buffer for data      int   in_file;              // input file descriptor     int   out_file;             // output file descriptor      int   read_size;            // number of bytes on last read      if (argc != 3) {         std::cerr << "Error:Wrong number of arguments\n";         std::cerr << "Usage is: copy <from> <to>\n";         exit(8);     }     in_file = open(argv[1], O_RDONLY);     if (in_file < 0) {         std::cerr << "Error:Unable to open " << argv[1] << '\n';         exit(8);     }     out_file = open(argv[2], O_WRONLY  O_TRUNC  O_CREAT, 0666);     if (out_file < 0) {         std::cerr << "Error:Unable to open " << argv[2] << '\n';         exit(8);     }     while (true) {         read_size = read(in_file, buffer, sizeof(buffer));         if (read_size == 0)             break;              // end of file          if (read_size < 0) {             std::cerr << "Error:Read error\n";             exit(8);         }         write(out_file, buffer, (unsigned int) read_size);     }     close(in_file);     close(out_file);     return (0); } 

Several things should be noted about this program. First of all, the buffer size is defined as a constant, so it is easily modified. Rather than have to remember that 16K is 16,384, the programmer used the expression (16 * 1024) . This form of the constant is obviously 16K.

If the user improperly uses the program, an error message results. To help the user get it right, the message tells how to use the program.

You may not read a full buffer for the last read. That is why read_size is used to determine the number of bytes to write.

I l @ ve RuBoard


Practical C++ Programming
Practical C Programming, 3rd Edition
ISBN: 1565923065
EAN: 2147483647
Year: 2003
Pages: 364

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net