File IO

File Descriptors

A file descriptor is a non-negative integer. When we open an existing file or create a new file, the kernel returns a file descriptor to the process.

STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO

open Function

#include <fcntl.h>

int open(const char *pathname, int oflag, ... /* mode_t mode   */);
  • Returns: file descriptor if OK, -1 on error
  • oflag
oflag meaning
O_RDONLY Open for reading only.
O_WRONLY Open for writing only.
O_RDWR Open for reading and writing.
optional  
O_APPEND Append to the end of file on each write.
O_CREAT Create the file if it doesn’t exist. This option requires a third argument to the open function, the mode, which specifies the access permission bits of the new file.
O_EXCL Generate an error if O_CREAT is also specified and the file already exists.
O_TRUNC If the file exists and if it is successfully opened for either write-only or readwrite, truncate its length to 0.
O_NOCTTY If the pathname refers to a terminal device, do not allocate the device as the controlling terminal for this process.
O_NONBLOCK O_NONBLOCK
If the pathname refers to a FIFO, a block special file, or a character special file, this option sets the nonblocking mode for both the opening of the file and subsequent I/O.    
  O_DSYNC Have each write wait for physical I/O to complete, but don’t wait for file attributes to be updated if they don’t affect the ability to read the data just written.
  O_RSYNC O_RSYNC
Have each read operation on the file descriptor wait until any pending writes for the same portion of the file are complete.    
  O_SYNC Have each write wait for physical I/O to complete, including I/O necessary to update file attributes modified as a result of the write.
     

creat Function

#include <fcntl.h>

int creat(const char *pathname, mode_t mode);
  • equivalent to open (pathname, O_WRONLY | O_CREAT | O_TRUNC, mode);

close Function

#include <unistd.h>

int close(int filedes);

lseek Function

#include <unistd.h>

off_t lseek(int filedes , off_t offset, int whence);
  • whence may be one of SEEK_SET, SEEK_CUR or SEEK_END
#include<stdio.h>
#include <unistd.h>

int main() {
    if (lseek(STDIN_FILENO,0,SEEK_SET)<0){
        printf("can not seekn");
    } else{
        printf("can seekn");
    }
    return 0;
}
> ./seekable.out < /dev/seekable.c
can seek
> cat seekable.c | ./seekable.out
can not seek
#include<stdio.h>
#include <apue.h>
#include <fcntl.h>

int main() {
    char buf1[] = "abc";
    char buf2[] = "dfe";
    int fd;
    if ((fd = creat("hole.tmp", FILE_MODE)) < 0)
        err_sys("create file errorn");
    if ((write(fd, buf1, strlen(buf1))) < 0)
        err_sys("write errorn");
    if ((lseek(fd, 100000000, SEEK_CUR)) == -1)
        err_sys("seek errorn");
    if ((write(fd, buf2, strlen(buf2))) < 0)
        err_sys("write errorn");
    return 0;
}
  • A hole in a file isn’t required to have storage backing it on disk
> ./file_hole.out             
> dd bs=1 count=100006 if=/dev/zero of=nohole.tmp
100006+0 records in
100006+0 records out
100006 bytes (100 kB) copied, 0.190321 s, 525 kB/s
> ls -ls
 12 -rwxr-xr-x 1 lion lion  10872 Nov 12 13:42 file_hole.out
  8 -rw-r--r-- 1 lion lion 100006 Nov 12 13:42 hole.tmp
100 -rw-r--r-- 1 lion lion 100006 Nov 12 13:42 nohole.tmp

read Function

#include <unistd.h>

ssize_t read(int filedes, void *buf, size_t nbytes);
  • Returns nuber of bytes read , 0 if EOF , 1 on error

write Function

#include <unistd.h>

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

The return value is usually equal to the nbytes argument; otherwise, an error has occurred.

IO Efficiency

#include<stdio.h>
#include <apue.h>
#include <stdlib.h>

int copy(int bufsize) {
    int n;
    char buf[bufsize];

    while ((n = read(STDIN_FILENO, buf, bufsize)) > 0) {
        if (n != write(STDOUT_FILENO, buf, n)) {
            err_sys("write errorn");
            return -1;
        }
    }

    if (n < 0) {
        err_sys("read errorn");
        return -1;
    }
}

int main(int argc, char **argv) {
    if (argc != 2) {
        err_sys("usage copy.out bufsizen");
    }

    int bufsize = atoi(argv[1]);
    copy(bufsize);
    return 0;
}

File Sharing

Kernel data structures for open files

Two independent processes with the same file open

Atomic Operation

Appending to a File

pread and pwrite Functions

#include <unistd.h>

ssize_t pread(int filedes, void *buf, size_t nbytes, off_t offset);
ssize_t pwrite(int fileses, const void *buf, size_t nbytes, off_t offset);

Creating a File

dup and dup2 Functions

#include <unistd.h>

int dup(int filedes);
int dup2(int filedes, int filedes2);

The new file descriptor returned by dup is guaranteed to be the lowest-numbered available file descriptor.
With dup2, we specify the value of the new descriptor with the filedes2 argument.
If filedes2 is already open, it is first closed. If filedes equals filedes2, then dup2 returns filedes2 without closing it.

Kernel data structures after dup(1)

dup2(filedes, filedes2); is equivalent to

  • dup example
    ```c
    #include
    #include
    #include

int main() {
int fd;
if ((fd = dup(STDOUT_FILENO)) == -1)
err_sys(“dup failed”);
printf(“fd=%dn”, fd);
if (write(fd, “aaan”, 4) != 4)
err_sys(“write failed”);
exit(0);
}


 

fd=3
aaa

- dup2 return value


```c
close(filedes2);
fcntl(filedes, F_DUPFD, filedes2);