Tutorial Sheet #2
Tutorial Sheet #2
Topics Covered:
1. READ AND WRITE SYSTEM CALL
2. FILE SEEK (lseek FUNCTION CALL)
3. DUPLICATING FILE DESCRIPTOR (dup and dup2 SYSTEM CALLS)
4. SAVING AND DESTROYING FILES( sync, fsync, fdatasync SYSTEM CALLS)
5. TRUNCATING FILES(truncate and ftruncate SYSTEM CALLS)
6. SOLVED EXAMPLES and EXERCISE
write operation. After a successful write, the file's offset is incremented by the number of bytes
actually written.
The write call does not actually write the contents to the disk. It just transfers data to an
intermediate place called buffer cache and returns. At some later point (which can be ANY
arbitrary time), the kernel transfers the data from the buffer cache to the actual place in disk
where the file resides. The user process may never be able to find out when the data was actually
transferred to the disk. This delayed writing accounts for the speeds up the write call. This is
because read or write to buffer caches are very fast compared to read or write to disk. This
technique is referred to as delayed writing.
Solved Exercise 1:
Program to copy a file using read and write. It reads from standard input and writes to standard
output. Execute this program below:
#include<sys/types.h>
#include<sys/uio.h>
#include <unistd.h>
#define BUFFSIZE 4096
int main()
{
int n;
char buf[BUFFSIZE];
while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
if (write(STDOUT_FILENO, buf, n) != n)
err_sys("write error");
if (n < 0)
err_sys("read error");
exit(0);
}
In this example, we use the two defined names, STDIN_FILENO and STDOUT_FILENO, from
<unistd.h>. The program doesn't close the input file or output file. Instead, the program uses the
feature of the UNIX kernel that closes all open file descriptors in a process when that process
terminates.
Solved Exercise 2:
A Complete IO Program: Using open(), creat(), read(), write() and close()
/* This program reads contents from an existing file "some.txt" and copies the contents into
another newly created file "new.txt" */
#
#
#
#
#
#
include<stdio.h>
include<fcntl.h>
include<unistd.h>
include<string.h>
include<sys/types.h>
include<sys/uio.h>
if(n== -1) {
printf("Error writing to stdout\n");
return 3;
}
}
close(fd);
close(fd1);
return 0;
}
First argument in above function call is file descriptor, a handle to file opened. Second argument
is the offset i.e. how many bytes to move either forward or backward. Third argument is from
which position to move in the file. It means it will add offset to the whence.
If call is successful it will return new offset otherwise it will return -1 (error situation)
The interpretation of the offset depends on the value of the whence argument.
1. If whence is SEEK_SET, the file's offset is set to offset bytes from the beginning of the file.
2. If whence is SEEK_CUR, the file's offset is set to its current value plus the offset. The offset
can be positive or negative.
3. If whence is SEEK_END, the file's offset is set to the size of the file plus the offset. The
offset can be positive or negative.
Solved Exercise 3:
Program to test whether standard input is capable of seeking or not.
#include <unistd.h>
#include<stdio.h>
int main(){
if (lseek(STDIN_FILENO, 0, SEEK_CUR) == -1)
printf("cannot seek\n");
else printf("seek OK\n");
exit(0);
}
Solved Exercise 4:
Write a program that prints a file backwards.
#include<stdlib.h>
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<sys/stat.h>
#include<unistd.h>
exit(-1);
}
//rwx permission for user on dest
if ((dest = creat(argv[2], 0700)) < 0) {
fprintf(stderr, "can't create dest");
exit(-1);
}
//filesize is lastbyte +offset
filesize = lseek(source, (off_t) 0, SEEK_END);
printf("Source file size is %d\n", filesize);
//read byte by byte from end
for (i = filesize - 1; i >= 0; i--) {
lseek(source, (off_t) i, SEEK_SET);
n = read(source, &buf, 1);
if (n != 1) {
fprintf(stderr, "can't read 1 byte");
exit(-1);
}
n = write(dest, &buf, 1);
if (n != 1) {
fprintf(stderr, "can't write 1 byte");
exit(-1);
}
}
write(STDOUT_FILENO, "DONE\n", 5);
close(source);
close(dest);
return 0;
}
Solved Exercise 5:
If process executes newfd = dup(1), then how the kernel data structure look like.
Solution:
Solved Exercise 6
As The dup() call provides the programmer the flexibility to use more than one file descriptor on
the same file, without having to worry about synchronizing file offsets every time. The following
program demonstrates the same:
/* Using two open calls on the same file */
# include<string.h>
# include<stdio.h>
# include<fcntl.h>
int main(int argc,char **argv){
int fd1,fd2;
fd1=open("xyz.txt",O_RDWR);
fd2=open("xyz.txt",O_RDWR);
printf("fd1 file offset before write: %d\n",lseek(fd1,0,1));
write(fd1,"hello",strlen("hello"));
printf("fd2 file offset before write: %d\n",lseek(fd2,0,1));
write(fd2,"world",strlen("world"));
return 0;
}
Solved Exercise 7:
Same file pointer (that is, both file descriptors share one file pointer)
Same access mode (read, write or read/write)
Same file status flags (refer to stat() system call)
dup2() is frequently used to re-direct file input\output. That is to reset the standard file input or
output. The file descriptor STDIN_FILENO (standard input) does not always have to be the
terminal (console). It can be made to refer to any other file (or device). The redirection is made
simpler because dup2() automatically takes care of closing the old file descriptor.
sync function
The sync function simply queues all the modified block buffers for writing and returns; it does
not wait for the disk writes to take place.
#include<unistd.h>
void sync(void);
sync() tells the kernel to flush the buffer cache. Kernel flushes the buffer cache at the earliest
possible after the sync() call is issued. sync() is heavy handed in the sense that all the data in the
buffer cache, written by so many different processes to so many different files get copied to the
disk. Usually the sync() command is executed before the UNIX system is shutdown or a
removable device is unmounted.
fsync function
The function fsync refers only to a single file, specified by the file descriptor filedes, and waits
for the disk writes to complete before returning. The intended use of fsync is for an application,
such as a database, that needs to be sure that the modified blocks have been written to the disk. A
frequent call to sync() would affect other users too much. Because every time sync() is called,
the entire contents of the buffer cache has to be copied into the disk and this is a very time
consuming operation. A solution to this problem is to use the fsync() call.
#include<unistd.h>
int fsync(int fd); /* Returns 0 on error or -1 on error */
This function accepts a file descriptor as an argument and all cached changes only for that file is
written out on the disk.
fdatasync function
The fdatasync function is similar to fsync, but it affects only the data portions of a file. With
fsync, the file's attributes are also updated synchronously.
#include<unistd.h>
int fdatasync(int filedes);
7. Truncating files
The open and create calls when used with the appropriate flags can be used to truncate a file.
However if a file needs to be truncated after its opened, the following calls could be used.
/* File Descriptor */
/* New Length */
Oddly enough, the truncate() and ftruncate() calls can also be used to expand the file, if the new
length is greater than the original length of the file. The expanded portion of the file would be
filled with bytes of zero.
Usage:
int x,y;
int fd=open(./pqr/lmn/file,O_RDWR);
x= truncate(./abc/xyz/filename,0);
y= ftruncate(fd,10);
5.
6.
Scatter Read and Gather Write: readv() and writev() system calls
Ignoring the file offset : pread() and pwrite() system calls
File status: stat() and fstat() system calls
Write a C program using UNIX system calls that would mimic the grep command , i.e.,
print the lines of a file which contain a given search word. Wildcard characters need not be
considered
Write a C program using UNIX system calls that would mimic the head and tail
commands. The commands should be supported with the option -n
Write your own version of the scanf() function. You are allowed to change the existing
signature of the scanf() function. But the semantics of scanf() should not change (obvious!)
Write a C program to Print the file flags of specified file descriptor.
Write a C program to Turn on one or more of the file status flags for a descriptor.
7.
8.
9. fcntl() function.