Unit 2
Unit 2
Unit 2
2.Syntax in C language:
How it work in OS
Create new empty file on disk
Create file table entry
Set first unused file descriptor to point to file table entry
Return file descriptor used, -1 upon failure
open: Used to Open the file for reading, writing or
both.Syntax in C language
#include<sys/types.h>
#includ<sys/stat.h>
#include <fcntl.h>
int open (const char* Path, int flags [, int
mode ]);
Parameters
•Path : path to file which you want to use
•use absolute path begin with “/”, when you are not
work in same directory of file.
•Use relative path which is only file name with
extension, when you are work in same directory of file.
•flags : How you like to use
O_RDONLY: read only,
O_WRONLY: write only,
O_RDWR: read and write,
O_CREAT: create file if it doesn’t exist,
O_EXCL: prevent creation if it already exists.
How it works in OS
Find existing file on disk
Create file table entry
Set first unused file descriptor to point to file table entry
Return file descriptor used, -1 upon failure
//C program to illustrate open system call
#include<stdio.h>
#include<fcntl.h>
#include<errno.h>
extern int errno;
int main()
{ Output:
// if file does not have in directory fd = 3
// then file foo.txt is created.
int fd = open("foo.txt", O_RDONLY | O_CREAT);
if (fd ==-1)
{
// print which type of error have in a code
printf("Error Number % d\n", errno);
1. If write() has not written any data yet, it returns -1 and sets errno to
EINTR.
fd = open("foo.txt", O_RDONLY);
if (fd < 0)
{
perror("r1"); exit(1);
}
sz = read(fd, c, 10);
printf("called read(% d, c, 10). returned that”" %d bytes were read.\n", fd, sz);
c[sz] = '\0';
printf("Those bytes are as follows: % s\n", c);
}
Output:
called read(3, c, 10).
There are other access modes, which are termed as access modifier
flags, and one or more of the following can be specified by bitwise-
ORing them with one of the above access mode flags to alter the access
mechanism of the file.
O_APPEND - Append data to the end of file.
O_CREAT - Create the file if it doesn’t exist.
O_EXCL - Generate an error if O_CREAT is also
specified and the file already exists.
O_TRUNC - If file exists discard the file content and
set the file size to zero bytes.
O_NONBLOCK - Specify subsequent read or write on
the file should be non-blocking.
O_NOCTTY - Specify not to use terminal device file
as the calling process control terminal.
To illustrate the use of the above flags, the following
example statement opens a file called /usr/divya/usp
for read and write in append mode:
int fd=open(“/usr/divya/usp”, O_RDWR | O_APPEND,0);
If the file is opened in read only, then no other
modifier flags can be used.
If a file is opened in write only or read write, then
we are allowed to use any modifier flags along with
them.
The third argument is used only when a new file is
being created. The symbolic names for file permission
are given in the table in the previous page.
creat
This system call is used to create new regular files.
The prototype of creat is
#include <sys/types.h>
#include<unistd.h>
int creat(const char *pathname, mode_t mode);
#include<sys/types.h>
#include<unistd.h>
size_t read(int fdesc, void *buf, size_t nbyte);
The second argument, buf is the address of a buffer holding any data
read.
The third argument specifies how many bytes of data are to be read
from the file.
o When reading from a regular file, if the end of file is reached before
the requested number of bytes has been read. For example, if 30 bytes
remain until the end of file and we try to read 100 bytes, read returns
30. The next time we call read, it will return 0 (end of file).
F_SETFL Sets or clears access control flags that are specified in the third
argument to fcntl. The allowed access control flags are O_APPEND &
O_NONBLOCK.
F_GETFD Returns the close-on-exec flag of a file referenced by fdesc. If a
return value is zero, the flag is off; otherwise on.
F_SETFD Sets or clears the close-on-exec flag of a fdesc. The third argument to
fcntl is an integer value, which is 0 to clear the flag, or 1 to set the
flag.
F_DUPFD Duplicates file descriptor fdesc with another file descriptor. The third
argument to fcntl is an integer value which specifies that the
duplicated file descriptor must be greater than or equal to that
value. The return value of fcntl is the duplicated file descriptor
The fcntl function is useful in changing the access control flag of a file
descriptor.
For example: after a file is opened for blocking read-write access and
the process needs to change the access to non-blocking and in write-
append mode, it can call:
int cur_flags= fcntl(fdesc, F_GETFL);
int rc = fcntl(fdesc, F_SETFL, cur_flag | O_APPEND | O_NONBLOCK);
The following example reports the close-on-exec flag of fdesc, sets it to
on afterwards:
cout<<fdesc<<”close-on-exec”<<fcntl(fdesc, F_GETFD)<<endl;
(void) fcntl(fdesc, F_SETFD, 1); //turn on close-on-exec flag
The following statements change the standard input of a process to a file called FOO:
The dup and dup2 functions in UNIX perform the same file duplication function as fcntl.
#include<stdio.h>
int rename(const char * old_path_name,
const char * new_path_name);
The UNIX mv command can be implemented using the link and unlink APIs as shown:
#include <iostream.h>
#include <unistd.h>
#include<string.h>
int main ( int argc, char *argv[ ])
{
if (argc != 3 || strcmp(argv[1],argcv[2]))
cerr<<”usage:”<<argv[0]<<””<old_link><new_link>\n”;
else
if(link(argv[1],argv[2]) == 0)
return unlink(argv[1]);
return 1;
}
stat, fstat
The stat and fstat function retrieves the file attributes of a given file.
The only difference between stat and fstat is that the first argument of
a stat is a file pathname, where as the first argument of fstat is file
descriptor.
The prototypes of these functions are
#include<sys/stat.h>
#include<unistd.h>
int stat(const char *pathname, struct stat *statv);
int fstat(const int fdesc, struct stat *statv);
The second argument to stat and fstat is the address of a struct stat-
typed variable which is defined in the <sys/stat.h> header.
Its declaration is as follows:
Struct stat
{
dev_t st_dev; /* file system ID */
ino_t st_ino; /* file inode number */
mode_t st_mode; /* contains file type and permission */
nlink_t st_nlink; /* hard link count */
uid_t st_uid; /* file user ID */
gid_t st_gid; /* file group ID */
dev_t st_rdev; /*contains major and minor device#*/
off_t st_size; /* file size in bytes */
time_t st_atime; /* last access time */
time_t st_mtime; /* last modification time */
time_t st_ctime; /* last status change time */
};
The return value of both functions is
o 0 if they succeed
o -1 if they fail
o errno contains an error status code
The lstat function prototype is the same as that of stat:
int lstat(const char * path_name, struct stat* statv);
We can determine the file type with the macros as shown.
#include<unistd.h>
int access(const char *path_name, int flag);
if(access(“/usr/divya/usp.txt”, F_OK)==-1)
printf(“file does not exists”);
else
printf(“file exists”);
chmod, fchmod
The chmod and fchmod functions change file access
permissions for owner, group & others as well as the set_UID,
set_GID and sticky flags.
A process must have the effective UID of either the super-
user/owner of the file.
The prototypes of these functions are
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
int chmod(const char *pathname, mode_t flag);
int fchmod(int fdesc, mode_t flag);
The pathname argument of chmod is the path name of a file whereas
the fdesc argument of fchmod is the file descriptor of a file.
#include<unistd.h>
#include<sys/types.h>
int chown(const char *path_name, uid_t uid, gid_t gid);
int fchown(int fdesc, uid_t uid, gid_t gid);
int lchown(const char *path_name, uid_t uid, gid_t gid);
The program first converts a given user name to a user ID via getpwuid
function.
If that succeeds, the program processes each named file as follows:
it calls stat to get the file group ID, then it calls chown to change the file
user ID.
The differences between the read lock and the write lock is
that when write lock is set, it prevents the other process from
setting any over-lapping read or write lock on the locked file.
A kernel at the system call level does not enforce advisory locks.
This means that even though a lock may be set on a file, no other
processes can still use the read and write functions to access the file.
To make use of advisory locks, process that manipulate the
same file must co-operate such that they follow the given
below procedure for every read or write operation to the file.
Furthermore, if a process now unblocks the file from 128 to 480, it will
own two write locks on the file: one from 0 to 127 and the other from
481 to 512. This process is called “Lock Splitting”.
UNIX systems provide fcntl function to support file locking.
By using fcntl it is possible to impose read or write locks on
either a region or an entire file.
The entries for “.” and “..” are automatically created. The
specified file access permission, mode, are modified by the
file mode creation mask of the process.
Function Use
opendir Opens a directory file for read-only. Returns a file
handle dir * for future reference of the file.
readdir Reads a record from a directory file referenced by dir-
fdesc and returns that record information.
rewinddir Resets the file pointer to the beginning of the directory
file referenced by dir-fdesc. The next call to readdir will
read the first record from the file.
closedir closes a directory file referenced by dir-fdesc.
An empty directory is deleted with the rmdir API.
The prototype of rmdir is
#include<unistd.h>
int rmdir (const char * path_name);
If the link count of the directory becomes 0, with the call and no other
process has the directory open then the space occupied by the directory
is freed.
UNIX systems have defined additional functions for random access of
directory file records.
Function Use
telldir Returns the file pointer of a given dir_fdesc
seekdir Changes the file pointer of a given dir_fdesc to a
specified address
following list_dir.C program illustrates the uses of the mkdir, opendir,
readdir, closedir and rmdir APIs:
#include<iostream.h>
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<sys/stat.h>
#if defined(BSD) && !_POSIX_SOURCE
#include<sys/dir.h>
typedef struct dirent Dirent;
#else
#include<dirent.h>
typedef struct dirent Dirent;
#endif
int main(int agc, char* argv[])
{
Dirent* dp;
DIR* dir_fdesc;
while(--argc>0)
{
if(!(dir_fdesc=opendir(*++argv)))
{
if(mkdir(*argv,S_IRWXU | S_IRWXG | S_IRWXO)==-1)
perror("opendir"); continue;
}
for(int i=0;i<2;i++)
for(int cnt=0;
dp=readdir(dir_fdesc);)
{
if(i) cout<<dp->d_name<<endl;
if(strcmp(dp->d_name,".") && strcmp(dp->d_name,".."))
cnt++;
}
if(!cnt)
{ rmdir(*argv); break; }
rewinddir(dir_fdesc);
} closedir(dir_fdesc); } }
Device file APIs
Device files are used to interface physical device with application
programs.
A process with superuser privileges to create a device file must call the
mknod API.
The user ID and group ID attributes of a device file are assigned in the
same manner as for regular files.
When a process reads or writes to a device file, the kernel uses the
major and minor device numbers of a file to select a device driver
function to carry out the actual data transfer.
Device file support is implementation dependent. UNIX System
defines the mknod API to create device files.
The prototype of mknod is
#include<sys/stat.h>
#include<unistd.h>
int mknod(const char* path_name, mode_t mode, int device_id);
The first argument pathname is the pathname of a device file to be
created.
The second argument mode specifies the access permission, for the
owner, group and others, also S_IFCHR or S_IBLK flag to be assigned to
the file.
The third argument device_id contains the major and minor device
number.
Example
mknod(“SCSI5”,S_IFBLK | S_IRWXU | S_IRWXG | S_IRWXO,(15<<8) |
3);
The above function creates a block device file “divya”, to which all the
three i.e. read, write and execute permission is granted for user, group
and others with major number as 8 and minor number 3.
On success mknod API returns 0 , else it returns -1
The following test_mknod.C program illustrates the use of the mknod, open, read, write and
close APIs on a block device file.
#include<iostream.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
int main(int argc, char* argv[])
{ if(argc!=4)
{ cout<<"usage:"<<argv[0]<<"<file><major_no><minor_no>"; return 0; }
int major=atoi(argv[2],minor=atoi(argv[3]);
(void) mknod(argv[1], S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, (major<<8) | minor);
int rc=1,fd=open(argv[1],O_RDW | O_NONBLOCK | O_NOCTTY);
char buf[256];
while(rc && fd!=-1)
if((rc=read(fd,buf,sizeof(buf)))<0) perror("read");
else if(rc) cout<<buf<<endl;
close(fd); }
FIFO file API’s
FIFO files are sometimes called named pipes.
Pipes can be used only between related processes when a common
ancestor has created the pipe.
Creating a FIFO is similar to creating a file.
Indeed the pathname for a FIFO exists in the file system.
The prototype of mkfifo is
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
int mkfifo(const char *path_name, mode_t mode);
The first argument pathname is the pathname(filename) of a FIFO file
to be created.
The second argument mode specifies the access permission for user,
group and others and as well as the S_IFIFO flag to indicate that it is a
FIFO file.
On success it returns 0 and on failure it returns –1.
Example
mkfifo(“FIFO5”,S_IFIFO | S_IRWXU | S_IRGRP | S_ROTH);
The above statement creates a FIFO file “divya” with read-write-execute permission
for user and only read permission for group and others.
Once we have created a FIFO using mkfifo, we open it using open.
Indeed, the normal file I/O functions (read, write, unlink etc) all work with FIFOs.
When a process opens a FIFO file for reading, the kernel will block the process until
there is another process that opens the same file for writing.
Similarly whenever a process opens a FIFO file write, the kernel will block the
process until another process opens the same FIFO for reading.
This provides a means for synchronization in order to undergo inter-process
communication.
If a particular process tries to write something to a FIFO file that is full, then that
process will be blocked until another process has read data from the FIFO to make
space for the process to write.
Similarly, if a process attempts to read data from an empty FIFO, the
process will be blocked until another process writes data to the FIFO.
From any of the above condition if the process doesn’t want to get
blocked then we should specify O_NONBLOCK in the open call to the
FIFO file.
If the data is not ready for read/write then open returns –1 instead
of process getting blocked.
If a process writes to a FIFO file that has no other process attached
to it for read, the kernel will send SIGPIPE signal to the process to
notify that it is an illegal operation.
Another method to create FIFO files (not exactly) for inter-process
communication is to use the pipe system call.
The prototype of pipe is
#include <unistd.h>
int pipe(int fds[2]);
Returns 0 on success and –1 on failure.
If the pipe call executes successfully, the process can read from fd[0]
and write to fd[1]. A single process with a pipe is not very useful. Usually
a parent process uses pipes to communicate with its children.
The following test_fifo.C example illustrates the use of mkfifo, open,
read, write and close APIs for a FIFO file:
#include<iostream.h>
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<string.h>
#include<errno.h>
int main(int argc,char* argv[])
{
if(argc!=2 && argc!=3)
{
cout<<"usage:"<<argv[0]<<"<file> [<arg>]";
return 0;
}
int fd; char buf[256];
(void) mkfifo(argv[1], S_IFIFO | S_IRWXU | S_IRWXG | S_IRWXO );
if(argc==2)
{
fd=open(argv[1],O_RDONLY | O_NONBLOCK);
while(read(fd,buf,sizeof(buf))==-1 && errno==EAGAIN) sleep(1);
while(read(fd,buf,sizeof(buf))>0)
cout<<buf<<endl;
}
Else
{
fd=open(argv[1],O_WRONLY);
write(fd,argv[2],strlen(argv[2]));
}
close(fd);
}
Symbolic Link File APIs
A symbolic link is an indirect pointer to a file, unlike the hard links
which pointed directly to the inode of the file.
Symbolic links are developed to get around the limitations of hard
links:
o Symbolic links can link files across file systems.
o Symbolic links can link directory files
o Symbolic links always reference the latest version of the files to which
they link
o There are no file system limitations on a symbolic link and what it
points to and anyone can create a symbolic link to a directory.
o Symbolic links are typically used to move a file or an entire directory
hierarchy to some other location on a system.
o A symbolic link is created with the symlink.
o The prototype is
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
int symlink(const char *org_link, const char *sym_link);
int readlink(const char* sym_link, char* buf,int size);
int lstat(const char * sym_link, struct stat* statv);
/* Regfile.h*/ */
/* Program to illustrate the use of regfile class */
refer from first text book terrence chan
dirfile class for Directory file
The dirfile class is defined to encapsulate all UNIX directory file
functions. The dirfile class defines the create, open, read, tellg, seekg,
close and remove functions that use the UNIX directory file specific API’s.
The dirfile class definition is: /* dirfile.h*/
/* Program to illustrate the use of dirfile class */ refer from first text
book terrence chan
FIFO file Class
A FIFO file object differs from a filebase object in that a FIFO file is
created, and the tellg and seekg functions are invalid for FIFO file
objects. The following pipefile class encapsulates all the FIFO file type
properties: /* pipefile.h */