Operating System Tutorials
Operating System Tutorials
Augustine
Department
Department ofofComputing
Mathematics and Computer
and Information Science
Technology
CS31A
COMP 2604- Semester I, 2005/2006
- Operating Systems
Tutorial
Lab 4 #4
The UNIX operating system kernel keeps track of open files for each process by using “file
descriptors”. File descriptors are non-negative integers. It is customary to have the first
three file descriptors reserved for special purposes.
Descriptor Purpose
0 Standard Input (keyboard by default)
1 Standard Output (screen by default)
2 Standard Error (screen by default)
A C programmer can perform low-level file operations using file descriptors. This approach
is necessary for some of the exercises that will be performed below.
File Permissions
“File Permissions” indicate what operations a user is allowed to do with a file. There are
three (3) different operations that are permissible:
• r (read)
• w (write)
• x (execute)
When you use the ls command to view the contents of a directory, it is possible to see the
file permissions associated with each file.
. Open a shell window.
. Enter the command
ls -l [ENTER]
1
You will notice that in the first column, the file permissions are displayed. Ignoring the first
character, there are 3 groups of 3 characters. The first three represent the read, write and
execute permissions for the User. The next three are for the Group and the last three are
for Others.
The creat() function is used to create a new file. This function takes two (2) parameters.
The first is the name of the file to be created. The second in an integer that specifies the file
permissions that will be used to create the file. The return value of creat() is an integer
representing the file descriptor for the file created.
The permissions integer is usually specified as a 3-digit octal number, which is constructed as
follows. The leftmost digit represents permissions for the User. The middle digit represents
permissions for the Group. And the rightmost digit represents permissions for Others.
To understand how an octal digit can represent file permissions, the digit must first be
written as a 3-digit binary number. When written as such, the bits represent the read, write
and execute permission, where a 1 means “set” and a 0 means “not set”.
So for example if the second parameter to creat() is the octal number 0777, this would
indicate that the file to be create should allow reading, writing and executing for users,
group members and all others.
. Create a new C program called testcreat.c.
. At the top of the source code, include the header file fcntl.h.
. Make a call to creat() to create a file called "file.dat", with rwx permissions for
the user, and r-- permissions for the group and others.
. Capture the return value of the above function call in an integer called fd (short for
file descriptor).
. Compile and run testcreat.c
. At the shell prompt, use the ls -l command to view the files. Search for the file you
just created.
2
Closing a File Using close()
The close() function is used to close a file. The only argument to this function is an integer
representing the file descriptor of the file to be closed.
. Add a call to close() in your above code to close the file before exiting.
The open() function is used to open a file. There is more than one version of this function,
each version takes a different number of arguments. Here, we are interested in the version
that takes 2 arguments. The signature is
The first argument fd is a file descriptor corresponding to the file where the data will be
written. The pointer buff is a pointer to the place in memory where the data to be written
is stored, and num bytes is the number of bytes to write.
. Save a copy of your above code as testwrite.c
. Declare an integer x and initialize it to the value 5.
. After the call to creat(), add a write() statement to write the contents of the
variable x to the file.
. Compile and run the program.
. Use the command ls -l to determine the size of the file you created. Is the size what
you expect?
3
Reading from a File Using read()
The read() function is used to read data from a file. Its signature is
fd is a file descriptor for the file from which data will be read. buff is a pointer to a
place in memory where the data will be stored. num bytes is the number of bytes to read
from the file.
. Save a copy of your above code as testread.c.
. Remove the call to creat().
. In its place call open() to open the file.dat for reading.
. Declare an integer y, but do not initialize it.
. After the open() statement, use read() to read an integer from the file into the
variable y.
. Print the value of y.
. Compile and run testread.c.
By default any given process has three “streams” associated with it. These are, an input
stream, an output stream and an error stream. The input stream is by default the keyboard.
The output stream is by default the screen, and the error stream is also by default the screen.
As noted before, these streams are identified by the file descriptors 0, 1 and 2 respectively.
Thus reading from the keyboard is actually conceptualized as reading from a “file”. Similarly,
writing to the screen is conceptualized as writing to a “file”.
It is possible to change these streams by manipulating either process’ file descriptors.
. Dowload the source code redirect output.c.
. Compile and run the program.
. Use the following command to view the output of the program, which would have
been redirected to the file output.txt
4
duplicate will be made. The statement dup2(fd1, fd2); closes fd2, and duplicates fd1
into its place.
In the source code of redirect output.c, the statement dup2(fd, 1); is used. This closes
file descriptor 1 (i.e. the screen output stream) and duplicates the file descriptor fd into its
place. Thus anything that the program prints that would normally go to the screen would
instead go to the file associated with fd.
When a process spawns a child process, both the parent and the child share the same input,
output and error streams. Thus, output produced by the parent and child can become
intermixed and hence confusing to read.
This problem can be solved by redirecting the output of the child, or the parent, or both.
. Download the source code redirect parent child output.c.
. Compile and run the program.
. View the output of the child and the parent in their respective files.
One of the methods available for inter-process communication in UNIX is the pipe. A pipe
can be thought of as a communication link. There are two “ends” on a pipe, the “read end”
and the “write end”.
One process can put data into the pipe at the write end, and another process can read data
from the pipe from the read end.
The pipe() function is used to create a pipe. It takes a single argument which should be
a size-2 integer array. Once the call to pipe() has been made, index 0 of the integer array
contains a file descriptor corresponding to the read end of the pipe and index 1 contains a
file descriptor corresponding to the write end of the pipe.
. Download the source code parent child pipe.c.
. Compile and run the program.
. Modify the program so that the for loop runs up to 100000. Save your program as
parent child pipe 2.c
Note that you will need to modify the number of bytes read and written in each step.
. Compile and run parent child pipe 2.c
. Modify the code further so that the parent and child output to files called output1.txt
and output2.txt respectively.