I/O is done through device drivers that have a standard interface.
5 main system calls for I/O:
Figure 3.2 (page 86): Structure of a typical UNIX filesystem
Current Working Directory
\begin{synopsisverb} #include <unistd.h> char *getcwd(char *buf, size_t size);Example 3.2 (page 80) outputs the pathname of the current working directory. It uses PATH_MAX to determine the size of the buffer needed.
Example 3.3 (page 81) determines the implementation's maximum pathname length relative to the root directory using pathconf.
Reading Directories
#include <sys/types.hi> #include <dirent.h> DIR *opendir(const char *filename); struct dirent *readdir(DIR *dirp); void rewinddir(DIR *dirp); int closedir(DIR *dirp);opendir provides a handle for the other functions.
Program 3.1 (page 83) lists the files in a directory.
Inodes
A directory entry contains only a name and an index into a table
giving information about a file. The table and the index are both
referred to as an inode.
This is what an inode looks like:
Figure 3.3 (page 87): Inodes are used to represent information about a UNIX file.
Inodes are read using the system call stat
#include <sys/types.h> #include <sys/stat.h> int stat(const char *path, struct stat *buf); int fstat(int fildes, struct stat *buf); int lstat(const char *path, struct stat *buf);stat is given the name of a file.
dev_t st_dev; /* ID of device containing directory entry */ ino_t st_ino; /* Inode number */ mode_t st_mode; /* File mode (type and permissions) */ nlink_t st_nlink; /* Number of links */ uid_t st_uid; /* User ID of the file's owner */ gid_t st_gid; /* Group ID of the file's group */ dev_t st_rdev; /* ID for special files */ off_t st_size; /* File size in bytes */ timestruc_t st_atim; /* Time of last access */ timestruc_t st_mtim; /* Time of last data modification */ timestruc_t st_ctim; /* Time of last file status change */ blksize_t st_blksize; /* Preferred I/O block size */ blkcnt_t st_blocks; /* Number st_blksize blocks allocated */The timestruc_t is the same as the struct timespec structure which contains two fields:
time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */Example 3.6 (page 89) takes a directory name passed on the command line and lists the contents of that directory along with the time it was last modified.
Links
![]() |
![]() |
Figure 3.4 (page 91): A directory entry, inode, and data block for a simple file. | Figure 3.5 (page 92): Two hard links to the same file as in Figure 3.4. |
![]() |
![]() |
Figure 3.5 (page 92): Two hard links to the same file as in Figure 3.4. | Figure 3.6 (page 93): The situation after editing the file. The original file had inode 12345 and two hard links prior to being edited. |
![]() |
![]() |
Figure 3.5 (page 92): Two hard links to the same file as in Figure 3.4. | Figure 3.7 (page 93): The situation after editing the file with an editor that makes a backup copy. |
How to do it right:
open("/dirA/name1");
read
close
modify memory image file file
open("/dirA/name1",O_WRONLY|O_TRUNC);
write
close
Symbolic Links
Figure 3.8 (page 94): An ordinary file with a symbolic link to it.
Figure 3.9 (page 95): The situation after editing a file that has a symbolic link.
File Handles
Two methods of doing I/O in UNIX:
File descriptors: this is part of the UNIX specification, POSIX, etc.
File pointers: this is from the C standard
File Descriptors
Use the POSIX names STDIN_FILENO,
STDOUT_FILENO and STDERR_FILENO.
open, close, read, write,ioctl
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *path, int oflag, ...);Common oflag values: O_RDONLY O_WRONLY O_RDWR O_CREAT
Figure 3.10 (page 98): The layout of the permissions mask.
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 3.1, Page 99: POSIX.1 symbolic names for file permission bits.
Example 3.10 (page 98) show simple code to create a new file.
Figure 3.11 (page 100): 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 3.11 (page 101) shows how to open a file file output using file
pointers.
Figure 3.12 (page 102): Schematic use of a file pointer after fopen.
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 3.14 (pages 103-104) shows a code segment in which a child inherits
a file descriptor of an open file.
Figure 3.13 (page 105) shows the file descriptor tables and system file table
corresponding to this code.
Figure 3.13 (page 105): If the parent opens my.dat before the fork, both parent and child share the system file table entry.
Figure 3.14 (page 106): If the parent and child open my.dat after the fork, the two processes use different system file table entries.
Figure 3.15 (page 107): 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 <udistd.h> int dup2(int fildes, int fildes2);It closes fildes2 and then copies the pointer of entry fildes into the entry fildes2.
Pipes
From the command line:
ls -l | sort -nr +4 | head
produces a list of files sorted by size with only the largest ones displayed.
This is done with the pipe system call:
#include <unistd.h> int pipe(int fildes[2]);
Figures 3.18 to 3.20 (pages 111-112) show the state of the file descriptor table for Example 3.20.
Figure 3.18 (page 111): The status of the file descriptor table after the fork has been executed in Example 3.20.
Figure 3.19 (page 112): The status of the file descriptor table after the pup2's have been executed in both processes of Example 3.20.
Figure 3.20 (page 112): The status of the file descriptor table just before the execl's have been executed in both processes of Example 3.20.
read and write
#include <unistd.h> ssize_t read(int fildes, void *buf, size_t nbyte); ssize_t write(int fildes, const void *buf, size_t nbyte);
This code is not correct since it is not guaranteed that the write will actually write all of the bytes that it is requested to write.
Program 3.2 (pages 114-115) show how to do this correctly.
The program takes source and destination as command line parameters.
An error occurs if the destination file already exists.
A loop for the write handles the case of partial writes.
Non-blocking I/O Various control flags can be changed using fcntl:
#include <sys/types.h> #include <unistd.h> #include <fcntl.h> int fcntl(int fildes, int cmd, /* arg */ ...);Example 3.22 (page 116) is a program segment that sets an open file descriptor to non-blocking.
You can use non-blocking I/O to monitor 2 file descriptors, but this solution
uses busy waiting.
Note: Monday's class moved to 2.01.06 HSS
Attendance is mandatory. You must attend or send me email explaining why you cannot.
Check the course web page on Monday morning in case there is a change in this.
select
#include <sys/time.h> #include <sys/types.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); void FD_SET(int fd, fd_set *fdset); void FD_CLR(int fd, fd_set *fdset); int FD_ISSET(int fd, fd_set *fdset); void FD_ZERO(fd_set *fdset);Example 3.24 (pages 118-119) shows how to use this to monitor two file descriptors.
FIFOs
FIFOs, or named pipes allow you to use pipes between processes that are
not related.
You can create a named pipe with the mkfifo command or the
mkfifo system call.
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *path, mode_t mode);The programs readfifo and writefifo illustrate how these can be used.
Get readfifo.c or writefifo.c.
Note: There is something wrong with these programs. What is it?