Wednesday, December 5, 2007

Unbuffered I/O System Calls (apue ch3)

apue ch3 covers Unbuffered File I/O System Calls. It starts with:
  • open takes a path and returns a file descriptor (or -1 on error)
  • The kernel hands a file descriptor int to a process which reads or writes to it
  • creat is equivalent to open(path, O_WRONLY | O_CREAT | O_TRUNC, mode)
  • close(fd) releases process locks on a file and returns 0 (or -1)
  • Every open file has a current offset --stored in kernel at no I/O cost-- initialized to 0 unless O_APPEND is used
  • lseek (fd, off_t, whence) returns the offset (or -1)
  • whence can be: SEEK_SET (off_t from beginning) SEEK_CUR (off_t from current) SEEK_END (off_t + file size)
  • currpos = lseek(fd, 0, SEEK_CUR); i.e. the new file offset zero bytes from current is a goodway to get the current offset
  • write(fd, *buff, n_bytes) writes n_bytes of *buff to fd, increments offset, returns n_bytes or -1
  • read(fd, *buff, n_bytes) reads n_bytes of *buff from fd, increments offset, returns n_bytes or -1
  • unlink(path) deletes a file
  • chmod(path, mode) changes file permissions
  • stat(path, struct stat *sb) returns ptr to stat struc with file inode info
Note that the last three sys calls above are not in ch3 of apue and that I borrowed them from File-Related System Calls in FreeBSD.

With this in mind we can "create a hole in a file". I.e. the filesystem just pretends that at a particular place in the file there is zero bytes, but no actual disk sectors are used. When this happens the offset is still incremented so that future writes don't fill the hole. E.g. hole.c:

if ( (fd = creat("file.hole", FILE_MODE)) < 0)
   err_sys("creat error");
// create file.hole in current directory

if (write(fd, buf1, 10) != 10)
 err_sys("buf1 write error");
// write 10 bytes of buf1 to fd (offset = 10)

if (lseek(fd, 40, SEEK_SET) == -1)
 err_sys("lseek error");
// seek 40 bytes in memory from beginning (offset = 40) 

if (write(fd, buf2, 10) != 10)
 err_sys("buf2 write error");
// write 10 bytes of buf2 to fd (offset = 50)
Note that ls reports that file.hole is 50 bytes. We can then see the holes with od:
:~/code/c/apue/ch3> ll
total 20K
-rw-r--r-- 1 anonymous anonymous  542 2007-12-05 23:42 hole.c
-rwxr-xr-x 1 anonymous anonymous 8.5K 2007-12-05 23:42 a.out*
-rw-r--r-- 1 anonymous anonymous   50 2007-12-05 23:56 file.hole
f:~/code/c/apue/ch3> od -c file.hole 
0000000   a   b   c   d   e   f   g   h   i   j  \0  \0  \0  \0  \0  \0
0000020  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000040  \0  \0  \0  \0  \0  \0  \0  \0   A   B   C   D   E   F   G   H
0000060   I   J
0000062
:~/code/c/apue/ch3> 
Ch3 then provides an unbuffered cat which differs from K&R's Ch1.5.1 cat:
#include <stdio.h>
/* copy input to output; 2nd version  */
main()
{
    int c;
    while ((c = getchar()) != EOF)
        putchar(c);
}
Which uses Standard Buffered I/O and is covered later in apue ch5.

The rest of the chapter covers File Sharing Between Processes, Atomic Operations and ioctl.

No comments: