Unix Mod 3
Unix Mod 3
UNIX Standardization
• ISO C
• In late 1989, ANSI Standard X3.1591989 for the C programming language was approved. This
standard has also been adopted as international standard ISO/IEC 9899:1990.
• ANSI is the American National Standards Institute, the U.S. member in the International
Organization for Standardization (ISO). IEC stands for the International Electrotechnical
Commission.
• The C standard is now maintained and developed by the ISO/IEC international standardization
working group for the C programming language, known as ISO/IEC JTC1/SC22/WG14, or
WG14 for short.
• The intent of the ISO C standard is to provide portability of conforming C programs to a wide
variety of operating systems, not only the UNIX System.
• This standard defines not only the syntax and semantics of the programming language but also
a standard library
• In 1999, the ISO C standard was updated and approved as ISO/IEC 9899:1999, largely to
improve support for applications that perform numerical processing. The changes don't affect
the POSIX standards described in this book, except for the addition of the restrict keyword to
some of the function prototypes. This keyword is used to tell the compiler which pointer
references can be optimized, by indicating that the object to which the pointer refers is accessed
in the function only via that pointer.
• As with most standards, there is a delay between the standard's approval and the modification
of software to conform to it. As each vendor's compilation systems evolve, they add more
support for the latest version of the ISO C standard.
• A summary of the current level of conformance of gcc to the 1999 version of the ISO C standard
is available at http://www.gnu.org/software/gcc/c99status.html.
• The ISO C library can be divided into 24 areas, based on the headers defined by the standard.
Figure 2.1 lists the headers defined by the C standard.
IEEE POSIX
• POSIX is a family of standards developed by the IEEE (Institute of Electrical and Electronics
Engineers). POSIX stands for Portable Operating System Interface. It originally referred only
Dept of CSE, Vemana I.T Page 2 of 41
UNIX System Programming Module-3
to the IEEE Standard 1003.11988 the operating system interface but was later extended to
include many of the standards and draft standards with the 1003 designation, including the shell
and utilities (1003.2).
• This standard defines the services that must be provided by an operating system if it is to be
"POSIX compliant," and has been adopted by most computer vendors. Although the 1003.1
standard is based on the UNIX operating system, the standard is not restricted to UNIX and
UNIX-like systems. Indeed, some vendors supplying proprietary operating systems claim that
these systems have been made POSIX compliant, while still leaving all their proprietary
features in place.
• Because the 1003.1 standard specifies an interface and not an implementation, no distinction is
made between system calls and library functions. All the routines in the standard are called
functions.
IEEE POSIX
• The 2001 version of 1003.1 departed from the prior versions in that it combined several 1003.1
amendments, the 1003.2 standard, and portions of the Single UNIX Specification (SUS),
Version 2 (more on this later). The resulting standard, IEEE Standard 1003.1-2001, includes
the following other standards:
ISO/IEC 9945-1 (IEEE Standard 1003.1-1996), which includes
IEEE Standard 1003.1-1990
IEEE Standard 1003.1b-1993 (real-time extensions)
IEEE Standard 1003.1c-1995 (pthreads)
IEEE Standard 1003.1i-1995 (real-time technical corrigenda)
IEEE P1003.1a draft standard (system interface revision)
IEEE Standard 1003.1d-1999 (advanced real-time extensions)
IEEE Standard 1003.1j-2000 (more advanced real-time extensions)
IEEE Standard 1003.1q-2000 (tracing)
IEEE Standard 1003.2d-1994 (batch extensions)
IEEE P1003.2b draft standard (additional utilities)
Parts of IEEE Standard 1003.1g-2000 (protocol-independent interfaces)
ISO/IEC 9945-2 (IEEE Standard 1003.2-1993)
The Base Specifications of the Single UNIX Specification, version 2, which include
IEEE POSIX
The Base Specifications of the Single UNIX Specification, version 2, which include :
System Interface Definitions, Issue 5
Commands and Utilities, Issue 5
System Interfaces and Headers, Issue 5
Open Group Technical Standard, Networking Services, Issue 5.2
Dept of CSE, Vemana I.T Page 3 of 41
UNIX System Programming Module-3
FIPS
• FIPS stands for Federal Information Processing Standard. It was published by the U.S.
government, which used it for the procurement of computer systems.
• FIPS 1511 (April 1989) was based on the IEEE Std. 1003.11988 and a draft of the ANSI C
standard. This was followed by FIPS 1512 (May 1993), which was based on the IEEE Standard
1003.11990. FIPS 1512 required some features that POSIX.1 listed as optional. All these
options have been included as mandatory in POSIX.1-2001.
• The effect of the POSIX.1 FIPS was to require any vendor that wished to sell POSIX.1-
compliant computer systems to the U.S. government to support some of the optional features
of POSIX.1. The POSIX.1 FIPS has since been withdrawn, so we won't consider it further in
this text.
Dept of CSE, Vemana I.T Page 4 of 41
UNIX System Programming Module-3
4.4BSD
• The Berkeley Software Distribution (BSD) releases were produced and distributed by
the
Computer Systems Research Group (CSRG) at the University of California at Berkeley.
• 4.2BSD was released in 1983 and 4.3BSD in 1986. Both of these releases ran on the VAX
minicomputer. The next release, 4.3BSD Tahoe in 1988, also ran on a particular minicomputer
called the Tahoe. This was followed in 1990 with the 4.3BSD Reno release; 4.3BSD Reno
supported many of the POSIX.1 features.
• The original BSD systems contained proprietary AT&T source code and were covered by
AT&T licenses.
• To obtain the source code to the BSD system you had to have a UNIX source license from
AT&T. This changed as more and more of the AT&T source code was replaced over the years
with non-AT&T source code and as many of the new features added to the Berkeley system
were derived from non-AT&T sources.
• In 1989, Berkeley identified much of the non-AT&T source code in the 4.3BSD Tahoe release
and made it publicly available as the BSD Networking Software, Release 1.0. This was
followed in 1991 with Release 2.0 of the BSD Networking Software, which was derived from
the 4.3BSD Reno release.
• The intent was that most, if not all, of the 4.4BSD system would be free of any AT&T license
restrictions, thus making the source code available to all.
FreeBSD
• FreeBSD is based on the 4.4BSD-Lite operating system. The FreeBSD project was formed to
carry on the BSD line after the Computing Science Research Group at the University of
California at Berkeley decided to end its work on the BSD versions of the UNIX operating
system, and the 386BSD project seemed to be neglected for too long.
• All software produced by the FreeBSD project is freely available in both binary and source
forms. The FreeBSD 5.2.1 operating system was one of the four used to test the examples in
this book.
• Several other BSD-based free operating systems are available. The NetBSD project
(http://www.netbsd.org) is similar to the FreeBSD project, with an emphasis on portability
between hardware platforms. The OpenBSD project (http://www.openbsd.org) is similar to
FreeBSD but with an emphasis on security.
Linux
• Linux is an operating system that provides a rich UNIX programming environment, and is
freely available under the GNU Public License. The popularity of Linux is somewhat of a
phenomenon in the computer industry. Linux is distinguished by often being the first operating
system to support new hardware.
• Linux was created in 1991 by Linus Torvalds as a replacement for MINIX. A grass-roots effort
then sprang up, whereby many developers across the world volunteered their time to use and
enhance it.
• The Mandrake 9.2 distribution of Linux was one of the operating systems used to test the
examples in this book. That distribution uses the 2.4.22 version of the Linux operating system
kernel.
Mac OS X
• Mac OS X is based on entirely different technology than prior versions. The core operating
system is called "Darwin," and is based on a combination of the Mach kernel (Accetta et al.
[1986]) and the FreeBSD operating system. Darwin is managed as an open source project,
similar to FreeBSD and Linux.
• Mac OS X version 10.3 (Darwin 7.4.0) was used as one of the operating systems to test the
examples in this book.
Solaris
• Solaris is the version of the UNIX System developed by Sun Microsystems. It is based on
System V Release 4, with more than ten years of enhancements from the engineers at Sun
Microsystems.
• It is the only commercially successful SVR4 descendant, and is formally certified to be a UNIX
system.
• The Solaris 9 UNIX system was one of the operating systems used to test the examples in this
book.
Files are the building blocks of any operating system. When you execute a command in
UNIX, the UNIX kernel f e t c h e s the corresponding executable file from a file system, loads
its instruction text to memory, and creates a process to execute the command on your behalf. In
the course of execution, a process may read from or write to files. All these operations involve
files. Thus, the design of an operating system always begins with an efficient file management
system.
File Types:
A file in a UNIX or POSIX system may be one of the following types:
regular file
directory file
FIFO file
Character device file
Block device file
Regular file
A regular file may be either a text file or a binary file
These files may be read or written to by users with the appropriate access
permission
Regular files may be created, browsed through and modified by various means such as
text editors or compilers, and they can be removed by specific system commands
Directory file
It is like a folder that contains other files, including sub-directory files.
It provides a means for users to organise their files into some hierarchical
structure based on file relationship or uses.
Ex: /bin directory contains all system executable programs, such as cat, rm, sort
A directory may be created in UNIX by the mkdir command
Ex: mkdir /usr/foo/xyz
A directory may be removed via the rmdir command
Ex: rmdir /usr/foo/xyz
The content of directory may be displayed by the ls command
Device file
A physical device may have both block and character device files representing it for
different access methods.
An application program may perform read and write operations on a device file and the OS
will automatically invoke an appropriate device driver function to perform the actual data
transfer between the physical device and the application
An application program in turn may choose to transfer data by either a character based(via
character device file) or block-based(via block device file).
A device file is created in UNIX via the mknod command
Ex: mknod /dev/cdsk c 115 5
Here , c - character device file
115 - major device number
5 - minor device number
For block device file, use argument ‘b’ instead of ‘c’.
Major device number: an index to a kernel table that contains the addresses of all device driver
functions known to the system. Whenever a process reads data from or writes data to a device file,
the kernel uses the device file’s major number to select and invoke a device driver function to carry
out actual data transfer with a physical device.
Minor device number: an integer value to be passed as an argument to a device driver function when
it is called. It tells the device driver function what actual physical device is talking to and the I/O
buffering scheme to be used for data transfer.
FIFO file
It is a special pipe device file which provides a temporary buffer for two or more processes
to communicate by writing data to and reading data from the buffer.
The size of the buffer is fixed to PIPE_BUF.
Data in the buffer is accessed in a first-in-first-out manner.
The buffer is allocated when the first process opens the FIFO file for read or write
The buffer is discarded when all processes close their references (stream pointers) to the
FIFO file.
Data stored in a FIFO buffer is temporary.
A FIFO file may be created via the mkfifo command.
o The following command creates a FIFO file (if it does not exists)
mkfifo /usr/prog/fifo_pipe
Dept of CSE, Vemana I.T Page 9 of 41
UNIX System Programming Module-3
o The following command creates a FIFO file (if it does not exists)
mknod /usr/prog/fifo_pipe
FIFO files can be removed using rm command.
Symbolic link file
BSD UNIX & SV4 defines a symbolic link file.
A symbolic link file contains a path name which references another file in eitherlocal or a
remote file system.
POSIX.1 does not support symbolic link file type
A symbolic link may be created in UNIX via the ln command
Ex: ln -s /usr/divya/original /usr/raj/slink
It is possible to create a symbolic link to reference another symbolic link. rm, mv and chmod
commands will operate only on the symbolic link arguments directly and not on the files that
they reference.
The UNIX and POSIX File Systems:
Files in UNIX or POSIX systems are stored in tree-like hierarchical file system.
The root of a file system is the root (“/”) directory.
The leaf nodes of a file system tree are either empty directory files or other types of files.
Absolute path name of a file consists of the names of all the directories, starting from the root.
Ex: /usr/divya/a.out
Relative path name may consist of the “.” and “..” characters. These are references to current
and parent directories respectively.
Ex: ../../.login
denotes .login file which may be found 2 levels up from the current directory
A file name may not exceed NAME_MAX characters (14 bytes) and the total number of
characters of a path name may not exceed PATH_MAX (1024 bytes).
POSIX.1 defines _POSIX_NAME_MAX and _POSIX_PATH_MAX in <limits.h>header
File name can be any of the following character set only.
A to Z a to z 0 to 9
Path name of a file is called the hardlink.
A file may be referenced by more than one path name if a user creates one or more hard links
to the file using ln command.
ln /usr/foo/path1 /usr/prog/new/n1
If the –s option is used, then it is a symbolic (soft) link
Dept of CSE, Vemana I.T Page 10 of 41
UNIX System Programming Module-3
In addition to the above attributes, UNIX systems also store the major and minor device
numbers for each device file. All the above attributes are assigned by the kernel to a file
when it is created. The attributes that are constant for any file are:
File type
File inode number
File system ID
Major and minor device number
The other attributes are changed by the following UNIX commands or system calls
open
• This is used to establish a connection between a process and a file i.e. it is used to open an
existing file for data transfer function or else it may be also be used to create a new file.
• The returned value of the open system call is the file descriptor (row number of the file
table), which contains the inode information.
• The prototype of open function is
#include<sys/types.h>
#include<sys/fcntl.h>
int open(const char *pathname, int accessmode, mode_t permission);
open
If successful, open returns a nonnegative integer representing the open file descriptor.If
unsuccessful, open() returns –1.
The first argument is the name of the file to be created or opened.
This may be an absolute pathname or relative pathname.
If the given pathname is symbolic link, the open function
will resolve the symbolic link reference to a non-symbolic link file to which it
refers.
The second argument is access modes, which is an integer value that specifies
how actually the file should be accessed by the calling process.
Generally the access modes are specified in <fcntl.h>. Various access modes are:
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.
3 O_EXCL Generate an error if O_CREAT is also specified and the file already
exists.
4 O_TRUNC If file exists discard the file content and set the file size to zero bytes.
• To illustrate the use of the above flags, the following example statement opens a file called
/usr/syed/usp for read and write in append mode:
int fd=open(“/home/UNIX/file1.c”, O_RDWR | O_APPEND|O_CREAT,755);
• 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.
To open "sample.txt" in the current working directory for appending or create it, if it
does not exist, with read, write and execute permissions for owner only:
fd = open(“sample.txt", O_WRONLY|O_APPEND|O_CREAT, S_IRWXU); fd = open(“sample.txt",
O_WRONLY|O_APPEND|O_CREAT, 0700);
fd = open(“sample.txt", O_WRONLY|O_CREAT|O_EXCL,
S_IRWXU|S_IROTH|S_IWOTH);
fd = open(“sample.txt", O_WRONLY|O_CREAT|O_EXCL,0706);
fd = open(“sample.txt", O_WRONLY|O_CREAT|O_TRUNC,0706);
creat ( )
• This system call is used to create new regular files.
#include <sys/types.h> #include<unistd.h>
int creat(const char *pathname, mode_t mode);
• Returns: file descriptor opened for write-only if OK, -1 on error.
• The first argument pathname specifies name of the file to be created.
• The second argument mode_t, specifies permission of a file to be accessed by owner group
and others.
• The creat function can be implemented using open function as:
#define creat(path_name, mode)
Dept of CSE, Vemana I.T Page 14 of 41
UNIX System Programming Module-3
read ( )
The read function fetches a fixed size of block of data from a file referenced by a given file
descriptor.
The prototype of read function is:
#include<sys/types.h> #include<unistd.h>
size_t read(int fdesc, void *buf, size_t nbyte);
If successful, read returns the no. of bytes actually read, on error it returns –1.
The first argument is an integer, fdesc that refers to an opened file.
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.
The size_t data type is defined in the <sys/types.h> header and should be the
same as unsigned int.
There are several cases in which the number of bytes actually read is less than the amount requested:
When readingfrom 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).
When readingfrom a terminal device. Normally, up to one
line is read at a time.
When reading from a network. Buffering within the network may cause less than the
requested amount to be returned.
When readingfrom a pipe or FIFO. If the pipe contains
fewer bytes than requested, read will return only what is available.
read ( )- Errors
Tag Description
EINTR The call was interrupted by a signal before any data was read.
EINVAL fd is attached to an object which is unsuitable for reading; or the file was opened
with the O_DIRECT flag, and either the address specified in buf, the value specified
in count, or the current file offset is not suitably aligned.
EIO I/O error. This will happen for example when the process is in a background
process group, tries to read from its controlling tty, and either it is ignoring or
blocking SIGTTIN or its process group is orphaned. It may also occur when there
is a low-level I/O error while reading from a disk or tape.
write( )
The write system call is used to write data into a file
The write function puts data to a file in the form of fixed block size referred by a given file.
The prototype of read function is:
#include<sys/types.h> #include<unistd.h>
ssize_t write(int fdesc, const void *buf, size_t size);
If successful, write returns the number of bytes actually written.
If unsuccessful, write returns –1.
The first argument, fdesc is an integer that refers to an opened file.
Dept of CSE, Vemana I.T Page 16 of 41
UNIX System Programming Module-3
The second argument, buf is the address of a buffer that contains data to be written.
The third argument, size specifies how many bytes of data are in the buf argument.
The return value is usually equal to the number of bytes of data successfully written to a
file. (size value)
write( )-Errors
Error Code Description
EAGAIN Non-blocking I/O has been selected using O_NONBLOCK and the write would block.
EBADF fd is not a valid file descriptor or is not open for writing.
EFBIG An attempt was made to write a file that exceeds the implementation-defined
maximum file size or the process’ file size limit, or to write at a position past the
maximum allowed offset.
EINTR The call was interrupted by a signal before any data was written.
EINVAL fd is attached to an object which is unsuitable for writing; or the file was opened with
the O_DIRECT flag, and either the address specified in buf, the value
specified n count, or the current file offset is not suitably aligned.
EIO A low-level I/O error occurred while modifying the inode.
EPIPE fd is connected to a pipe or socket whose reading end is closed. When this happens the
writing process will also receive a SIGPIPE signal. (Thus, the write return value is seen
only if the program catches, blocks or ignores this signal.)
#include<fcntl.h>
int main()
{
int fd,nob;char c[]=“ This is sample text”; fd=open(“sample”,O_WRONLY,|O_CREAT,0777);
nob= write(fd,c,strlen(c));
if(nob!=-1)
printf(“Successfully written to file”);
else
perror(“write Error”);
close(fd);
return 0;
}
close( )
The close system call is used to terminate the connection to a file from a process.
The prototype of the close() is
#include<unistd.h> int close(int fdesc);
If successful, close returns 0.
If unsuccessful, close returns –1.
close( ) – Errors
Tag Description
fcntl:
The fcntl function helps to query or set access control flags and the close-on-exec flag of any file
descriptor. Users can also use fcntl to assign multiple file descriptors to reference the same file.
Its prototype is:
#include <fcntl.h>
int fcntl (int fdesc ,int cmd, ….);
fdesc: is an integer file descriptor that refers to an opened file.
cmd: specifies which operation to perform on a file referenced by the fdesc argument.
The third argument value, which may be specified after cmd is dependent on the actual cmd
value.
The possible cmd values are defined in the <fcntl.h> header. These values and their uses are:
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 nonblocking and in write-append mode, it can call fcntl on the file’s
descriptor as:
int cur_flags = fcntl(fdesc, FGETFL);
Dept of CSE, Vemana I.T Page 19 of 41
UNIX System Programming Module-3
The close-on-exec flag of a file descriptor specifies that if the process that owns the descriptor
calls the exec API to execute different program, the fdesc should be closed by the kernel
before the new program runs or not.
The example reports the close-on-exec flag of a fdesc, sets it to on afterwards:
cout<<fdesc<<”close-on-exec:”<<fcntl(fdesc,F_GETFD)<<endl;
(void)fcntl(fdesc, F_SETFD, 1);
The fcntl function can also be used to duplicate a fdesc with another fdesc. The results are
two fdesc reference the same file with same access mode and share the same file pointer to
read or write the file. This is useful in the redirection of standard input or output to reference
a file.
Example: Reference standard input of a process to a file called FOO
int fdesc = open(“FOO”, O_RDONLY); //open FOO for read
close(0); //close standard input
if(fcntl(fdesc, F_DUPFD, 0)==-1) perror(“fcntl”); //stdin from FOO
char buf[256];
int rc = read(0,buf,256); //read data from FOO
The dup and dup2 functions in UNIX perform the same file duplication function as fcntl.
They can be implemented using fcntl as:
#define dup(fdesc) fcntl(fdesc, F_FUPFD,0)
#define dup2(fdesc1,fd2) close(fd2),fcntl(fdesc, F_DUPFD, fd2)
The dup function duplicates a fdesc with the lowest unused fdesc of a calling process.
The dup2 function will duplicate a fdesc using a fd2 fdesc, regardless of whether fd2 is used
to reference another file.
Directory File API’s:
A Directory file is a record-oriented file, where each record stores a file name and the inode
number of a file that resides in that directory.
Directories are created with the mkdir API and deleted with the rmdir API.
#include<unistd.h>
The second argument mode, specifies the access permission for the owner, groups and others to be
assigned to the file. This function creates a new empty directory.
The specified file access permission, mode, are modified by the file mode creation mask of the
process.
To allow a process to scan directories in a file system independent manner, a directory record is
defined as struct dirent in the <dirent.h> header for UNIX.
Some of the functions that are defined for directory file operations in the above header are
#include<sys/types.h>
#if defined (BSD) && ! _POSIX_SOURCE
#include<sys/dir.h>
typedef struct direct Dirent;
#else
#include<dirent.h>
typedef struct dirent Dirent;
#endif
DIR *opendir(const char *path_name);
Dirent *readdir(DIR *dir_fdesc);
int closedir(DIR *dir_fdesc);
void rewinddir(DIR *dir_fdsec);
The uses of these functions are
Function Use
Opens a directory file for read-only. Returns a file handle dir * for future
opendir
reference of the file.
Resets the file pointer to the beginning of the directory file referenced by
rewinddir
dir- fdesc. The next call to readdir will read the first record from the file.
UNIX systems have defined additional functions for random access of directory file records.
The following list_dir.C program illustrates the uses of the mkdir, opendir, readdir, closedir
and rmdir APIs:
#include<sys/dir.h> {
#include<dirent.h> }
The following list_dir.C program illustrates the uses of the mkdir, opendir, readdir, closedir
and rmdir APIs:
Dept of CSE, Vemana I.T Page 22 of 41
UNIX System Programming Module-3
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(“SCS15”,S_IFBLK | S_IRWXU | S_IRWXG | S_IRWXO,(15<<8) | 3);
The above function creates a block device file “SCS15”, 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.
Dept of CSE, Vemana I.T Page 23 of 41
UNIX System Programming Module-3
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];
(void) mknod(argv[1], S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, (major<<8) | minor);
int rc=1,
Int 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);
}
#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 “fifo5” 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.
Dept of CSE, Vemana I.T Page 25 of 41
UNIX System Programming Module-3
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.
FIFO file API’s
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)
{
Dept of CSE, Vemana I.T Page 26 of 41
UNIX System Programming Module-3
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);
}
1) With a neat diagram, explain how a C – program is initiated and various ways it can be
terminated.
Figure summarizes how a C program is started and the various ways it can terminate.
PROCESS TERMINATION:
There are eight ways for a process to terminate.
1. Normal termination occurs in five ways:
Return from main
Calling exit
Calling _exit or _Exit
Return of the last thread from its start routine
Calling pthread_exit from the last thread
2. Abnormal termination occurs in three ways:
Calling abort
Receipt of a signal
Response of the last thread to a cancellation request
Three functions terminate a program normally: _exit and _Exit, which return to the kernel
immediately, and exit, which performs certain cleanup processing and then returns to the
kernel. Thus, the only way a program is executed by the kernel is when one of the exec
functions is called. The only way a process voluntarily terminates is when _exit or _Exit is
called, either explicitly or implicitly (by calling exit). A process can also be involuntarily
terminated by a signal (not shown in Figure).
2) What are the different ways for a process to terminate? Explain exit,_exit, atexit functions
with its prototypes.
ANS: Prototypes: #include <stdlib.h>
void exit(int status);
void _Exit(int status);
#include <unistd.h>
void _exit(int status)
The reason for the different headers is that exit and _Exit are specified by ISO C, whereas _exit
is specified by POSIX.1.
a) exit:
exit function terminates the process normally, performs certain cleanup
processing and then returns to the kernel.
the exit function has always performed a clean shutdown of the standard I/O
library: the fclose function is called for all open streams. This causes all
buffered output data to be flushed (written to the file).
Dept of CSE, Vemana I.T Page 29 of 41
UNIX System Programming Module-3
All three exit functions expect a single integer argument, called the exit status.
The exit status of the process is undefined if
Any of these functions is called without an exit status,
main does a return without a return value, or
The main function is not declared to return an integer.
Returning an integer value from the main function is equivalent to calling exit with the same
value. Thus exit(0); is the same as return(0); from the main function.
c) atexit Function:
With ISO C, a process can register up to 32 functions that are automatically
called by exit. These are called exit handlers and are registered by calling the
atexit function.
Prototype:
#include <stdlib.h>
int atexit(void (*func)(void));
Returns: 0 if OK, nonzero on error
This declaration says that the address of a function is passed as an argument
toatexit. When this function is called, it is not passed any arguments and is
notexpected to return a value. The exit function calls these functions in reverse
order of their registration. Each function is called as many times as it was
registered.
The following example shows how a process registers for a function using atexit
#include<stdio.h>
#include<stdlib.h>
void exit_handler1(void)
{
printf(“\n first exit handler “);
}
void exit_handler2(void)
{
printf(“\n second exit hadler”);
}
int main( )
{
atexit(exit_handler1);
atexit(exit_handler1);
atexit(exit_handler2);
printf(“IN THE MAIN PROGRAMM”);
return 0;
}
Output :
$ cc atexit.c
$ ./a.out
IN THE MAIN PROGRAMM
second exit handler
first exit handler
first exit handler
With ISO C and POSIX.1, exit first calls the exit handlers and then closes (via fclose) all open streams.
#include<unistd.h>
#include<stdio.h>
Output :
$ cc echocmd.c
$ ./a.out unix system programming
argv[0]: ./a.out
argv[1]: unix
argv[2]: system
argv[3]: programming
a) Text segment,
consists of the machine instructions that the CPU executes. Usually, the text segment is sharable
so that only a single copy needs to be in memory for frequently executed programs, such as text
editors, the C compiler, the shells, and so on. Also, the text segment is often read-only, to prevent
a program from accidentally modifying its instructions.
d) Stack,
where (local variables )automatic variables are stored, along with information that is saved
each time a function is called. Each time a function is called, the address of where to return to and
certain information about the caller's environment, such as some of the machine registers, are
saved on the stack. The newly called function then allocates room on the stack for its automatic
and temporary variables.
e) Heap,
where dynamic memory allocation usually takes place. Historically, the heap has been located
between the uninitialized data and the stack.
5) Write an explanatory note on environment variables. Also write a C/C++ program that
outputs the contents of its environment list.
ANS:
The environment list is an array of character pointers, with each pointer containing the
address of a null-terminated C string called environment variable. The address of the
array of pointers is contained in the global variable environ:
extern char **environ;
The environment strings are usually of the form:
name=value
The shells use numerous environment variables. Some, such as HOME and USER, are
set automatically at login, and others are for us to set. We normally set environment
variables in a shell start-up file to control the shell’s actions.
The function that is used to fetch the value from the variables is getenv,
The prototype of getenv function:
#include <stdlib.h>
char *getenv(const char *name);
Returns: pointer to value associated with name, NULL if not found.
this function returns a pointer to the value of a name=value string. We should always use
getenv to fetch a specific value from the environment, instead of accessing environ directly.
The functions that are used to set an environment variable are putenv and setenv.
These functions are used to change the value of an existing variable or add a new
variable to the environment.
The prototypes of these functions:
#include <stdlib.h>
int putenv(char *str);
int setenv(const char *name, const char *value, int rewrite);
All return: 0 if OK, nonzero on error.
The putenv function takes a string of the form name=value and places it in the environment
list. If name already exists, its old definition is first removed.
The setenv function sets name to value. If name already exists in the environment, then
(a) if rewrite is nonzero, the existing definition for name is first removed;
(b) if rewrite is 0, an existing definition for name is not removed, name is not
set to the new value, and no error occurs.
The function that is used to remove any definition of name is unsetenv,
The prototype of unsetenv function:
#include <stdlib.h>
int unsetenv(const char *name);
Rreturns: 0 if OK, nonzero on error
The unsetenv function removes any definition of name. It is not an error if such a definition
does not exist
exit(0);
}
a) malloc, which allocates a specified number of bytes of memory. The initial value of the
memory is indeterminate(garbage value).
b) calloc, which allocates space for a specified number of objects(n) of a specified size(s).
([n*s] bytes). The space is initialized to all 0 bits.
c) realloc, which increases or decreases the size of a previously allocated area. When the
size increases, it may involve moving the previously allocated area somewhere else, to
provide the additional room at the end. Also, when the size increases, the initial value of
the space between the old contents and the end of the new area is indeterminate.
For example, if we allocate room for 512 elements in an array that we
fill in at runtime but find that we need room for more than 512 elements,
we can call realloc.
If there is room beyond the end of the existing region for the requested
space, then realloc doesn’t have to move anything; it simply allocates the
additional area at the end and returns the same pointer that we passed it.
But if there isn’t room at the end of the existing region, realloc allocates
another area that is large enough, copies the existing 512-element array
to the new area, frees the old area, and returns the pointer to the new area.
PROTOTYPES
Dept of CSE, Vemana I.T Page 35 of 41
UNIX System Programming Module-3
#include <stdlib.h>
void *malloc(size_t size);
void *calloc(size_t nobj, size_t size);
void *realloc(void *ptr, size_t newsize);
All three return: non-null pointer if OK, NULL on error
The pointer returned by the three allocation functions is guaranteed to be suitably aligned so
that it can be used for any data object.
The allocation routines are usually implemented with the sbrk system call. This system call
expands (or contracts) the heap of the process.
The function free causes the space pointed to by ptr to be deallocated. This freed space is usually
put into a pool of available memory and can be allocated in a later call to one of the three
alloc functions.
d) alloca Function: The function alloca has the same calling sequence as malloc; however,
instead of allocating memory from the heap, the memory is allocated from the stack frame
of the current function. The advantage is that we don't have to free the space; it goes
away automatically when the function returns. The alloca function increases the size of
the stack frame. The disadvantage is that some systems can't support alloca, if it's
impossible to increase the size of the stack frame after the function has been called.
DISADVANTAGES
If a process calls malloc, but forgets to call free, its memory usage continually
increases; this is called leakage. By not calling free to return unused space, the
size of a process's address space slowly increases until no free space is left. During
this time, performance can degrade from excess paging overhead.
We call setjmp from the location that we want to return to, which in this example is in the
main function. In this case, setjmp returns 0 because we called it directly.
In the call to setjmp, the argument env is of the special type jmp_buf. This data type is
some form of array that is capable of holding all the information required to restore the status
of the stack to the state when we call longjmp.
Normally, the env variable is a global variable, since we'll need to reference it from another
function.
When we encounter an error say, in the add function, we call longjmp with two arguments.
The first is the same env that we used in a call to setjmp, and the second, val, is a nonzero
value that becomes the return value from setjmp. The reason for the second argument is to
allow us to have more than one longjmp for each setjmp.
Program 1:
#include "apue.h"
#include <setjmp.h>
jmp_buf env;
int main(void)
{
int i;
if ((i=setjmp(env)) != 0)
printf("error");
add(a,b);
exit(0);
}
sum=a+b;
if(sum<0)
longjmp(env,1)
}
When main is executed, we call setjmp, which records whatever information it needs to in the
variable env and returns 0. We then call add and assume that an error of some form is detected.
longjmp causes the stack to be "unwound" back to the main function, throwing away the stack
frames add .Calling longjmp causes the setjmp in main to return, but this time it returns with a
value of 1 (the second argument for longjmp).
PROGRAMM 2:
#include<stdio.h>
#include<setjmp.h>
jmp_buf env;
void func();
int main(void)
{
int i;
printf("THIS IS 1 MAIN \n");
if ((i=setjmp(env)) == 0)
{
printf(“ The value of i=%d\n”,i);
fun();
printf("THIS IS 2 MAIN \n");
}
printf("THIS IS 3 MAIN \n");
printf(“ The value of i=%d\n”,i);
return(0);
}
Void fun()
{
printf("THIS IS FUNC \n");
longjmp(env,2)
}
OUTPUT:
$cc 1.c
$./a.out
THIS IS 1 MAIN
The value of i=0
THIS IS FUNC
THIS IS 3 MAIN
The value of i=2
8) Explain getrlimit and setrlimit functions with prototype. Mention the 3 rules to change
the resource limits. Give four resource values.
ANS:
Every process has a set of resource limits, some of which can be queried
and changed by the getrlimit and setrlimit functions.
struct rlimit {
rlim_t rlim_cur; /* soft limit: current limit */
rlim_t rlim_max; }; /* hard limit: maximum value for rlim_cur */
#include <stdio.h>
#include <sys/resource.h>
#include <sys/time.h>
int main(void)
{
if (limit.rlim_cur == RLIM_INFINITY)
printf("(infinite) ");
else
printf(“%10ld”, limit.rlim_cur);
if (limit.rlim_max == RLIM_INFINITY)
printf("(infinite)");
else
printf((“%10ld”, limit.rlim_max);
return 0;
}
Output:
$./a.out
RLIMIT_CPU (infinite) (infinite)