Reading and Writing

The UNIX kernel readies a file for I/O by giving you a file descriptor, which is returned by open(2) or creat(2). The file descriptor might represent an I/O device, a socket or, most often, a regular file. The I/O semantics vary somewhat, depending on what it is that your program is interacting with. This will be noted in a few places as you are introduced to the system calls for I/O.

Introducing read(2) and write(2)

These are perhaps the most basic of all UNIX I/O system calls. Their function synopsis is as follows:

#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>

ssize_t read(int fd,void *buf,size_t nbytes);

ssize_t write(int fd,const void *buf,size_t nbytes);

The read(2) and write(2) calls take the same arguments, with the exception that the write(2) function does not modify the buffer it is supplied with. Each must be supplied with an open file descriptor, which can be a socket.

The read(2) Function

The read(2) function reads into the buffer buf[] to a maximum of nbytes. The number of bytes actually read is the return value. If an error occurs, -1 is returned (with errno).

A return value of zero indicates that end-of-file has been reached. There is no error code associated with end-of-file, since this is not an error.

In some read contexts, you may receive fewer bytes than requested by the nbytes argument. This can happen when reading from regular files, when the end-of-file is reached while trying to satisfy the count. Otherwise, when reading from a regular file, you are guaranteed that the function will not return until nbytes is returned.

In all other read contexts, such as when reading from a socket, the count nbytes serves as a maximum number. Any number of bytes from one to nbytes may be returned.

Tip

For any slow device, it is possible for read(2) to return the error EINTR if a signal handler has handled a signal. Simply retry the read(2) call when this error is received.

A regular file is not considered a slow device.


The write(2) Function

The write(2) function writes from the supplied buffer buf exactly nbytes. It returns the number of bytes actually written. If an error occurs, the value -1 is returned (with errno).

For regular files, write(2) should always write the requested number of bytes nbytes. In other write contexts, the return value indicates the actual number of bytes written.

Tip

For any slow device, it is possible for write(2) to return the error EINTR if a signal handler has handled a signal. Simply retry the write(2) call when this error is received.

A regular file is not considered a slow device.


Applying UNIX I/O

The program in Listing 4.2 shows a simple I/O example, using the basic system calls. This program opens the file /etc/motd by default and copies its contents to the standard output device (file unit 1). A different pathname can be supplied by specifying it as the first command-line argument.

Code Listing 4.2. unixio.c—A Simple UNIX I/O Example Program
1:   /* unixio.c */
2:
3:   #include <stdio.h>
4:   #include <fcntl.h>
5:   #include <unistd.h>
6:   #include <errno.h>
7:   #include <string.h>
8:   #include <sys/types.h>
9:   #include <sys/uio.h>
10:
11:  int
12:  main(int argc,char **argv) {
13:      int z;                              /* Return status code */
14:      int n;                              /* # of bytes written */
15:      int fd;                             /* Read file descriptor */
16:      char buf[128];                      /* I/O Buffer */
17:      char *pathname = "/etc/motd";       /* Default file to open */
18:
19:      if ( argc > 1 )
20:          pathname = argv[1];             /* Choose a different file */
21:
22:      fd = open(pathname,O_RDONLY);       /* Open /etc/motd file */
23:
24:      if ( fd == -1 ) {
25:          fprintf(stderr,"%s: opening %s for read
",
26:              strerror(errno),pathname);
27:          return 1;                       /* Failed */
28:      }
29:
30:      for (;;) {
31:          z = read(fd,buf,sizeof buf);    /* Fill buf with read data */
32:          if ( !z )
33:              break;                      /* End of file */
34:          if ( z == -1 ) {
35:              fprintf(stderr,"%s: reading file %s
",
36:                  strerror(errno),pathname);
37:              return 2;                   /* Failed */
38:          }
39:
40:          n = write(1,buf,z);             /* Write out buffer contents */
41:          if ( n == -1 ) {
42:              fprintf(stderr,"%s: writing to stdout
",strerror(errno));
43:              return 3;                   /* Failed */
44:          }
45:      }
46:
47:      close(fd);                          /* Close the file */
48:
49:      return 0;
50:  }
						

The basic procedure used in Listing 4.2 is this:

  1. The pathname variable defaults to the C string "/etc/motd" (line 17) or uses the command line argument (lines 19 and 20).

  2. The file is opened with a call to open(2) (line 22).

  3. If the open(2) call fails, the error is reported (lines 24 to 28).

  4. An I/O loop is started in lines 30 to 45.

  5. The read(2) call reads as many bytes as it can to fill the buffer buf[]. The maximum number of bytes read is indicated by the argument sizeof buf.

  6. If there is no more data to be read, the return value will be zero, and the loop is exited (lines 32 and 33) with the break statement.

  7. If a read error occurs, the error is reported (lines 34 to 38).

  8. The data read into array buf[] is now written out to standard output (file unit 1 in line 40). Note that the number of bytes being written is z. This is the value returned from step 5.

  9. If a write error occurs, the error is reported (lines 41 to 44).

  10. When the loop is exited, the close(2) function is called (line 47).

The program in Listing 4.2 is called a simple program because it does not allow for the possibility that the write(2) call may not always write the full amount of data expected if the standard output is not a regular file. Furthermore, it does not allow for the possibility of the error EINTR, which it needs to do if there is any signal catching used in this program.

In a production quality program, the buffer size would be declared a larger size. Generally, a buffer like this should be a minimum of 1024 bytes in length to better match the I/O size that is being used by the operating system.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.136.170