Lecture 6
Lecture 6
Operating System Concepts – 9th Edition 1.1 Silberschatz, Galvin and Gagne ©2013
AGENDA
• Operating System Modes
• File System Calls
• File Descriptor
• File Permissions
• File System Calls with Programs
Operating System Concepts – 9th Edition 1.2 Silberschatz, Galvin and Gagne ©2013
Operating System Modes
User Mode MODE bit = 1
1 1
0 0
Modes of Operating System
Execute System
Call
Operating System Concepts – 9th Edition 1.3 Silberschatz, Galvin and Gagne ©2013
System Calls
It is a programmatic way in which a computer program requests a service from
the kernel of the operating system.
System calls are the only entry points into the kernel system.
Operating System Concepts – 9th Edition 1.4 Silberschatz, Galvin and Gagne ©2013
File Descriptor
• File descriptor: FD is integer that uniquely identifies an open file of the
process.
• File Descriptor table: FDT is the collection of integer array indices that
are file descriptors in which elements are pointers to file table entries.
One unique file descriptors table is provided in operating system for each
process.
• File Table Entry: FDE is a structure In-memory surrogate for an open
file, which is created when process request to opens file and these entries
maintains file position.
File Descriptor Table
• the fd table is an array
for one running process
associated with every process
list of open files stored in the fd table
each entry corresponds to one open file
that is copied when fork is executed
• e.g.:
each process has its own table
open: add an entry to the table (new file created)
close: delete an entry from the table
this is for a "normal" process, one with no redirection of i/o (no forks)
fd # from 0 to 31
File Descriptor Table
creat() system call
Syntax in C language:
int creat(char *filename, mode_t mode)
Parameter :
filename : name of the file which you want to create
mode : indicates permissions of new file.
Returns :
return first unused file descriptor (generally 3 when first creat use in process
because 0, 1, 2 fd are reserved)
return -1 when error
How it works internally in the Operating System:
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
creat() system call Example
# include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main ()
{
int fd1, fd2;
printf(“/nThis would create two files”);
fd1= creat(“txt1.txt”, 0777);
fd2= creat(“txt2.txt”, 0777);
}
open() system call
Syntax in C language: fd = open(pathname, flags, modes);
int open(const char *path, int flags, mode_t
mode);
Parameter :
pathname is the file name
flags indicate type of file (reading/writing)
modes gives the file permission (if file is created)
Returns an integer called file descriptor
Rest of the system calls make use of this file descriptor
open() system call
Flag Description
O_RDONLY Open for reading only
O_WRONLY Open for writing only
O_RDWR Open for reading and writing
O_NONBLOC
K Do not block on open
O_APPEND Append on each write
O_CREAT Create file if it does not exist
O_TRUNC Truncate size to 0
O_EXCL Error if create and file exists
O_SHLOCK Atomically obtain a shared lock
O_EXLOCK Atomically obtain an exclusive lock
O_DIRECT Eliminate or reduce cache effects
O_FSYNC Synchronous writes
O_NOFOLLO
open() system call Example
# include <stdio.h>
#include <fcntl.h>
#include <unistd.h> abc@UNIX:~/OS_TUT$ ./open abd.txt
int main (int argc, char *argv[]) Error opening file
{ abc @UNIX :~/OS_TUT$ ./open tex1.txt
int fd1; file opened successfully
fd1 = open(argv[1],O_RDONLY); fd1=3
if(fd1 == -1){
printf("Error opening file \n");
exit(0);
}
printf("file opened successfully\n");
printf("fd1=%d\n",fd1);
}
close() system call
Syntax in C language: int close(int fd);
Parameter :
fd : file descriptor
Returns :
Returns 0 on success and -1 on error
Functioning:
It makes the file descriptor available for re-use.
It does not flush any kernel buffers or perform any other cleanup task.
close() system call Example
# include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
int main ()
{
int fd1, ret;
fd1 = open(“txt1.txt”, O_RDONLY, 0777);
ret = close(fd1);
printf(“Result:%d\n”, ret);
}
read() system call
Syntax in C language: size_t read(int fd, void *buf, size_t
nbytes);
Parameter :
fd : file descriptor
buf: buffer to hold data after read
nbytes: number of bytes to be read
Returns :
returns the number of bytes read, 0 for end of file (EOF) and -1 in case an error
occurred.
Functioning:
The process that executes a read operation waits until the system puts the
data from the disk into the buffer.
write() system call
Syntax in C language: size_t write(int fd, void *buf, size_t
nbytes);
Parameter :
fd : file descriptor
buf: buffer to hold data for write operation
nbytes: number of bytes to be written
Returns :
rseturns the number of bytes written and the value -1 in case of an error.
write() system call
# include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main ()
{
char buffer[12] = “Hello World”;
int fd1, ret;
fd1 = creat(“txt1.txt”, 0777);
ret = write(fd1, buffer, sizeof(buffer));
return 0;
}
lseek() system call
Syntax in C language:
off_t lseek(int fd, off_t offset, int reference);
Parameters:
offset is used to specify the position
reference is used by the offset
SEEK_SET – offset is absolute position
SEEK_CUR – offset is relative to the current position
SEEK_END – offset is relative to the end of the file
Functioning:
It sets the read/write pointer of a file descriptor which it can use for next
read/write.
lseek() system call Example
# include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
int main ()
{ abc@UNIX:~/OS_TUT$ cat testfile.txt
int file; This is a text file that will explain the use of
if((file=open("testfile.txt",O_RDONLY)) < 0) lseek system call.
return -1; abc@UNIX :~/OS_TUT$ ./lseek
char buffer[19]; This is a text file
if(read(file,buffer,19) != 19) return -1; text file that will
printf("%s\n",buffer);
if(lseek(file,10,SEEK_SET) < 0) return -1;
if(read(file,buffer,19) != 19) return -1;
printf("%s\n",buffer);
return 0;
}
link() system call
Make a new name for a file (symlinks in Ubuntu).
Syntax in C language: int link(const char *oldpath, const char
*newpath);
Returns :
– On success, zero is returned. On error, -1 is returned.
Functioning:
– link() creates a new link (also known as a hard link) to an existing file.
– If new path exists it will not be overwritten
– This new name may be used exactly as the old one for any operation;
both names refer to the same file (and so have the same permissions
and ownership) and it is impossible to tell which name was the
`original’.
link() system call Example
/
For e.g.:
• link ("/usr/src/uts/sys", "/usr/include/sys"); usr
• link ("/usr/include/realfile.h“,
"/usr/src/uts/sys/testfile.h"); src include
Output:
3 pathnames refer to the same file: uts sys
“/usr/src/uts/sys/testfile.h”
“/usr/include/sys/testfile.h” sys realfile.h
“/usr/include/realfile.h”
inode.h
Note: Only a superuser is allowed to link
directories. testfile.h
unlink() system call
Delete a name and possibly the file it refers to.
Syntax in C language: int unlink(const char *pathname);
Returns :
– On success, zero is returned. On error, -1 is returned, and errno
is set appropriately.
Functioning:
– unlink() deletes a name from the filesystem. If that name was
the last link to a file and no processes have the file open, the file
is deleted and the space it was using is made available for reuse.
– If the name was the last link to a file but any processes still have
the file open, the file will remain in existence until the last file
descriptor referring to it is closed.
dup() system call
Syntax in C language: int dup(int oldfd);
Parameter :
– oldfd : file descriptor being duplicated
Returns :
– returns a copy of the file descriptor.
Functioning:
– The dup() system call creates a copy of the file descriptor oldfd,
using the lowest-numbered unused file descriptor for the new
descriptor.
dup() system call Example
#include<stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int file_desc = open("dup.txt", O_WRONLY | O_APPEND);
if(file_desc < 0)
printf("Error opening the file\n");
int copy_desc = dup(file_desc);
write(copy_desc,"This will be output to the file named dup.txt\n", 46);
write(file_desc,"This will also be output to the file named dup.txt\n", 51);
return 0;
}
dup2() system call
Syntax in C language: int dup2(int oldfd, int newfd);
Parameter :
– oldfd : file descriptor being duplicated
– newfd : file descriptor to which the old file descriptor is to be copied
Returns :
– On success, these system calls return the new file descriptor. On
error, -1 is returned.
Functioning:
– The dup2() system call performs the same task as dup(), but instead of using the lowest-
numbered unused file descriptor, it uses the file descriptor number specified in newfd.
– If the file descriptor newfd was previously open, it is silently closed before being
reused.
dup2() system call Example
#include<stdlib.h>
#include<unistd.h>
#include<stdio.h>
#include<fcntl.h>
int main()
{
int file_desc = open("tricky.txt",O_WRONLY | O_APPEND);
dup2(file_desc, 1) ;
printf("I will be printed in the file tricky.txt\n");
return 0;
}
Directory system
calls
Directory System Calls
1. getcwd()
2. chdir()
3. mkdir()
4. rmdir()
5. opendir()
6. closedir()
7. readdir()
getcwd() system call
Syntax in C language: char *getcwd(char *buf, size_t size)
Parameters :
– buf : To hold the pathname after read.
– size : size of the buffer.
Returns :
– On success: A pointer to buf
– On failure: NULL. errno is set to indicate the error.
Functioning:
– The getcwd() function copies an absolute pathname of the
current working directory to the array pointed to by buf, which is
of length size.
getcwd() system call errors
Tag Description
EACCES Permission to read or search a component of the filename
was denied.
ENOMEM If user memory cannot be mapped
ENOENT If directory does not exist (i.e. The current working
directory has been unlinked)
ERANGE If not enough space available for storing the path
EFAULT If memory access violation occurs while copying
EINVAL The size argument is zero and buf is not a null pointer.
getcwd() system call
Example 1
#include <stdlib.h>
#include <unistd.h>
#include < errno.h >
#define PATH_MAX 255
int main (void) {
char dirname[PATH_MAX+1];
int main(void) {
long max;
char *buf;
max= pathconf(“/”,_PC_PATH_MAX);
buf=(char*)malloc(max);
if (getcwd(buf,max)==NULL)
{ printf(Error: Could not obtain current working directory.\n"); }
else
{ printf(“Path: current working directory is: %s\n", buf); }
return 0;
}
chdir() system call
Syntax in C language: int chdir(const char *pathname);
Parameters :
– pathname : The Directory path which the user want to make the
current working directory
Returns :
– On success: zero is returned and the current working directory
changes to the specified path.
– On failure: Returns -1. errno is set to indicate the error.
Functioning:
– The getcwd() function copies an absolute pathname of the current
working directory to the array pointed to by buf, which is of
length size.
chdir() system call errors
Error Code Description
EACCES Search permission is denied for one of the directories in the
path prefix of path.
EFAULT path points outside your accessible address space.
EIO An I/O error occurred.
ELOOP Too many symbolic links were encountered in resolving path.
ENAMETOOL path is too long.
ONG
ENOENT The file does not exist.
ENOMEM Insufficient kernel memory was available.
ENOTDIR A component of path is not a directory.
chdir() system call Example
#include <unistd.h>
#include <stdio.h>
int main(int argc,char *argv[])
{
char cwd[256];
printf("%s\n", getcwd(cwd, 500));
if (chdir(argv[1]) != 0)
printf("chdir() error()");
else
{
if (getcwd(cwd, sizeof(cwd)) == NULL)
printf("getcwd() error");
else
printf("current working directory is: %s\n", cwd);
}
return 0;
}
mkdir() system call
Syntax in C language: int mkdir(const char *pathname,
mode_t mode);
Parameters :
– pathname : The Directory path name to be created.
– mode: specifies the permission to use.
Returns :
– On success: zero is returned and a directory is created with the
name specified.
– On failure: Returns -1. errno is set to indicate the error.
Functioning:
– Creates a new, empty directory with name pathname.
mkdir() system call errors
Tag Description
EACCES The parent directory does not allow write permission to the process, or one of the
directories in pathname did not allow search permission. (See also
path_resolution(2).)
EEXIST pathname already exists (not necessarily as a directory). This includes the case
where pathname is a symbolic link, dangling or not.
EFAULT pathname points outside your accessible address space.
ELOOP Too many symbolic links were encountered in resolving pathname.
ENAMETOOLO pathname was too long.
NG
ENOENT A directory component in pathname does not exist or is a dangling symbolic link.
ENOMEM Insufficient kernel memory was available.
ENOSPC The device containing pathname has no room for the new directory.
ENOSPC The new directory cannot be created because the user’s disk quota is exhausted.
ENOTDIR A component used as a directory in pathname is not, in fact, a directory.
EPERM The filesystem containing pathname does not support the creation of directories.
EROFS pathname refers to a file on a read-only filesystem.
rmdir() system call
Syntax in C language: int rmdir(const char *pathname);
Parameters :
– pathname : The Directory to be removed.
Returns :
– On success: zero is returned.
– On failure: Returns -1. errno is set to indicate the error.
Functioning:
– Removes a directory, pathname, provided that the directory is empty.
– If no process currently has the directory open, rmdir() deletes the
directory itself. The space occupied by the directory is freed for new use.
– If one or more processes have the directory open when it is removed,
the directory itself is not removed until the last process closes the
directory.
rmdir() system call errors
Tag Description
EACCES Write access to the directory containing pathname was not allowed, or one of the directories
in the path prefix of pathname did not allow search permission.
EBUSY pathname is currently in use by the system or some process that prevents its removal. On
Linux this means pathname is currently used as a mount point or is the root directory of the
calling process.
EFAULT pathname points outside your accessible address space.
EINVAL pathname has . as last component.
ELOOP Too many symbolic links were encountered in resolving pathname.
ENAMETOOLON pathname was too long.
G
ENOENT A directory component in pathname does not exist or is a dangling symbolic link.
ENOMEM Insufficient kernel memory was available.
ENOTDIR pathname, or a component used as a directory in pathname, is not, in fact, a directory.
ENOTEMPTY pathname contains entries other than . and .. ; or, pathname has .. as its final component.
EPERM The directory containing pathname has the sticky bit (S_ISVTX) set and the process’s effective
user ID is neither the user ID of the file to be deleted nor that of the directory containing it,
and the process is not privileged (Linux: does not have the CAP_FOWNER capability).
EPERM The filesystem containing pathname does not support the removal of directories.
EROFS pathname refers to a file on a read-only filesystem.
mkdir() and rmdir() system
call Example
int main(int argc,char *argv[])
{ int md, rd;
//DIR *ds;
struct dirent *dir;
if(argc!=3)
{
printf("give sufficient filenames \n");
exit(1);
}
md = mkdir(argv[1],0777);
if(md == 0)
printf("%s directory is created\n",argv[1]);
else
printf("%s directory is not created\n",argv[1]);
rd = rmdir(argv[2]);
if(rd == 0)
printf("%s directory is removed\n",argv[2]);
else
printf("%s directory is not removed\n",argv[2]);
}
opendir() system call
Syntax in C language: DIR* opendir(const char *path);
Parameters :
– path : path name of the directory to be opened.
Returns :
– On success: a DIR pointer.
– On failure: Returns NULL. errno is set to indicate the error.
Functioning:
– opens a directory stream corresponding to the directory named
by the path argument.
– Note: add # include<dirent.h>
closedir() system call
Syntax in C language: int closedir(DIR *dirp);
Parameters :
– dirp : DIR pointer from opendir
Returns :
– On success: returns 0.
– On failure: Returns -1. errno is set to indicate the error.
Functioning:
– closes the directory stream associated with dirp.
– Note: add # include<dirent.h>
readdir() system call
Syntax in C language: struct dirent *readdir (DIR *dirp );
Parameters :
– dirp : A pointer to a DIR that refers to the open directory stream
to be read.
Returns :
– On success: A pointer to a dirent structure describing the next
directory entry in the directory stream
– On failure: Returns NULL. errno is set to indicate the error.
Functioning:
– Describes the next directory entry in the directory stream
associated with dirp.
– If readdir() reached the end of the directory stream, the errno
global variable is not changed.
– If readdir() was not successful, the errno is set.
dirent structure
struct dirent {
ino_t d_ino; /* inode number */
char d_name[256]; /* filename */
…
};
Program to display the files
in current working directory.
int main(int argc,char *argv[])
{
DIR *ds; struct dirent *dir;
ds = opendir(argv[1]);
if(ds == NULL)
printf("directory %s is not opened\n",argv[1]);
else
printf(“Directory opened/n”);
printf("list of files and directories\n");
while((dir = readdir(ds)) != NULL)
printf("%s\n",dir->d_name);
if((closedir(ds)) == 0)
printf("%s is successfully closed\n",argv[1]);
else
printf("%s is not successfully closed\n",argv[1]);
}
getpid() & getppid() system
calls
● To obtain current process id, use getpid() system call.
● To obtain parent process id, use getppid() system
call.
#include <stdio.h>
#include <unistd.h>
#include<sys/types.h>
int main() {
printf("I am process %ld\n",(long)getpid());
printf("My Parent process is %ld\n",(long)getppid());
}
fork() and exec() system
calls
• fork()
– It creates a new process which is an identical copy of an existing process.
– The newly created process will contain all the instructions and data of its parent
process.
– Hence it executes the same parent process.
– fork() is the only way to create new processes in UNIX
• exec()
– This on the other hand re-initializes the existing process with some other
designated program.
– It does not create a new process.
– It merely flushes the current context of a program and loads a new context (new
program).
– exec() call is the only way to execute programs in UNIX. In fact, the kernel boots
itself using the exec() call.
fork() system call
• fork() call creates a “new” process.
• The child process’ context will be the same as the parent process.
• After a fork() call, two copies of the same context exist, one
belonging to the child and another to the parent.
• Contrast this to exec(), where a single context will exist because of
child context over-writing the parent.
• Return values: The child receives a 0 as return value from fork()
and the parent receives the process-ID of the child.
• # include<unistd.h>
• int fork(void);
• /*Returns child process-ID and 0 on success and -1 on error */
fork() system call
#include<stdio.h>
int main()
{
printf(“************ Before Fork************\n”);
fork();
printf(“************ After Fork *************\n”;
return 0;
}
Multiple Fork System Call
Repeated fork() calls (also known as fork #include <stdio.h>
bombs) can eventually lead to resource
#include
exhaustion and may collapse the system.
<sys/types.h>
Program to demonstrate repeated forking
int main()
int main(void){
int cnt=0; {
int pid; fork();
for(cnt=0;cnt<3;cnt++){ fork();
pid=fork(); fork();
printf("Now in process %d\n",getpid()); printf("hello\n");
}
return 0;
return 0;
}
}
how many new processes would be created?
Output
The number of children would be : 7
S.n Fork() system call in Print output Parent process Child Process
o. program “Hello”
1 Fork() 2 1 1
2 Fork() 4 1 3
Fork()
3 Fork() 8 1 7
Fork()
Fork()
exec() System Call
⚫exec() re-initializes the existing process with some other designated
program.
⚫It does not create a new process.
⚫It merely flushes the current context of a program and loads a new
context (new program).
⚫exec() call is the only way to execute programs in UNIX.
exec() System Call family
1. int execl(const char *path, const char *arg, …, NULL);
2. int execlp(const char *file, const char *arg, …, NULL );
3. int execv(const char *path, char *const argv[]);
4. int execvp(const char *file, char *const argv[]);
5. int execle(const char *path, const char *arg, …, NULL, char * const envp[] );
6. int execve(const char *file, char *const argv[], char *const envp[]);
exec() System Call family
execAB
S.No. A B Name Meaning
4 e Environment pointer
5 l p execlp Execute file with arguments explicitly in call and PATH search
• The other exec calls are very similar to execl(). They provide the
following three features that are not available in execl().
• Arguments can be put into a vector/array instead of explicitly listing
them in the execl call. This feature is useful if the arguments are
not known at compile time.
• Search an executable using the value of the PATH environment
variable. When this feature is used, we don’t have to specify the
complete path in the exec call.
• Manually passing an explicit environment pointer instead of
automatically using environ.
2. execv() System Call
Syntax: int execv (
const char *path, /* Program pathname */
char* const argv[] /* Argument vector */
);
Parameters:
• Like execl(), this function takes the path of the executable binary file
(i.e. /bin/ls) as the first argument.
• With execv(), you can pass all the parameters in a NULL terminated array
argv. The first element of the array should be the path of the executable
file.
Return Value:
• If any error occurs, then execl() returns -1.
• Otherwise, it returns nothing.
execv() System call Example
#include <unistd.h>
int main(void) {
char *binaryPath = "/bin/ls";
char *args[] = {binaryPath,"-lh", "/home", NULL};
execv(binaryPath, args);
return 0;
}
3. execlp() System Call
Syntax: int execlp (
const char *file, /* Program filename */
const char *arg0, /*First Argument(filename) */
const char *arg1,
…
(char *) NULL /* Arg list terminator */
);
Parameters:
• First argument is the program filename - the full path of the executable file is not
required. The PATH Environment variable is used to find the executable file location.
• Parameters to the executable are passed as arguments in a sequential order and
terminated with a null pointer.
Return Value:
• If any error occurs, then execlp() returns -1.
• Otherwise, it returns nothing.
execlp() System call
Example
#include <unistd.h>
int main(void) {
char *programName = "ls";
char *arg1 = "-lh";
char *arg2 = "/home";
execlp(programName, programName, arg1, arg2, NULL);
return 0;
}
In execlp() system function takes the Command_name (example: ls). Then, the arguments
(i.e. -lh, /home) that you want to pass to the executable followed by NULL. Then execlp()
system function runs the command and prints the output. If any error occurs, then
execlp() returns -1. Otherwise, it returns nothing.
4. execvp() System Call
Syntax: int execvp (
const char *file, /* Program filename */
char* const argv[] /* Argument vector */
);
Parameters:
• First argument is the program filename - the full path of the executable file is
not required. The PATH Environment variable is used to find the executable
file location.
• Like execv(), you can pass all the parameters in a NULL terminated array
argv. The first element of the array should be the name of the executable file.
Return Value:
• If any error occurs, then execvp() returns -1.
• Otherwise, it returns nothing.
execvp() System call
Example
#include <unistd.h>
int main(void) {
char *programName = "ls";
char *args[] = {programName, "-lh", "/home", NULL};
execvp(programName, args);
return 0;
}
Works the same way as execv() system function. But, the PATH environment variable is
used. So, the full path of the executable file is not required just as in execlp().
5. execle() System Call
Syntax: int execle (
const char *path, /* Program pathname */
const char *arg0, /*First Argument(filename) */
const char *arg1,
…
(char *) NULL, /* Arg list terminator */
char *const env[] /* Environment vector */
);
Parameters:
• Like execl(), this function takes the path of the executable binary file (i.e. /bin/ls) as the first and
second argument.
• The environment variables are passed as an array envv. The last element of the envv array
should be NULL. All the other elements contain the key-value pairs as string.
Return Value:
• If any error occurs, then execle() returns -1.
• Otherwise, it returns nothing.
execle() System call
Example1
#include <unistd.h>
int main(void) {
char *binaryPath = "/bin/bash";
char *arg1 = "-c";
char *arg2 = "echo YOU ARE THE PART OF $A $B";
char *const env[] = {“A=BITS“,”B=PILANI”, NULL};
Works just like execl() but you can provide your own environment variables along with it.
The environment variables are passed as an array envp. The last element of the envp array
should be NULL. All the other elements contain the key-value pairs as string.
execle() Program Example2
#include <unistd.h>
int main(void) {
char *binaryPath = "/bin/bash";
char *arg1 = "-c";
char *arg2 = "echo Visit $HOSTNAME:$PORT from your browser.";
char *const env[] = {"HOSTNAME=www.bits.com", "PORT=8080", NULL};
execle(binaryPath, binaryPath,arg1, arg2, NULL, env);
return 0;
}
6. execve() System Call
Syntax: int execve (
const char *path, /* Program pathname */
char *const argv[], /* Argument vector */
char *const env[] /* Environment vector */
);
Parameters:
• Like execl(), this function takes the path of the executable binary file (i.e. /bin/ls) as the
first argument.
• With execv(), you can pass all the parameters in a NULL terminated array argv. The first
element of the array should be the path of the executable file. The environment variables
are passed as an array envv. The last element of the envv array should be NULL. All the
other elements contain the key-value pairs as string.
Return Value:
• If any error occurs, then execle() returns -1.
• Otherwise, it returns nothing.
execve() System call
Example1
#include <unistd.h>
int main(void) {
char *binaryPath = "/bin/bash";
char *const args[] = {binaryPath, "-c", "echo YOU ARE THE PART OF $A $B
", NULL};
char *const env[] = {“A=BITS“,”B=PILANI”, NULL};
Just like execle() you can provide your own environment variables along with execve(). You
can also pass arguments as arrays as you did in execv().
wait() System Call
• The wait() system call suspends execution of the current process
until one of its children terminates or a signal is received. At that
moment, the caller resumes its execution.
• One of the main purposes of wait() is to wait for completion of child
processes.
• If any process has more than one child processes, then after calling
wait(), parent process has to be in wait state if no child terminates.
• In the case of a terminated child, performing a wait allows the
system to release the resources associated with the child; if a wait
is not performed, then terminated the child remains in a "zombie"
state
wait() System Call
Returns:
• If any process has no child process then wait() returns immediately
“-1”.
• If the parent process has a child that has terminated, that child's
PID is returned and it is removed from the process table.
• If only one child process is terminated, then return a wait() returns
PID of the terminated child process.
• If the parent process has a child that is not terminated, it (the
parent) is suspended till it receives a signal. The signal is received
as soon as a child dies.
Fork() and wait() system call
#include<stdio.h>
if (pid == -1) /* check for error in fork */{
#include<unistd.h>
perror("fork failed");
int main(void)
exit(1);}
{
if (pid == 0)
int pid;
printf("I am the child process. %d\n",getpid());
int status;
else{
printf("Hello World!\
wait(&status); /* parent waits for child to
n");
finish */
pid = fork( );
printf("Child Process with pid = %d completed
with a status %d\n",pid,status);
printf("I am the parent process.%d\
n",getpid());}
}
Fork() and wait() system call
Output:
Hello World!
I am the child process. 24995
Child Process with pid = 24995 completed with a status 0
I am the parent process. 24994
Multiple fork() and wait()
system calls
#include<stdio.h>
else if (second == -1){
#include<unistd.h>
perror("2nd fork: something went wrong\n")
int main(){ ; exit(1); }
pid_t whichone, first, second ; int howmany, printf("This is parent\n");
status ;
howmany = 0;
if((first = fork()) == 0) /* Parent spawns 1st
while(howmany < 2) {/* Wait Twice */
child */ {
whichone = wait(&status);
printf("I am the first child, & my ID is %d\n",
getpid()); howmany++;
sleep(10); exit(0); } if(whichone == first)
else if(first == -1) { printf("First child exited\ncorrectly");
perror("1st fork: something went wrong\n") ; else
exit(1); } printf("Second child exited\ncorrectly");}}
else if((second = fork()) == 0) /* Parent
spawns 2nd child */ {
printf("I am the second child, & my ID is %d\
Multiple fork() and wait()
system calls
Output:
I am the first child, & my ID is 77
This is parent
I am the second child, & my ID is 78
<after 10 seconds>
First child exited
Correctly
<after 15 seconds>
Second child exited
correctly
Zombie Process
• On Unix and Unix-like computer operating systems, a zombie process or defunct
process is a process that has completed it’s execution but still has an entry in the
process table.
• This entry is still needed to allow the parent process to read its child’s exit status.
• You cannot kill zombie process directly because it is already dead.
• To kill the zombie process, you should kill the parent process. However, if the parent
process is init (i.e., 1), then only thing you can do is reboot.
• It takes very tiny memory(description info) but will take process ID which is limited.
So, it is better not to have zombie process.
Zombie Process
init(1)
F1(19)
Z or Z+ [Defunct]
(Died
process)
You can’t kill a zombie process(F1) because it’s already dead – like an actual zombie.
Zombie Process
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
// Fork returns process id // in parent process
pid_t child_pid = fork();
// Parent process
if (child_pid > 0)
sleep(50);
// Child process
else
exit(0);
return 0;
}
Zombie Process
int main() {
int pid ;
pid = fork(); /* Duplicate. Child and parent continue from here */
if ( pid != 0){
printf("Its a Parent Process with pid=%d, goes to sleep\n",getpid());
/* pid is non-zero, so I must be the parent */
while (1) /* Never terminate and never execute a wait ( ) */
sleep (10); /* stop executing for 10 seconds */
}
else{
printf("Child Process with pid = %d\n",getpid());
/* pid is zero, so I must be the child */
exit (1) ; /* exit with any number */
}
}
Getting Rid of Zombie Processes
• Send the signal with the kill command, replacing pid in the command below with the
parent process’s PID:
$ kill defunct-pid
• Still zombie process(defunct) present:
$ kill -9 defunct-pid(Force kill)
• Still zombie process(defunct) present:
$ kill parent-id-of-defunct-pid
If you still find defunct process eating up RAM then last and final solution is to reboot
your machine.
Orphan Process
int main()
{
/* Create a child process */
int pid = fork();
if (pid > 0)
printf("In parent process");
/* Note that pid is 0 in child process & negative if fork() fails */
else if (pid == 0){
sleep(30);
printf("In child process");
}
return 0;
}
Orphan Process Example
main()
{
int pid ;
printf("I'am the original process with PID %d and PPID %d.\n", getpid(), getppid()) ;
pid = fork ( ) ; /* Duplicate. Child and parent continue from here*/
if ( pid != 0 ) /* pid is non-zero, so I must be the parent*/
{
printf(“I’m the parent with PID %d and PPID %d.\n", getpid(), getppid()) ;
printf("My child's PID is %d\n", pid ) ;
}
else /* pid is zero, so I must be the child */
{
sleep(4); /* make sure that the parent terminates first */
printf("I'm the child with PID %d and PPID %d.\n", getpid(), getppid()) ;
}
printf ("PID %d terminates.\n", getpid()) ;
}
Orphan Process
Output:
I'am the original process with PID 2219 and PPID 1754.
I'm the parent with PID 2219 and PPID 1754.
My child's PID is 2220
PID 2219 terminates.
After 4 seconds
I'm the child with PID 2220 and PPID 1.
PID 2220 terminates.
Orphan Process Example
main()
{
int pid ;
printf("I'am the original process with PID %d and PPID %d.\n", getpid(), getppid()) ;
pid = fork ( ) ; /* Duplicate. Child and parent continue from here*/
if ( pid != 0 ) /* pid is non-zero, so I must be the parent*/
{
printf(“I’m the parent with PID %d and PPID %d.\n", getpid(), getppid()) ;
printf("My child's PID is %d\n", pid ) ;
sleep(10); /* make sure that the parent terminates first */
}
else /* pid is zero, so I must be the child */
{
sleep(20); /* make sure that the parent terminates first */
printf("I'm the child with PID %d and PPID %d.\n", getpid(), getppid()) ;
}
printf ("PID %d terminates.\n", getpid()) ;
}
vfork() System call Example
int main() {
Output:
pid_t pid = vfork(); //creating the child process
if (pid == 0) //if this is a child process Child process started
{ finished process
printf("Child process started\n"); Now I’m coming back to parent process
}
else //parent process execution
finished process
{
printf("Now I’m coming back to parent
process\n");
}
printf("finished process”);
return 0;
}
Corresponding fork() Example
int main() {
Output:
pid_t pid = fork(); //creating the child process
Now I’m coming back to parent process
if (pid == 0) //if this is a child process
finished process
{
printf("Child process started\n"); Child process started
} finished process
else //parent process execution
{
printf("Now I’m coming back to parent
process\n");
}
printf("finished process”);
return 0;
}
fork() comparison with vfork()
System call create a new process and blocks the parent process.
BASIS FOR
FORK() VFORK()
COMPARISON
Basic Child process and parent process has separate Child process and parent process shares the same address
address spaces. space.
Execution Parent and child process execute Parent process remains suspended till child process
simultaneously. completes its execution.
Modification If the child process alters any page in the If child process alters any page in the address space, it is
address space, it is invisible to the parent visible to the parent process as they share the same address
process as the address space are separate. space.
Copy-on-write fork() uses copy-on-write as an alternative vfork() does not use copy-on-write.
where the parent and child shares same pages
until any one of them modifies the shared
page.
Outcome of Usage Behavior is predictable Behavior is not predictable
Pipes (Byte Stream)
Shell Command
$ ls | wc –l
to execute the above command, the shell creates two processes, executing ls and wc,
respectively using fork() and exec() system calls.
two processes are connected to the pipe so that the writing process (ls) has its
standard output (file descriptor 1) joined to the write end of the pipe, while the
reading process (wc) has its standard input (file descriptor 0) joined to the read end
of the pipe.
Pipes are unidirectional
Creating and Using Pipes
#include <unistd.h>
int pipe(int filedes[2]);
Returns 0 on success, or –1 on error
A successful call to pipe() returns two open file descriptors in the array filedes: one for the read end of
the pipe ( filedes[0]) and one for the write end ( filedes[1]).
we can use the read() and write() system calls to perform I/O on the pipe.
File descriptor
filedes:
one for the read end of the pipe ( filedes[0]) and
one for the write end ( filedes[1]).
Pipe between parent and child process
int main()
{
int filedes[2];
if (pipe(filedes) == -1) printf("error creating pipe \n");
else
printf("Pipe Created Successfully\nfiledes[0] = %d,filedes[1] = %d\n",filedes[0],filedes[1]);
switch (fork()) { /* Create a child process */
case -1:
printf("fork failed \n");
case 0: /* Child */ $ ./a.out
printf("Child Process.......%d\n",getpid());
if (close(filedes[1]) == -1) printf("failed to close \n");
Pipe Created Successfully
else filedes[0] = 3,filedes[1] = 4
printf("Closed write end, read file descriptor = %d\n",filedes[0]);
break; Child Process.......1966
default: /* Parent */ Closed write end, read file descriptor = 3
wait(NULL);
Parent Process.......1965
printf("Parent Process.......%d\n",getpid());
if (close(filedes[0]) == -1) printf("failed to close \n"); Closed read end, write file descriptor = 4
else
printf("Closed read end, write file descriptor = %d\n",filedes[1]);
break; } }
Using a pipe to communicate between a parent and child process
Int main(int argc, char *argv[]) if (numRead == -1)
{ printf("read error\n");
int pfd[2]; if (numRead == 0)
char buf[BUF_SIZE]; break; /* End-of-file */
ssize_t numRead; if (write(STDOUT, buf, numRead) != numRead)
if (argc != 2 || strcmp(argv[1], "--help") == 0) printf("child - partial/failed write\n");
printf("%s %s \n", argv[0],argv[1]); write(STDOUT, "\n", 1);
if (pipe(pfd) == -1) if (close(pfd[0]) == -1)
printf("failed to create pipe\n"); printf("failed to close");
exit(EXIT_SUCCESS);
else
default:
printf("Pipe created successfully\n
read file des = %d, write file des = %d\ /* Parent- write to pipe */
n",pfd[0],pfd[1]); printf("Parent continues %d\n",getpid());
switch (fork()) { if (close(pfd[0]) == -1)/* Read end is unused */
case -1: printf("failed to close read end in - parent\n");
printf("failed to fork\n"); if(write(pfd[1],argv[1],strlen(argv[1]))!=strlen(argv[1]))
printf("parent - partial/failed write\n");
case 0:
else
/* Child - reads from pipe */
printf("Parent successfully write to pipe (%d): %s\
printf("ChildCreated.........%d\n",getpid());
n",pfd[1],argv[1]);
numRead = read(pfd[0], buf, BUF_SIZE); if (close(pfd[1]) == -1)
printf("text read from pipe by child : %s , printf("failed to close");
numRead = %d\n",buf,numRead); wait(NULL);
exit(EXIT_SUCCESS);
}
}
Usage of Pipes to execute
commands ls|wc
• ls | wc
o Create a pipe in the parent
o Fork a child
o Duplicate the standard output descriptor to write end of pipe
o Exec ‘ls’ program
o In the parent wait for the child.
o Duplicate the standard input descriptor to read end of pipe
o Exec ‘wc’ program
ls|wc command using pipe
int main(){
int fd[2]; // array of 2 size fd[0] is for reading and fd[1] is for writing over a pipe
pipe(fd);
if(!fork())
{
close(1); // closing normal stdout
dup(fd[1]); // making stdout same as fd[1]
close(fd[0]); // closing reading part of pipe
execlp("ls","ls",NULL);
}
else
{
close(0); // closing normal stdin
dup(fd[0]); // making stdin same as fd[0]
close(fd[1]); File descriptor Abbreviation
execlp("wc","wc",NULL);
}
0 Stdin
} 1 Stdout
2 Stderr
Creating a pipe using mkfifo()
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int res;
char Path= “~/Desktop/Tutorial9/fifo”;
res =mkfifo(Path , 0777);
printf("Pipe Created Successfully\n");
}
//Sender side with write system call //Reader side with read system call
//Reader side with read system call //Sender side with write system call
//Blocking MODE //Blocking MODE
//User2: Reader //User1: Reader
// Send.c //Receiver.c
int main() int main()
{ {
int fd; int fd;
char * myfifo = "~/Desktop/Tutorial9/fifo"; char * myfifo = "~/Desktop/Tutorial9/fifo";
mkfifo(myfifo, 0777); mkfifo(myfifo, 0777);
printf("Name pipe create\n"); printf("Name pipe create\n");
char arr1[80], arr2[80]; char arr1[80], arr2[80];
while (1) { while (1) {
fd = open(myfifo, O_WRONLY); // Open FIFO for write fd1 = open(myfifo,O_RDONLY); // Open FIFO for read only
only
read(fd1, str1, 80);
fgets(arr2, 80, stdin); printf("User1: %s\n", str1);
write(fd, arr2, strlen(arr2)+1); close(fd1);
close(fd); fd1 = open(myfifo,O_WRONLY); // Open FIFO for write only
fd = open(myfifo, O_RDONLY); // Open FIFO for read
fgets(str2, 80, stdin);
only
read(fd, arr1, sizeof(arr1)); write(fd1, str2, strlen(str2)+1);
printf("User2: %s\n", arr1); close(fd1); }
close(fd); } return 0;
return 0; }
}
Message Queues
• Data transfer happens through fixed Message Queue
length messages. Item
Next
• Messages are delimited.
Type
• Messages are transferred in its entirety.
• Transfer of partial message is not Size
allowed.
message
Structure of Message Queue struct msg {
struct msg *msg_next; /* pointer to next message on q */
System Message long msg_type; /* message type */
Permission Structure ushort msg_ts; /* message text size */
Queue Structure short msg_spot; /* message text map address */
First Message };
Last Message
.
.