4.2 Reading and Writing

Team-FLY

UNIX provides sequential access to files and other devices through the read and write functions. The read function attempts to retrieve nbyte bytes from the file or device represented by fildes into the user variable buf . You must actually provide a buffer that is large enough to hold nbyte bytes of data. (A common mistake is to provide an uninitialized pointer, buf , rather than an actual buffer.)

  SYNOPSIS  #include <unistd.h>    ssize_t read(int fildes, void *buf, size_t nbyte);  POSIX  

If successful, read returns the number of bytes actually read. If unsuccessful , read returns “1 and sets errno . The following table lists the mandatory errors for read .

errno

cause

ECONNRESET

read attempted on a socket and connection was forcibly closed by its peer

EAGAIN

O_NONBLOCK is set for file descriptor and thread would be delayed

EBADF

fildes is not a valid file descriptor open for reading

EINTR

read was terminated due to receipt of a signal and no data was transferred

EIO

process is a member of a background process group attempting to read from its controlling terminal and either process is ignoring or blocking SIGTTIN or process group is orphaned

ENOTCONN

read attempted on socket that is not connected

EOVERFLOW

the file is a regular file, nbyte is greater than 0, and the starting position exceeds offset maximum

ETIMEDOUT

read attempted on socket and transmission timeout occurred

EWOULDBLOCK

file descriptor is for socket marked O_NONBLOCK and no data is waiting to be received ( EAGAIN is alternative)

A read operation for a regular file may return fewer bytes than requested if, for example, it reached end-of-file before completely satisfying the request. A read operation for a regular file returns 0 to indicate end-of-file. When special files corresponding to devices are read, the meaning of a read return value of 0 depends on the implementation and the particular device. A read operation for a pipe returns as soon as the pipe is not empty, so the number of bytes read can be less than the number of bytes requested. (Pipes are a type of communication buffer discussed in Chapter 6.) When reading from a terminal, read returns 0 when the user enters an end-of-file character. On many systems the default end-of-file character is Ctrl-D.

The ssize_t data type is a signed integer data type used for the number of bytes read, or “1 if an error occurs. On some systems, this type may be larger than an int . The size_t is an unsigned integer data type for the number of bytes to read.

Example 4.1

The following code segment reads at most 100 bytes into buf from standard input.

 char buf[100]; ssize_t bytesread; bytesread = read(STDIN_FILENO, buf, 100); 

This code does no error checking.

The file descriptor, which represents a file or device that is open, can be thought of as an index into the process file descriptor table. The file descriptor table is in the process user area and provides access to the system information for the associated file or device.

When you execute a program from the shell, the program starts with three open streams associated with file descriptors STDIN_FILENO , STDOUT_FILENO and STDERR_FILENO . STDIN_FILENO and STDOUT_FILENO are standard input and standard output, respectively. By default, these two streams usually correspond to keyboard input and screen output. Programs should use STDERR_FILENO , the standard error device, for error messages and should never close it. In legacy code standard input, standard output and standard error are represented by , 1 and 2 , respectively. However, you should always use their symbolic names rather than these numeric values. Section 4.6 explains how file descriptors work.

Exercise 4.2

What happens when the following code executes?

 char *buf; ssize_t bytesread; bytesread = read(STDIN_FILENO, buf, 100); 

Answer:

The code segment, which may compile without error, does not allocate space for buf . The result of read is unpredictable, but most probably it will generate a memory access violation. If buf is an automatic variable stored on the stack, it is not initialized to any particular value. Whatever that memory happens to hold is treated as the address of the buffer for reading.

The readline function of Program 4.1 reads bytes, one at a time, into a buffer of fixed size until a newline character ( '\n' ) or an error occurs. The function handles end-of-file, limited buffer size and interruption by a signal. The readline function returns the number of bytes read or “1 if an error occurs. A return value of 0 indicates an end-of-file before any characters were read. A return value greater than 0 indicates the number of bytes read. In this case, the buffer contains a string ending in a newline character. A return value of “1 indicates that errno has been set and one of the following errors occurred.

  • An error occurred on read .

  • At least one byte was read and an end-of-file occurred before a newline was read.

  • nbytes-1 bytes were read and no newline was found.

Upon successful return of a value greater than 0, the buffer contains a string ending in a newline character. If readline reads from a file that does not end with a newline character, it treats the last line read as an error. The readline function is available in the restart library, of Appendix B.

Program 4.1 readline.c

The readline function returns the next line from a file.

 #include <errno.h> #include <unistd.h> int readline(int fd, char *buf, int nbytes) {    int numread = 0;    int returnval;    while (numread < nbytes - 1) {       returnval = read(fd, buf + numread, 1);       if ((returnval == -1) && (errno == EINTR))          continue;       if ( (returnval == 0) && (numread == 0) )          return 0;       if (returnval == 0)          break;       if (returnval == -1)          return -1;       numread++;       if (buf[numread-1] == '\n') {          buf[numread] = ' 
 #include <errno.h> #include <unistd.h> int readline(int fd, char *buf, int nbytes) { int numread = 0; int returnval; while (numread < nbytes - 1) { returnval = read(fd, buf + numread, 1); if ((returnval == -1) && (errno == EINTR)) continue; if ( (returnval == 0) && (numread == 0) ) return 0; if (returnval == 0) break; if (returnval == -1) return -1; numread++; if (buf[numread-1] == '\n') { buf[numread] = '\0'; return numread; } } errno = EINVAL; return -1; } 
'; return numread; } } errno = EINVAL; return -1; }
Example 4.3

The following code segment calls the readline function of Program 4.1 to read a line of at most 99 bytes from standard input.

 int bytesread; char mybuf[100]; bytesread = readline(STDIN_FILENO, mybuf, sizeof(mybuf)); 
Exercise 4.4

Under what circumstances does the readline function of Program 4.1 return a buffer with no newline character?

Answer:

This can only happen if the return value is 0 or “1. The return value of 0 indicates that nothing was read. The return of “1 indicates some type of error. In either case, the buffer may not contain a string.

The write function attempts to output nbyte bytes from the user buffer buf to the file represented by file descriptor fildes .

  SYNOPSIS  #include <unistd.h>   ssize_t write(int fildes, const void *buf, size_t nbyte);  POSIX  

If successful, write returns the number of bytes actually written. If unsuccessful, write returns “1 and sets errno . The following table lists the mandatory errors for write .

errno

cause

ECONNRESET

write attempted on a socket that is not connected

EAGAIN

O_NONBLOCK is set for file descriptor and thread would be delayed

EBADF

fildes is not a valid file descriptor open for writing

EFBIG

attempt to write a file that exceeds implementation-defined maximum; file is a regular file, nbyte is greater than 0, and starting position exceeds offset maximum

EINTR

write was terminated due to receipt of a signal and no data was transferred

EIO

process is a member of a background process group attempting to write to controlling terminal, TOSTOP is set, process is neither blocking nor ignoring SIGTTOU and process group is orphaned

ENOSPC

no free space remaining on device containing the file

EPIPE

attempt to write to a pipe or FIFO not open for reading or that has only one end open (thread may also get SIGPIPE ), or write attempted on socket shut down for writing or not connected (if not connected, also generates SIGPIPE signal)

EWOULDBLOCK

file descriptor is for socket marked O_NONBLOCK and write would block ( EAGAIN is alternative)

Exercise 4.5

What can go wrong with the following code segment?

 #define BLKSIZE 1024 char buf[BLKSIZE]; read(STDIN_FILENO, buf, BLKSIZE); write(STDOUT_FILENO, buf, BLKSIZE); 

Answer:

The write function assumes that the read has filled buf with BLKSIZE bytes. However, read may fail or may not read the full BLKSIZE bytes. In these two cases, write outputs garbage.

Exercise 4.6

What can go wrong with the following code segment to read from standard input and write to standard output?

 #define BLKSIZE 1024 char buf[BLKSIZE]; ssize_t bytesread; bytesread = read(STDIN_FILENO, buf, BLKSIZE); if (bytesread > 0)    write(STDOUT_FILE, buf, bytesread); 

Answer:

Although write uses bytesread rather than BLKSIZE , there is no guarantee that write actually outputs all of the bytes requested. Furthermore, either read or write can be interrupted by a signal. In this case, the interrupted call returns a “1 with errno set to EINTR .

Program 4.2 copies bytes from the file represented by fromfd to the file represented by tofd . The function restarts read and write if either is interrupted by a signal. Notice that the write statement specifies the buffer by a pointer, bp , rather than by a fixed address such as buf . If the previous write operation did not output all of buf , the next write operation must start from the end of the previous output. The copyfile function returns the number of bytes read and does not indicate whether or not an error occurred.

Example 4.7 simplecopy.c

The following program calls copyfile to copy a file from standard input to standard output.

 #include <stdio.h> #include <unistd.h> int copyfile(int fromfd, int tofd); int main (void) {    int numbytes;    numbytes = copyfile(STDIN_FILENO, STDOUT_FILENO);    fprintf(stderr, "Number of bytes copied: %d\n", numbytes);    return 0; } 
Exercise 4.8

What happens when you run the program of Example 4.7?

Answer:

Standard input is usually set to read one line at a time, so I/O is likely be entered and echoed on line boundaries. The I/O continues until you enter the end-of-file character (often Ctrl-D by default) at the start of a line or you interrupt the program by entering the interrupt character (often Ctrl-C by default). Use the stty -a command to find the current settings for these characters.

Program 4.2 copyfile1.c

The copyfile.c function copies a file from fromfd to tofd .

 #include <errno.h> #include <unistd.h> #define BLKSIZE 1024 int copyfile(int fromfd, int tofd) {    char *bp;    char buf[BLKSIZE];    int bytesread, byteswritten;    int totalbytes = 0;    for (  ;  ;  ) {       while (((bytesread = read(fromfd, buf, BLKSIZE)) == -1) &&              (errno == EINTR)) ;         /* handle interruption by signal */       if (bytesread <= 0)          /* real error or end-of-file on fromfd */          break;       bp = buf;       while (bytesread > 0) {          while(((byteswritten = write(tofd, bp, bytesread)) == -1 ) &&               (errno == EINTR)) ;        /* handle interruption by signal */          if (byteswritten <= 0)                     /* real error on tofd */             break;          totalbytes += byteswritten;          bytesread -= byteswritten;          bp += byteswritten;       }       if (byteswritten == -1)                       /* real error on tofd */           break;    }    return totalbytes; } 
Exercise 4.9

How would you use the program of Example 4.7 to copy the file myin.dat to myout.dat ?

Answer:

Use redirection. If the executable of Example 4.7 is called simplecopy , the line would be as follows .

 simplecopy < myin.dat > myout.dat 

The problems of restarting read and write after signals and of writing the entire amount requested occur in nearly every program using read and write . Program 4.3 shows a separate r_read function that you can use instead of read when you want to restart after a signal. Similarly, Program 4.4 shows a separate r_write function that restarts after a signal and writes the full amount requested. For convenience, a number of functions, including r_read , r_write , copyfile and readline , have been collected in a library called restart.c . The prototypes for these functions are contained in restart.h , and we include this header file when necessary. Appendix B presents the complete restart library implementation.

Program 4.3 r_read.c

The r_read.c function is similar to read except that it restarts itself if interrupted by a signal .

 #include <errno.h> #include <unistd.h> ssize_t r_read(int fd, void *buf, size_t size) {    ssize_t retval;    while (retval = read(fd, buf, size), retval == -1 && errno == EINTR) ;    return retval; } 
Program 4.4 r_write.c

The r_write.c function is similar to write except that it restarts itself if interrupted by a signal and writes the full amount requested .

 #include <errno.h> #include <unistd.h> ssize_t r_write(int fd, void *buf, size_t size) {    char *bufp;    size_t bytestowrite;    ssize_t byteswritten;    size_t totalbytes;    for (bufp = buf, bytestowrite = size, totalbytes = 0;         bytestowrite > 0;         bufp += byteswritten, bytestowrite -= byteswritten) {       byteswritten = write(fd, bufp, bytestowrite);       if ((byteswritten) == -1 && (errno != EINTR))          return -1;       if (byteswritten == -1)          byteswritten = 0;       totalbytes += byteswritten;    }    return totalbytes; } 

The functions r_read and r_write can greatly simplify programs that need to read and write while handling signals.

Program 4.5 shows the readwrite function that reads bytes from one file descriptor and writes all of the bytes read to another one. It uses a buffer of size PIPE_BUF to transfer at most PIPE_BUF bytes. This size is useful for writing to pipes since a write to a pipe of PIPE_BUF bytes or less is atomic. Program 4.6 shows a version of copyfile that uses the readwrite function. Compare this with Program 4.2.

Program 4.5 readwrite.c

A program that reads from one file descriptor and writes all the bytes read to another file descriptor .

 #include <limits.h> #include "restart.h" #define BLKSIZE PIPE_BUF int readwrite(int fromfd, int tofd) {    char buf[BLKSIZE];    int bytesread;    if ((bytesread = r_read(fromfd, buf, BLKSIZE)) == -1)       return -1;    if (bytesread == 0)       return 0;    if (r_write(tofd, buf, bytesread) == -1)       return -1;    return bytesread; } 
Program 4.6 copyfile.c

A simplified implementation of copyfile that uses r_read and r_write .

 #include <unistd.h> #include "restart.h" #define BLKSIZE 1024 int copyfile(int fromfd, int tofd) {    char buf[BLKSIZE];    int bytesread, byteswritten;    int totalbytes = 0;    for (  ;  ;  ) {       if ((bytesread = r_read(fromfd, buf, BLKSIZE)) <= 0)          break;       if ((byteswritten = r_write(tofd, buf, bytesread)) == -1)          break;       totalbytes += byteswritten;    }    return totalbytes; } 

The r_write function writes all the bytes requested and restarts the write if fewer bytes are written. The r_read only restarts if interrupted by a signal and often reads fewer bytes than requested. The readblock function is a version of read that continues reading until the requested number of bytes is read or an error occurs. Program 4.7 shows an implementation of readblock . The readblock function is part of the restart library. It is especially useful for reading structures.

Program 4.7 readblock.c

A function that reads a specific number of bytes .

 #include <errno.h> #include <unistd.h> ssize_t readblock(int fd, void *buf, size_t size) {    char *bufp;    size_t bytestoread;    ssize_t bytesread;    size_t totalbytes;    for (bufp = buf, bytestoread = size, totalbytes = 0;         bytestoread > 0;         bufp += bytesread, bytestoread -= bytesread) {       bytesread = read(fd, bufp, bytestoread);       if ((bytesread == 0) && (totalbytes == 0))          return 0;       if (bytesread == 0) {          errno = EINVAL;          return -1;       }       if ((bytesread) == -1 && (errno != EINTR))          return -1;       if (bytesread == -1)          bytesread = 0;       totalbytes += bytesread;    }    return totalbytes; } 

There are only three possibilities for the return value of readblock . The readblock function returns 0 if an end-of-file occurs before any bytes are read. This happens if the first call to read returns 0. If readblock is successful, it returns size , signifying that the requested number of bytes was successfully read. Otherwise, readblock returns “1 and sets errno . If readblock reaches the end-of-file after some, but not all, of the needed bytes have been read, readblock returns “1 and sets errno to EINVAL .

Example 4.10

The following code segment can be used to read a pair of integers from an open file descriptor.

 struct {    int x;    int y; } point; if (readblock(fd, &point, sizeof(point)) <= 0)    fprintf(stderr, "Cannot read a point.\n"); 

Program 4.8 combines readblock with r_write to read a fixed number of bytes from one open file descriptor and write them to another open file descriptor.

Program 4.8 readwriteblock.c

A program that copies a fixed number of bytes from one file descriptor to another .

 #include "restart.h" int readwriteblock(int fromfd, int tofd, char *buf, int size) {    int bytesread;    bytesread = readblock(fromfd, buf, size);    if (bytesread != size)                      /* can only be 0 or -1 */       return bytesread;    return r_write(tofd, buf, size); } 
Team-FLY


Unix Systems Programming
UNIX Systems Programming: Communication, Concurrency and Threads
ISBN: 0130424110
EAN: 2147483647
Year: 2003
Pages: 274

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