0% found this document useful (0 votes)
93 views

Unix File API's: Chapter 7 & 8-Terrance Chan Chapter 7 & 8 - Richard Stevens

The document discusses various Unix file APIs. It describes functions like open(), read(), write(), close(), lseek(), link(), unlink(), stat(), fstat(), fcntl(), access() and umask(). It explains what each function does, its prototype, parameters and usage. The document also includes a sample C program to emulate the 'ls -l' command and display file attributes using stat().

Uploaded by

Shreya ms
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
93 views

Unix File API's: Chapter 7 & 8-Terrance Chan Chapter 7 & 8 - Richard Stevens

The document discusses various Unix file APIs. It describes functions like open(), read(), write(), close(), lseek(), link(), unlink(), stat(), fstat(), fcntl(), access() and umask(). It explains what each function does, its prototype, parameters and usage. The document also includes a sample C program to emulate the 'ls -l' command and display file attributes using stat().

Uploaded by

Shreya ms
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 169

Unix File API’s

Chapter 7 & 8- Terrance Chan


Chapter 7 & 8- Richard Stevens
General file APIs
 Open Opens a file for data access
 Read Reads data from file
 Write Writes data into file
 Lseek Allows random access of
data in a file
 Close Terminates connection to a
file
 Stat,fstat Queries attributes of a
file
 Chmod Changes access
permissions of a file
 Chown Changes UID and/or GID of a
file
 Utime Changes last modification
time and access time
stamps of a file
 Link creates a hard link to a file
 Ulink Deletes a hard link to a file
 Umask Sets default file creation
mask
Open
 The function establishes connection
between process and a file
 The prototype of the function

#include <sys/types.h>
#include <fcntl.h>
int open (const char *pathname, int access
mode , mode_t permission);
 Pathname : It can be absolute path
name or a relative path
name
 Access_mode : An integer which
specifies how file is to be
accessed by calling
process
 Access mode flag Use

 O_RDONLY Opens file for read-


only
 O_WRONLY Opens file for write
only
 O_RDWR Opens file for read
& write
 Access modifier flag

 O_APPEND
 O_CREAT
 O_EXCL
 O_TRUNC
 O_NONBLOCK
 O_NOCTTY
 O_APPEND : appends data to end of file
 O_TRUNC : if the file already exists,
discards its contents and
sets file size to zero
 O_CREAT : creates the file if it does not
exist
 O_EXCL : used with O_CREAT only.
This flag causes open to
fail if the file exists
 O_NONBLOCK : specifies that any
subsequent read or write
on the file should be non
blocking

 O_NOCTTY : specifies not to use the


named terminal device
file as the calling process
control terminal
Umask
 It specifies some access rights to be masked
off
 Prototype:

mode_t umask ( mode_t new_umask);


mode_t old_mask =
umask (S_IXGRP|S_IWOTH);
/*removes execute permission from others and
write permission from group*/
 Actual_permission =

permission & ~umask_value


Creat

 It is used to create new regular files

#include <sys/types.h>
#include <unistd.h>
Int creat (const char* pathname,mode_t mode)‫‏‬
Read

 This function fetches a fixed size block of


data from a file referenced by a given file
descriptor

#include <sys/types.h>
#include <unistd.h>
ssize_t read (int fdesc ,void* buf, size_t size);
Write

 The write function puts a fixed size block


of data to a file referenced by a file
descriptor

#include <sys/types.h>
#include <unistd.h>
ssize_t read (int fdesc ,void* buf, size_t size);
Close

 Disconnects a file from a process

#include <unistd.h>
int close (int fdesc);

 Close function will allocate system resources


 It a process terminates without closing all the
files it has opened ,the kernel will close those
files for the process
fcntl

 The function helps to query or set access


control flags and the close-on-exec flag of
any file descriptor
#include <fcntl.h>
Int fcntl (int fdesc ,int cmd);

 cmd argument specifies which operation to


perform on a file referenced by the fdesc
argument
 cmd value
 F_GETFL : returns the access control flags of
a file descriptor fdesc
 F_SETFL : sets or clears control flags that
are specified
 F_GETFD : returns the close-on-exec flag of
a file referenced by fdesc
 F_SETFD : sets or clears close-on-exec flag
of a file descriptor fdesc
 F_DUPFD : duplicates the file descriptor
fdesc with another file descriptor
lseek

 the lseek system call is used to change the


file offset to a different value
 Prototype :

#include <sys/types.h>
#include <unistd.h>
Off_t sleek (int fdesc , off_t pos, int whence)‫‏‬
 Pos :
 specifies a byte offset to be added to a
reference location in deriving the new file
offset value
 Whence location reference
 SEEK_CUR current file pointer
address
 SEEK_SET the beginning of a
file
 SEEK_END the end of a file
link

 The link function creates a new link for


existing file
 Prototype :

#include <unistd.h>
int link (const char* cur_link ,const char*
new_link)
unlink

 Deletes link of an existing file

#include <unistd.h>
int unlink (const char* cur_link );

 Cannot link a directory unless the


calling function has the superuser
privilege
Stat fstat

 Stat and fstat retrieve arguments of a


given file

#include <sys/types.h>
#include <unistd.h>
int stat (const char* path_name,struct
stat* statv)‫‏‬
int fstat (const int fdesc,struct stat* statv)‫‏‬
 Struct stat
{
dev_ts t_dev;
ino_t st_ino;
mode_t st_mode;
nlink_t st_nlink;
uid_t st_uid;
gid_t st_gid;
dev_t st_rdev;
off_t st_size;
time_t st_mtime
time_t st_ctime
};
 If pathname specified in stast is a
symbolic link then the attributes of the
non-symbolic file is obtained
 To avoid this lstat system call is used
 It is used to obtain attribites of the
symbolic link file

int lstat (const char* path_name , struct stat*


statv);
/* Program to emulate the UNIX ls -l
command */
#include <iostream.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
static char xtbl[10] = "rwxrwxrwx";
#ifndef MAJOR
#define MINOR_BITS 8
#define MAJOR(dev) ((unsigned)dev >>
MINOR_BITS)‫‏‬
#define MINOR(dev) ( dev &
MINOR_BITS)‫‏‬
#endif

/* Show file type at column 1 of an output


line */
static void display_file_type ( ostream& ofs, int
st_mode )

{
switch (st_mode &S_IFMT)
{
case S_IFDIR: ofs << 'd'; return;
/* directory file */
case S_IFCHR: ofs << 'c'; return;
/* character device file */
case S_IFBLK: ofs << 'b'; return;
/* block device file */
case S_IFREG: ofs << ' '; return;
/* regular file */
case S_IFLNK: ofs << 'l'; return;
/* symbolic link file */
case S_IFIFO: ofs << 'p'; return;
/* FIFO file */
}
}
/* Show access permission for owner, group,
others, and any special flags */
static void display_access_perm ( ostream&
ofs, int st_mode )
{
char amode[10];
for (int i=0, j= (1 << 8); i < 9; i++, j>>=1)‫‏‬
amode[i] = (st_mode&j) ? xtbl[i] : '-
';
/* set access permission */
/* set access permission */
if (st_mode&S_ISUID)‫‏‬
amode[2] = (amode[2]=='x') ? 'S' : 's';
if (st_mode&S_ISGID)‫‏‬
amode[5] = (amode[5]=='x') ? 'G' : 'g';
if (st_mode&S_ISVTX)‫‏‬
amode[8] = (amode[8]=='x') ? 'T' :
't';
ofs << amode << ' ';
}
/* List attributes of one file */
static void long_list (ostream& ofs, char*
path_name)
{
struct stat statv;
struct group *gr_p;
struct passwd *pw_p;
if (stat (path_name, &statv))
{
perror( path_name );
return;
}
display_file_type( ofs, statv.st_mode );
display_access_perm( ofs, statv.st_mode );
ofs <<
statv.st_nlink;
/* display hard link count */
gr_p =
getgrgid(statv.st_gid);
/* convert GID to group name */
pw_p =
getpwuid(statv.st_uid);
/*convert UID to user name */

ofs << ' ' << pw_p->pw_name << ' ' <<
gr_p->gr_name << ' ';
if ((statv.st_mode&S_IFMT) == S_IFCHR ||
(statv.st_mode&S_IFMT)==S_IFBLK)‫‏‬
ofs << MAJOR(statv.st_rdev) << ','
<< MINOR(statv.st_rdev);
else ofs << statv.st_size;
/* show file size or major/minor no. */
ofs << ' ' << ctime (&statv.st_mtime);
/* print last modification time */
ofs << ' ' << path_name <<
endl; /* show file name */
}
/* Main loop to display file attributes one file
at a time */
int main (int argc, char* argv[])
{
if (argc==1)
cerr << "usage: " << argv[0] << " <file
path name> ...\n";
else while (--argc >= 1) long_list( cout,
*++argv);
return 0;
}
Access

 The access function checks the existence


and/or access permission of user to a
named file

#include <unistd.h>
int access (const char* path_name, int flag);
 The flag contains one or more bit flags
 Bit flags USE

 F_OK checks whether the file exists


 R_OK checks whether calling
process has read permission
 W_OK checks whether calling
process has write permission
 X_OK checks whether calling
process has read permission
Chmod fcmod

 The chmod and fcmod functions change


file access permissions for owner, group
and others and also set-UID ,set-GID and
sticky bits
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int chmod (const char* path_name, mode_t flag);
int fchmod (int fdsec, mode_t flag);
 Flag argument contains new access
permissions and any special flags to be
set on the file

 Flag value can be specified as an octal


integer value in UNIX, or constructed from
the manifested constants defined in
<sys/stat.h>
Chown fchown lchown
 The chown and fchown function change
the user ID and group ID of files
 lchown changes the ownership of
symbolic link 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);
utime
• The function modifies the access and
modification time stamps of a file

#include <unistd.h>
#include <sys/types.h>
#include <utime.h>
int utime (const char* path_name,
struct utimbuf* times);
 Struct utimbuf
{
time_t actime;
time_t modtime;
};
FILE AND RECORD LOCKING
 UNIX systems allow multiple processes to
read and write the same file concurrently.
 It is a means of data sharing among
processes.
 Why the need to lock files?
It is needed in some applications like
database access where no other process
can write or read a file while a process is
accessing a file-locking mechanism.
 File locking is applicable only to regular
files
Shared and exclusive locks
 A read lock is also called a shared lock and a
write lock is called an exclusive lock.
 These locks can be imposed on the whole
file or a portion of it.
 A write lock prevents other processes from
setting any overlapping read or write locks
on the locked regions of the file. The
intention is to prevent other processes from
both reading and writing the locked region
while a process is modifying the region.
 A read lock allows processes to set
overlapping read locks but not write
locks. Other processes are allowed to
lock and read data from the locked
regions.
 A mandatory locks can cause problems:
If a runaway process sets a mandatory
exclusive lock on a file and never unlocks
it, no other processes can access the
locked region of the file until either a
runaway process is killed or the system is
rebooted.
 If a file lock is not mandatory it is
advisory. An advisory lock is not enforced
by a kernel at the system call level
 The following procedure is to be followed
 Try to set a lock at the region to be
accessed. if this fails, a process can
either wait for the lock request to become
successful or go do something else and
try to lock the file again.
 After a lock is acquired successfully, read
or write the locked region
 Release the lock
Advisory locks
 A process should always release any lock
that it imposes on a file as soon as it is
done.
 An advisory lock is considered safe, as no
runaway processes can lock up any file
forcefully. It can read or write after a fixed
number of failed attempts to lock the file
 Drawback: the programs which create
processes to share files must follow the
above locking procedure to be
cooperative.
FCNTL file locking

 int‫‏‬fcntl‫(‏‬int‫‏‬fdesc,‫‏‬int‫‏‬cmd_flag,‫;)…‏‬
Cmd_flag Use
F_SETLK Sets a file lock. Do not block if this
cannot succeed immediately.
F_SETLKW Sets a file lock and blocks the
calling process until the lock is
acquired.
F_GETLK Queries as to which process locked
a specified region of a file.
For file locking the third argument is struct
flock-typed variable.

struct flock
{
short l_type;
short l_whence;
off_t l_start;
off_t l_len;
pid_t l_pid;
};
l_type and l_whence fields of flock

l_type value Use

F_RDLCK Sets as a read (shared) lock


on a specified region
F_WRLCK Sets a write (exclusive) lock
on a specified region
F_UNLCK Unlocks a specified region
l_whence value Use

SEEK_CUR The l_start value is


added to the current file
pointer address
SEEK_SET The l_start value is
added to byte 0 of file
SEEK_END The l_start value is
added to the end
(current size) of the file
 The l_len specifies the size of a locked
region beginning from the start address
defined by l_whence and l_start. If l_len is
0 then the length of the locked is imposed
on the maximum size and also as it
extends. It cannot have a –ve value.

 When fcntl is called, the variable contains


the region of the file locked and the ID of
the process that owns the locked region.
This is returned via the l_pid field of the
variable.
LOCK PROMOTION AND SPLITTING
 If a process sets a read lock and then
a write lock the read lock is now
covered by a write lock. This process
is called lock promotion.
 If a process unlocks any region in
between the region where the lock
existed then that lock is split into two
locks over the two remaining regions.
Mandatory locks can be achieved by setting
the following attributes of a file.
 Turn on the set-GID flag of the file.

 Turn off the group execute right of the file.

All file locks set by a process will be


unlocked terminates.
If a process locks a file and then creates a
child process via fork, the child process
will not inherit the lock.
The return value of fcntl is 0 if it succeeds
or -1 if it fails.
#include <iostream.h>
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
int main (int argc, char* argv[])
{
struct flock fvar;
int fdesc;
while (--argc > 0) { /* do the
following for each file */
if ((fdesc=open(*++argv,O_RDWR))==-1)
{
perror("open"); continue;
}
fvar.l_type = F_WRLCK;
fvar.l_whence = SEEK_SET;
fvar.l_start = 0;
fvar.l_len = 0;
/* Attempt to set an exclusive (write) lock on
the entire file */
while (fcntl(fdesc, F_SETLK,&fvar)==-1)‫‏‬
{
/* Set lock fails, find out who has locked the file
*/
while (fcntl(fdesc,F_GETLK,&fvar)!=-1 &&
fvar.l_type!=F_UNLCK)‫‏‬
{
cout << *argv << " locked by " << fvar.l_pid<<
" from " << fvar.l_start << " for "<<
fvar.l_len << " byte for " <<
(fvar.l_type==F_WRLCK ? 'w' : 'r')
<< endl;
if (!fvar.l_len) break;
fvar.l_start += fvar.l_len;
fvar.l_len = 0;
} /* while there are locks set by other
processes */
} /* while set lock un-successful */
// Lock the file OK. Now process data in the file
/* Now unlock the entire file */
fvar.l_type = F_UNLCK;
fvar.l_whence = SEEK_SET;
fvar.l_start = 0;
fvar.l_len = 0;
if (fcntl(fdesc, F_SETLKW,&fvar)==-1)
perror("fcntl");
}
return 0;
} /* main */
Directory File APIs

 Why do need directory files? Uses?


 To aid users in organizing their files
into some structure based on the
specific use of files
 They are also used by the operating
system to convert file path names to
their inode numbers
 To create
int mkdir (const char* path_name , mode_t
mode);

 The mode argument specifies the access


permission for the ownerv,group, and
others to be assigned to the file.
Difference between mkdir and mknod

 Directory created by mknod API does not


contain‫‏‬the‫“‏‬.”‫‏‬and‫“‏‬..”‫‏‬links.‫‏‬These‫‏‬links‫‏‬
are accessible only after the user
explicitly creates them.
 Directory‫‏‬created‫‏‬by‫‏‬mkdir‫‏‬has‫‏‬the‫“‏‬.”‫‏‬and‫‏‬
“..”‫‏‬links‫‏‬created‫‏‬in‫‏‬one‫‏‬atomic‫‏‬operation,‫‏‬
and it is ready to be used.
 One can create directories via system
API’s‫‏‬as‫‏‬well.
 A newly created directory has its user ID
set to the effective user ID of the process
that creates it.

 Directory group ID will be set to either the


effective group ID of the calling process
or the group ID of the parent directory that
hosts the new directory.
FUNCTIONS

Opendir:
DIR*opendir (const char* path_name);
This opens the file for read-only
Readdir:
Dirent* readdir(DIR* dir_fdesc);
The dir_fdesc value is the DIR* return
value from an opendir call.
Closedir :
int closedir (DIR* dir_fdesc);
It terminates the connection between the
dir_fdesc handler and a directory file.

Rewinddir :
void rewinddir (DIR* dir_fdesc);
Used to reset the file pointer associated
with a dir_fdesc.
 Rmdir API:
int rmdir (const char* path_name);
Used to remove the directory files. Users
may also use the unlink API to remove
directories provided they have superuser
privileges.
These‫‏‬API’s‫‏‬require‫‏‬that‫‏‬the‫‏‬directories‫‏‬to‫‏‬
be removed be empty, in that they contain
no‫‏‬files‫‏‬other‫‏‬than‫“‏‬.”‫‏‬and‫“‏‬..”‫‏‬links.
Device file APIs
 Device files are used to interface physical
devices (ex: console, modem) with
application programs.
 Device files may be character-based or
block-based
 The only differences between device files
and regular files are the ways in which
device files are created and the fact that
lseek is not applicable for character
device files.
To create:
int mknod(const char* path_name, mode_t
mode,int device_id);
 The mode argument specifies the access
permission of the file
 The device_id contains the major and
minor device numbers. The lowest byte of
a device_id is set to minor device number
and the next byte is set to the major
device number.
MAJOR AND MINOR NUMBERS
 When a process reads from or writes to a
device file, the file’s major device number
is used to locate and invoke a device
driver function that does the actual data
transmission.
 The minor device number is an argument
being passed to a device driver function
when it is invoked. The minor device
number specifies the parameters to be
used for a particular device type.
 A device file may be removed via the
unlink API.
 If O_NOCTTY flag is set in the open call,
the kernel will not set the character device
file opened as the controlling terminal in
the absence of one.
 The O_NONBLOCK flag specifies that the
open call and any subsequent read or
write calls to a device file should be
nonblocking to the process.
FIFO File APIs
 These are special device files used for
interprocess communication.
 These are also known as named files

 Data written to a FIFO file are stored in a


fixed-size buffer and retrieved in a
first-in-first-out order.
 To create:

int mkfifo( const char* path_name, mode_t


mode);
How is synchronization provided?

 When a process opens a FIFO file for


read-only, the kernel will block the
process until there is another process
that opens the same file for write.
 If a process opens a FIFO for write, it will
be blocked until another process opens
the FIFO for read.
This provides a method for process
synchronization
 If a process writes to a FIFO that is full,
the process will be blocked until another
process has read data from the FIFO to
make room for new data in the FIFO.
 If a process attempts to read data from a
FIFO that is empty, the process will be
blocked until another process writes data
to the FIFO.
 If a process does not desire to be blocked
by a FIFO file, it can specify the
O_NONBLOCK flag in the open call to the
FIFO file.
 UNIX System V defines the O_NDELAY
flag which is similar to the O_NONBLOCK
flag. In case of O_NDELAY flag the read
and write functions will return a zero
value when they are supposed to block a
process.
 If a process writes to a FIFO file that has
no other process attached to it for read,
the kernel will send a SIGPIPE signal to
the process to notify it of the illegal
operation.
 If Two processes are to communicate
via a FIFO file, it is important that the
writer process closes its file
descriptor when it is done, so that the
reader process can see the end-of-file
condition.
 Pipe API
Another method to create FIFO files for
interprocess communications
int pipe (int fds[2]);
 Uses of the fds argument are:
 fds[0] is a file descriptor to read
data from the FIFO file.
 fds[1] is a file descriptor to write
data to a FIFO file.

 The child processes inherit the FIFO file


descriptors from the parent, and they
can communicate among themselves
and the parent via the FIFO file.
Symbolic Link File APIs
 These were developed to overcome
several shortcomings of hard links:
 Symbolic links can link from across file
systems
 Symbolic links can link directory files
 Symbolic links always reference the
latest version of the file to which they link
 Hard links can be broken by removal of
one or more links. But symbolic link will
not be broken.
To create :
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);
 To QUERY the path name to which a
symbolic link refers, users must use the
readlink API. The arguments are:
 sym_link is the path name of the symbolic
link
 buf is a character array buffer that holds
the return path name referenced by the
link
 size specifies the maximum capacity of
the buf argument
UNIX PROCESSES
MAIN FUNCTION
 PROTOTYPE:
int main(int argc, char *argv[ ]);

Argc – is the number of command line


arguments
argv [ ] – is an array of pointers to the
arguments
 A C program is started by a kernel

 A special start up routine is called before t


he main function is called

 This start up routine takes values from the


kernel and sets things up so that the main
function is called
Process termination
 Normal termination
* 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
 Abnormal termination
* calling abort
*receipt of a signal
* response of last thread to a cancellation req
exit and _exit functions
 _exit , _Exit returns to kernel immediately
 exit performs certain cleanup processing a
nd then returns to kernel
 PROTOTYPE

#include <stdlib.h>
void _Exit (int status)‫;‏‬
void exit (int status)‫;‏‬
#include <unistd.h>
void _exit (int status)‫;‏‬
 The exit status is undefined if

 Either of these function is called without


an exit status
 Main does a return without a return value
 Main “falls‫‏‬of‫‏‬the‫‏‬end”
 exit(0) same as return (0) from main()
At exit function

 With ANSI C a process can register up to 3


2 functions that are called by exit ---called
exit handlers
 Exit handlers are registered by calling the
atexit function

#include <stdlib.h>
int atexit (void (*fun) void));
 Atexit function calls these functions in rev
erse order of their registration

 Each function is called as many times as i


t was registered
#include "ourhdr.h"
static void my_exit1(void), my_exit2(void);
int main(void)‫‏‬
{
if (atexit(my_exit2) != 0)‫‏‬
err_sys("can't register my_exit2");
if (atexit(my_exit1) != 0)‫‏‬
err_sys("can't register my_exit1");
if (atexit(my_exit1) != 0)‫‏‬
err_sys("can't register my_exit1");
printf("main is done\n");
return(0);
}
static void Output:
./a.out
my_exit1(void)‫‏‬ main is done
first exit handler
{ first exit handler
second exit handler
printf("first exit handler\n");
}
static void
my_exit2(void)‫‏‬
{
printf("second exit handler\n");
}
Command-line arguments
 /* program to echo command line
arguments*/
int main (int argc, char* argv[ ])‫‏‬
{
for(int i=0;i<argc ;i++)‫‏‬
{
printf(“argv[%d]:%s‫\‏‬n”,I,argv[i]);
}
}
Environment list

 Environment list – is an array of character


pointers ,where each pointer contains the
address of a null terminated C string
 The address of array of pointers is contain
ed in global variable environ
 extern char **environ;
 each string is of the form name=value
Environment
Environment list
pointer
HOME=/home/abc
PATH=:/bin:/usr/bin\0

NULL
Memory layout of a C program
 Text segment – sharable copy
 Initialized data segment – variables

specifically initialized in the program


 Uninitialized data segment – “bss”‫‏‬segment‫‏‬
(block started by symbol)‫‏‬
data is initialized to arithmatic 0 or null by ?
Stack – return address and information about
caller’s‫‏‬environment‫‏‬
 Heap – dynamic memory allocation takes pl
ace on the heap
High address

Command line arguments


Stack And environment variables

heap
Uninitialised data Intialized to 0 by exec

initialised data

Text
Low address
Shared libraries
 Shared libraries remove the common
library routines from the executable file ,
instead maintaining a single copy of the
library routine some where in memory that all
processes reference
 Advantage: reduces size of executable file

easy to replace with a newer version


 Disadvantage: some- runtime overhead

(pgm first execd or first time called)‫‏‬


Memory allocation

 malloc : allocates specified number of


bytes of memory
 calloc : allocates specified number of
objects of specified size
 realloc : changes size of previous
allocated area
#include <stdlib.h>
void *malloc (size_t size);
void *calloc (size_t nobj, size_t size);
void *realloc (void *ptr, size_t newsize);
Return nonnull pointer if OK,NULL error
Void free (void *ptr);
realloc may increase or decrease the size o
f previously allocated area .
If it decreases the size no problem occurs
But‫‏‬if‫‏‬the‫‏‬size‫‏‬increases‫‏‬then………….
 Either there is enough space then the me
mory is reallocated and the same pointer
is returned
 If there is no space then it allocates new
area copies the contents of old area to
new area frees the old area and returns
pointer to the new area
Alloca function
 It is same as malloc but instead of allocating
memory from heap, the memory allocated
from the stack frame of the current function.
 It increases the size of stack frame

 Advantage automatically frees the space

when the function returns.


 Disadvantage Not supported on all systems.
Environment variables
 Environment strings are of the form name
=value
 ANSI C defined functions
#include <stdlib.h>
char *getenv (const char *name);
int putenv (const char *str);
int setenv (const char *name, const char
*value ,int rewrite);
void unsetenv (const char *name);
 Getenv : fetches a specific value from the
environment
 Putenv : takes a string of the form
name=value , if it already exists then
old value is removed
 Setenv : sets name to value. If name alread
y exists then a) if rewrite is non zero, then
old definition is removed
b) if rewrite is zero old definition is
retained
 Unsetenv : removes any definition of name
 Removing an environment variable is simple just
find the pointer and move all subsequent pointers
down one
 But while modifying
* if size of new value<=size of new value
just copy new string over the old string
* if new value >oldvalue use malloc obtain
room for new string, copy the new
string to this area and replace the old
pointer in environment list for name
with pointer to this malloced area
 While adding a new name call malloc
allocate room for name=value string and
copy the string to this area
 If‫‏‬it’s‫‏‬the‫‏‬first‫‏‬time‫‏‬a‫‏‬new‫‏‬name‫‏‬is‫‏‬added‫‏‬,

use malloc to obtain area for new list of


pointers. Copy the old list of pointers to
the malloced area and add the new pointer
to its end
 If its not the first time a new name was

added ,then just reallocate area for new


pointer since the list is already on the
heap
Set jump and long jump

 To transfer control from one function to a


nother we make use of setjmp and
longjmp functions

#include <stdio.h>
int setjmp (jmp_buf env);
void longjmp (jmp_buf env, int val);
 env is of type jmp_buf ,this data type is
form of array that is capable of holding all
information required to restore the status
of the stack to the state when we call
longjmp.

 Val allows us to have more than one


longjmp for one setjmp
#include <setjmp.h>
#include "ourhdr.h"

static void f1(int, int, int);


static void f2(void);

static jmp_buf jmpbuffer;


int main(void)‫‏‬
{
int count;
register int val;
volatile int sum;
count = 2; val = 3; sum = 4;
if (setjmp(jmpbuffer) != 0) {
printf("after longjmp: count = %d,
val = %d, sum = %d\n", count, val, sum);
exit(0);
}
count = 97; val = 98; sum = 99;
/* changed after setjmp, before longjmp */
f1(count, val, sum);
/* never returns */
}
static void
f1(int i, int j, int k)‫‏‬
{
printf("in f1(): count = %d, val = %d,
sum = %d\n", i, j, k);
f2();
}
static void f2(void)‫‏‬
{
longjmp(jmpbuffer, 1);
}
 The state of automatic,register and
volatile variables after longjmp
 If compiled with optimization
getrlimit and setrlimit

#include <sys/time.h>
#include <sys/resource.h>
int getrlimit (int resource ,struct
rlimit *rlptr);
int setrlimit (int resource ,const struct
rlimit *rlptr);
Struct rlimit
{
rlim_t rlim_cur; /*soft limit*/
rlim_t rlim_max; /*hard limit */
}
• Soft link can be changed by any process
to a value <= to its hard limit
• Any process can lower its hard limit to a
value greater than or equal to its soft
limit
• Only super user can raise hard limit
 RLIMIT_CORE – max size in bytes of a
core file
 RLIMIT_CPU – max amount of CPU time in
seconds
 RLIMIT_DATA – max size in bytes of data

segment
 RLIMIT_FSIZE – max size in bytes of a file
that can be created
 RLIMIT_MEMLOCK – locked in-memory

address space
 RLIMIT_NOFILE – max number of open
files per process
 RLIMIT_NPROC – max number of child
process per real user ID
 RLIMIT_OFILE – same as RLIMIT_NOFILE
 RLIMIT_RSS – max resident set size in
bytes
 RLIMIT_STACK – max size in bytes of the
stack
 RLIMIT_VMEM – max size in bytes of the
mapped address space
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "ourhdr.h"
#definedoit(name) pr_limits(#name, name)‫‏‬
static voidpr_limits(char *, int);
int main(void)‫‏‬
{
doit(RLIMIT_CORE);
doit(RLIMIT_CPU);
doit(RLIMIT_DATA);
doit(RLIMIT_FSIZE);
#ifdef RLIMIT_MEMLOCK
doit (RLIMIT_MEMLOCK);
#endif
#ifdef RLIMIT_NOFILE /* SVR4 name */
doit (RLIMIT_NOFILE);
#endif
#ifdef RLIMIT_OFILE /* 44BSD name */
doit (RLIMIT_OFILE);
#endif
#ifdef RLIMIT_NPROC
doit (RLIMIT_NPROC);
#endif
#ifdef RLIMIT_RSS
doit(RLIMIT_RSS);
#endif
doit(RLIMIT_STACK);
#ifdef RLIMIT_VMEM
doit(RLIMIT_VMEM);
#endif
exit(0);
}
static void
pr_limits(char *name, int resource)‫‏‬
{
struct rlimit limit;
if (getrlimit(resource, &limit) < 0)‫‏‬
err_sys("getrlimit error for %s", name);
printf("%-14s ", name);
if (limit.rlim_cur == RLIM_INFINITY)‫‏‬
printf("(infinite) ");
else
printf("%10ld ", limit.rlim_cur);
if (limit.rlim_max == RLIM_INFINITY)‫‏‬
printf("(infinite)\n");
else
printf("%10ld\n", limit.rlim_max);
}
Kernel support for processes
Kernel region table
Process table

Per process region table


text

File descriptor table data


Current directory
root stack
Per process u-area
 A process consists of
 A text segment – program text of a proces
s in machine executable instruction code
format
 A data segment – static and global

variables in machine executable format


 A stack segment – function arguments,

automatic variables and return addresses


of all active functions of a process at any
time
 U-area is an extension of Process table

entry and contains process-specific data


Kernel region table
Process table
stack

data
parent File table

text
Fd table

child
Fd table
stack

data
Besides open files the other properties inhe
rited by child are
 Real user ID, group ID, effective user ID, ef
fective group ID
 Supplementary group ID

 Process group ID

 Session ID

 Controlling terminal

 set-user-ID and set-group-ID

 Current working directory


 Root directory
 Signal handling
 Signal mask and dispositions
 Umask
 Nice value
 The difference between the parent & child
 The process ID
 Parent process ID
 File locks
 Alarms clock time
 Pending signals
Process identifiers
 Every process has a unique process ID, a
non negative integer
 Special processes :
process ID 0 : scheduler process also
known as swapper
process ID 1 init process, init process
never dies ,it’s‫‏‬a‫‏‬normal‫‏‬user process run
with super user privilege
process ID 2 pagedaemon
#include <unistd.h>
#include <sys/types.h>
pid_t getpid (void);
pid_t getppid (void);
uid_t getuid (void);
uid_t geteuid (void);
gid_t getgid (void);
gid_t getegid (void);
Fork function

 The only way a new process is created


by UNIX kernel is when an existing
process calls the fork function

#include <sys/types.h>
#include <unistd.h>
pid_t fork (void);
 The new process created by fork is called
child process
 The function is called once but returns
twice
 The return value in the child is 0
 The return value in parent is the process
ID of the new child
 The child is a copy of parent
 Child gets a copy of parents data , heap
and stack
 Instead of completely copying we can use
COW ( copy on write) technique
#include <sys/types.h>
#include "ourhdr.h"
int glob = 6;
/* external variable in initialized data */
char buf[ ] = "a write to stdout\n";
int main(void)‫‏‬
{
int var;
/* automatic variable on the stack */
pid_t pid;
var = 88;
if (write(STDOUT_FILENO, buf, sizeof(buf)
-1) != sizeof(buf)-1)‫‏‬
err_sys("write error");

printf("before fork\n");
if ( (pid = fork()) < 0)‫‏‬
err_sys("fork error");
else if (pid == 0)‫‏‬
{ /* child */
glob++; /* modify variables */
var++;
}
else
sleep(2);
/* parent */

printf("pid = %d, glob = %d, var = %d\n", g


etpid(), glob, var);
exit(0);
}
 file sharing
 Fork creates a duplicate copy of the file
descriptors opened by parent
 There are two ways of handling
descriptors after fork
• The parent waits for the child to complete
• After fork the parent closes all descriptors
that‫‏‬it‫‏‬doesn’t‫‏‬need‫‏‬and‫‏‬the‫‏‬does‫‏‬the‫‏‬same‫‏‬
thing.
Besides open files the other properties in
herited by child are
 Real user ID, group ID, effective user ID,
effective group ID
 Supplementary group ID
 Process group ID
 Session ID
 Controlling terminal
 set-user-ID and set-group-ID
 Current working directory
 Root directory
 File mode creation mask
 Signal mask and dispositions
 The close-on-exec flag for any open file
descriptors
 Environment
 Attached shared memory segments
 Resource limits
The difference between the parent and child
 The return value of fork

 The process ID

 Parent process ID

 The values of tms_utime , tms_stime ,

tms_cutime , tms_ustime is 0 for child


 file locks set by parent are not inherited

by child
 Pending alarms are cleared for the child

 The set of pending signals for the child is


set to empty set
 The functions of fork

• A process can duplicate itself so that


parent and child can each execute
different sections of code.

• A process can execute a different


program.
vfork
 It is similar to fork.
 It is intended to create a new process
when the purpose of new process is to
exec a new program.
 The child runs in the same address space
as parent until it calls either exec or exit.
 vfork guarantees that the child runs first ,
until the child calls exec or exit.
int glob = 6;
/* external variable in initialized data */
int main(void)‫‏‬
{
int var;
/* automatic variable on the stack */
pid_t pid;
var = 88;
printf("before vfork\n");
if ( (pid = vfork()) < 0)‫‏‬
err_sys("vfork error");
else if (pid == 0) { /* child */
glob++;
/* modify parent's variables */
var++;
_exit(0); /* child terminates */
}
/* parent */
printf("pid = %d, glob = %d, var = %d\n", g
etpid(), glob, var);
exit(0);
}
exit functions
 Normal termination.
 Return from main.
 Calling exit – includes calling exit handlers.
 Calling _exit – it is called by exit function.
 Abnormal termination.
 Calling abort – SIGABRT.
 When process receives certain signals.
 Exit status is used to notify parent how a
child terminated.
 When a parent terminates before the
child, the child is inherited by init process.
 If the child terminates before the parent
then the information about the is obtained
by parent when it executes wait or waitpid.
 The information consists of the process
ID, the termination status and amount of
CPU time taken by process.
 A process that has terminated , but
whose parents has not yet waited for
it, is called a zombie.
 When a process inherited by init
terminates it doesn’t‫‏‬become‫‏‬a‫‏‬zombie.
 Init executes one of the wait functions to
fetch the termination status.
Wait and waitpid functions

 When a child id terminated the parent is


notified by the kernel by sending a
SIGCHLD signal.
 The termination of a child is an
asynchronous event.
 The parent can ignore or can provide a
function that is called when the signal
occurs.
 The process that calls wait or waitpid can
• Block.
• Return immediately with termination.
status of the child.
• Return immediately with an error.
#include <sys/wait.h>
pid_t wait (int *statloc);
pid_t waitpid (pid_t pid,int *statloc ,
int options );
Return : process ID if OK,-1 on error,0 ?
 Statloc is a pointer to integer
 If statloc is not a null pointer ,the termination
status of the terminated process is stored in
the location pointed to by the argument.
 The integer status returned by the two
functions give information about exit status,
signal number and about generation of core
file.
 Macros which provide information about
how a process terminated

WIFEXITED TRUE – if child terminated


normally
WEXITSTATUS – is used to
fetch the lower 8 bits of
argument child passed to
exit or _exit
TRUE – if child terminated
abnormally
WIFSIGNALED WTERMSIG – is used to
fetch the signal number
that caused termination
WCOREDUMP – is true is
core file was generated
WIFSTOPPED TRUE – for a child that is
currently stopped
WSTOPSIG -- is used to
fetch the signal number
that caused child to stop
waitpid
 The interpretation of pid in waitpid
depends on its value
 Pid == -1 – waits for any child
 Pid > 0 – waits for child whose process
ID equals pid
 Pid == 0 – waits for any child whose
process group ID equals that of calling
process
 Pid < -1 – waits for child whose process
group ID equals to absolute value of pid
 Waitpid helps us wait for a particular
process.
 It is nonblocking version of wait.
 It supports job control.

WCONTINUED If implementation supports


job control, status of any
child specified by pid that
has been continued after
being stopped.
WNOHANG Waitpid will not block if the
child specified is not
available.Return value 0
supports job control. The status of
WUNTRACED any child specfied by pid that has
stopped and whose status has not
been reported.
WIFSTOPPED macro determines
whether return value corresponds
to a stopped child process
#include <sys/types.h>
#include <sys/wait.h>
#include "ourhdr.h"
Int main(void)‫‏‬
{
pid_t pid;
int status;
if ( (pid = fork()) < 0)‫‏‬
err_sys("fork error");
else if (pid == 0) /* child */
exit(7);
if (wait(&status) != pid)
/* wait for child */
err_sys("wait error");
pr_exit(status);
/* and print its status */
if ( (pid = fork()) < 0)‫‏‬
err_sys("fork error");
else if (pid == 0) /* child */
abort();
/* generates SIGABRT */
if (wait(&status) != pid)
/* wait for child */
err_sys("wait error");
pr_exit(status);
/* and print its status */
if ( (pid = fork()) < 0)‫‏‬
err_sys("fork error");
else if (pid == 0) /* child */
status /= 0;
/* divide by 0 generates SIGFPE */
if (wait(&status) != pid)
/* wait for child */
err_sys("wait error");
pr_exit(status);
/* and print its status */

exit(0);
}
wait3 and wait4 functions

 These functions are same as waitpid but


provide additional information about the
resources used by the terminated
process.
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/times.h>
#include <sys/resource.h>
pid_t wait3 (int *statloc ,int options, struct
rusage *rusage );
pid_t wait4 (pid_t pid ,int *statloc ,int
options, struct rusage *rusage );
Race condition

 Race condition occurs when multiple


process are trying to do something with
shared data and final out come depends
on the order in which the processes run.
Program with race condition

#include <sys/types.h>
#include "ourhdr.h"
static void charatatime(char *);
int main(void)‫‏‬
{
pid_t pid;
if ( (pid = fork()) < 0)‫‏‬
err_sys("fork error");
else if (pid == 0)
{
charatatime("output from child\n");
}
else
{
charatatime("output from parent\n");
}
exit(0);
}
static void
charatatime(char *str)‫‏‬
{
char *ptr;
int c;
setbuf(stdout, NULL);
/* set unbuffered */
for (ptr = str; c = *ptr++; )‫‏‬
putc(c, stdout);
}
/*altered program*/
#include <sys/types.h>
#include "ourhdr.h"
static void charatatime(char *);
Int main(void)‫‏‬
{
pid_t pid;
TELL_WAIT();
if ( (pid = fork()) < 0)‫‏‬
err_sys("fork error");
else if (pid == 0)
{
WAIT_PARENT(); /* parent goes first */
charatatime("output from child\n");
}
else {
charatatime("output from parent\n");
TELL_CHILD(pid);
}
exit(0);
}
static void charatatime(char *str)‫‏‬
{
char *ptr;
int c;
setbuf(stdout, NULL);
/* set unbuffered */
for (ptr = str; c = *ptr++; )‫‏‬
putc(c, stdout);
}
exec functions

 Exec replaces the calling process by a


new program.
 The new program has same process ID as
the calling process.
 No new program is created , exec just
replaces the current process by a new
program.
#include <unistd.h>
int execl ( const char *pathname,
const char *arg0 ,…‫‏‬/*(char‫‏)*‏‬0*/);
int execv (const char *pathname, char *
const argv[ ]);
int execle (const char *pathname, const
char *arg0 ,…‫‏‬/*(char‫‏)*‏‬0,
char *const envp[ ] */);
int execve ( const char *pathname,
char *const argv[ ] ,
char *const envp [ ]);
int execlp (const char *filename, const
char *arg0 ,…‫‏‬/*(char‫‏)*‏‬0*/);
int execvp (const char *filename ,char
*const argv[ ] );
 Relation between exec functions

execl execle
execlp
Build argv Build argv Build argv

Try each use


execvp execv environ
execve
PATH prefix
#include <sys/types.h>
#include <sys/wait.h>
#include "ourhdr.h"
char *env_init[ ] =
{ "USER=unknown", "PATH=/tmp", NULL };
int main(void)‫‏‬
{
pid_t pid;
if ( (pid = fork()) < 0)‫‏‬
err_sys("fork error");
else if (pid == 0) {
/* specify pathname, specify environment */
if ( execle ("/home/stevens/bin/echoall",
"echoall", "myarg1", "MY ARG2",
(char *) 0, env_init) < 0)‫‏‬
err_sys("execle error");
}
if (waitpid(pid, NULL, 0) < 0)‫‏‬
err_sys("wait error");

if ( (pid = fork()) < 0)‫‏‬


err_sys("fork error");
else if (pid == 0) {
/* specify filename, inherit environment */
if (execlp("echoall",
"echoall", "only 1 arg",
(char *) 0) < 0)‫‏‬
err_sys("execlp error");
}
exit(0);
}

You might also like