CS 3733 Operating Systems Notes: UNIX I/O

Unix uses device independence.

I/O is done through device drivers that have a standard interface.

5 main system calls for I/O:

These calls return -1 on error and set errno

UNIX I/O is done with file descriptors.
Normally, when your program starts, there are 3 file descriptors open:


   #include <unistd.h>

   ssize_t read(int fildes, void *buf, size_t nbyte);
Look at Program 4.1 on page 95 that reads in a line.


   #include <unistd.h>

   ssize_t write(int fildes, const void *buf, size_t nbyte);
Look at the program simplecopy on page 97 that copies a file using copyfile
Look at Program 4.2 on page 98, copyfile1.
Look at Program 4.3 on page 99, r_read: restart if interrupted by a signal.
Look at Program 4.4 on page 99, r_write: also restarts if fewer bytes written.
Look at Program 4.4 on page 100, readwrite: does one read on one write.
Look at Program 4.6 on page 100, simple implentation of copyfile.
Look at Program 4.7 on page 101, read a specific number of bytes

Example 4.10: Read a pair of integers using readblock

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

open and close

   #include <fcntl.h>
   #include <sys/stat.h>
   int open(const char *path, int  oflag);
   int open(const char *path, int  oflag, mode_t mode);
Possible values of the flag include:
O_RDONLY: read only
O_WRONLY: write only
O_RDWR: read and write
O_APPEND: writes always write to end
O_CREAT: create the file if it does not exist
O_EXCL: used with O_CREAT, return an error if file exists
O_NOCTTY: do not become a controlling terminal
O_NONBLOCK: do not block if not ready to open, also affects reads and writes
O_TRUNC: discard previous contents

You must use the 3-parameter form of open if the O_CREAT flag is used. This specifies permissions.

Figure 4.10 (page 105): Historical layout of the permissions mask.

You should refer to the permissions with the POSIX symbolic names defined in sys/stat.h.

S_IRUSR read permission bit for owner
S_IWUSR write permission bit for owner
S_IXUSR execute permission bit for owner
S_IRWXU read, write, execute for owner
S_IRGRP read permission bit for group
S_IWGRP write permission bit for group
S_IXGRP execute permission bit for group
S_IRWXG read, write, execute for group
S_IROTH read permission bit for others
S_IWOTH write permission bit for others
S_IXOTH execute permission bit for others
S_IRWXO read, write, execute for others
S_ISUID set user ID on execution
S_ISGID set group ID on execution

Table 4.1, Page 105: POSIX symbolic names for file permission.

Look at program 4.9: copyfilemain that copies a file.

   #include <unistd.h>

   int close(int fildes);
Open files are closed when your program exits normally.

Look at Program 4.10, r_close on page 107.


Look at Program 4.11 that uses two processes to monitor 2 file descriptors.

How would you print out the total number of bytes read from the two files?

   #include <sys/select.h>
   int select(int nfds, fd_set *restrict readfds,
              fd_set *restrict writefds, fd_set *restrict errorfds,
              struct timeval *restrict timeout);
   void FD_CLR(int fd, fd_set *fdset);
   int FD_ISSET(int fd, fd_set *fdset);
   void FD_SET(int fd, fd_set *fdset);
   void FD_ZERO(fd_set *fdset);

Look at Program 4.12, whichisready, a function returns a file descriptor that is ready to be read.

Look at Program 4.13, copy2files that uses select to concurrently copy two files.

Look at Program 4.14, monitorselect, a function that monitors an array of file descriptors.

Look at Program 4.15, waitfdtimed, a function that uses the timeout feature of select to wait for a file descriptor with a timeout.

Look at Program 4.16, reatimed, a function that uses waitfdtimed.

File Representation

Figure 4.2 (page 120): Relationship between the file descriptor table, the system file table and the in-memory inode table.

File Pointers and Buffering
Use fopen, fclose, fread, fwrite, fprintf, fscanf, etc.
Example 4.24 (page 122) shows how to open a file for output using file pointers.
Example 4.24:

   FILE *myfp;

   if ((myfp = fopen("/home/ann/my.dat", "w")) == NULL)
      perror("Failed to open /home/ann/my.dat");
      fprintf(myfp, "This is a test");

Figure 4.3 (page 122): Schematic use of a file pointer after fopen.

Exercise 4.25: How does the output appear when the program bufferout executes?

Exercise 4.26: How does the output appear when the program bufferinout executes?

Inheritance of File Descriptors
When fork creates a child, the child inherits a copy of the parents address space, including the file descriptor table.

Example 4.27: In the program openfork the child inherits the file descriptor from the parent. Each process reads and outputs on character of the file. This is shown in Figure 4.4.

Figure 4.4 (page 126): If the parent opens my.dat before the fork, both parent and child share the system file table entry.

Example 4.32 (page 125): The program forkopen shows a similar code segment in which the fork is done before the open.
This is shown in Figure 4.5.

Figure 4.5 (page 127): If the parent and child open my.dat after the fork, the two processes use different system file table entries.

What output would be generated by the programs fileiofork and fileioforkline?

Filters and Redirection
Example 4.35 (page 129): Consider the following command:
cat > my.file
Figure 4.6 (page 130) shows the file descriptor table for this.

Figure 4.6 (page 130): The status of the file descriptor table before and after redirection for the process that is executing cat > my.file

Redirection can be done by copying one entry of the file descriptor table into another.

This is accomplished with the dup2 system call.

#include <unistd.h>
int dup2(int fildes, int fildes2);
It closes fildes2 and then copies the pointer of entry fildes into the entry fildes2.
Program 4.18, redirect, shows how this can be done.

Figure 4.7 (page 131): The status of the file descriptor table during the execution of Program 4.18.