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

System Calls

Uploaded by

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

System Calls

Uploaded by

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

UNIX File System Calls

AGENDA
• Operating System Modes
• File System Calls
• File Descriptor
• File Permissions
• File System Calls with Programs
Operating System Modes
User Mode MODE bit = 1

User Process Return system


Get System Call
Executing Call

1 1
0 0
Modes of Operating System Execute System
Call

Kernel Mode MODE bit = 0


System Calls
It is a programmatic way in which a computer program requests
a service from the kernel of the operating system.

It provides an interface between a process and operating system


to allow user-level processes to request services of the operating
system.
System calls are the only entry points into the kernel system.
File Descriptor
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>
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_NONBLOCK 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_NOFOLLOW Do not follow symlinks
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) return -1; lseek system call.
char buffer[19]; abc@UNIX :~/OS_TUT$ ./lseek
if(read(file,buffer,19) != 19) return -1; This is a text file
printf("%s\n",buffer); text file that will
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.:
usr
• link ("/usr/src/uts/sys", "/usr/include/sys");
• 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”
sys realfile.h
“/usr/include/sys/testfile.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];

if (getcwd(dirname, PATH_MAX) == NULL)


{ printf(Could not obtain current working directory.\n");
exit(1); }
else { printf("Current working directory: %s\n", dirname); }
return 0;
}
getcwd() system call Example 2
#include <stdlib.h>
#include <unistd.h>
#include < errno.h >

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.
ENAMETOOLONG path is too long.
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.
ENAMETOOLONG pathname was too long.

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.
ENAMETOOLONG pathname was too long.
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.
– Note: add # include<dirent.h>
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 bombs) #include <stdio.h>
can eventually lead to resource exhaustion and
may collapse the system. #include <sys/types.h>
Program to demonstrate repeated forking int main()
int main(void){ {
int cnt=0; fork();
int pid; fork();
for(cnt=0;cnt<3;cnt++){
fork();
pid=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

In the first iteration, only the parent


process exists and it creates a single
child, C1.
In the second iteration both P and C1
exist and each fork to give C3 and C2
respectively
In the last iteration P,C1,C2,C3 exist and
each fork to give C7,C6,C4,C5
respectively.
Multiple Fork System Call
(Outputs)

S.no Fork() system call in Print output Parent process Child Process
. 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

1 l execl Execute file with arguments explicitly in call

2 v execv Execute file with argument vector

3 p PATH environment variable

4 e Environment pointer
5 l p execlp Execute file with arguments explicitly in call and PATH search

6 v p execvp Execute file with argument vector and PATH search

7 l e execle Execute file with argument list and manually passed environment pointer

8 v e execve Execute file with argument vector and manually passed environment pointer
exec() System Call family syntax
int execl (
const char *path, /* Program pathname*/
const char *arg0, /*First Argument(filename) */
const char *arg1, ……
(char *) NULL /* Arg list terminator */
);
int execle ( int execlp (
const char *path, /* Program pathname */ const char *file, /* Program filename */
const char *arg0, /*First Argument(filename) */ const char *arg0, /*First Argument(filename) */
const char *arg1, const char *arg1,
… …
(char *) NULL, /* Arg list terminator */ (char *) NULL /* Arg list terminator */
char *const envv[] /* Environment vector */
);
);
exec() System Call family syntax
int execv (
const char *path, /* Program pathname */
char* const argv[] /* Argument vector */
);

int execve ( int execvp (


const char *path, /* Program pathname */ const char *file, /* Program filename */
char *const argv[], /* Argument vector */ char* const argv[] /* Argument vector */
char *const envv[] /* Environment vector */ );
);
1. execl() System Call
⚫It execute file with arguments explicitly in call.
⚫Syntax
int execl (
const char *path, /* Complete Program pathname */
const char *arg0, /* First Argument(filename) */
const char *arg1, /* Second Argument(optional) */
… /* Remaining Arguments (if any) */
(char *) NULL /* Arg list terminator */
);
/* Returns -1 on error (sets errno) */
execl() System Call
⚫After the call to execl() the context of the process is overwritten.
⚫Previous code is replaced by the code/instructions of the executable in ‘path’.
⚫User data is also replaced with the data of the program in ‘path’ thereby
reinitializing the stack.
⚫And the new program begins to execute from its main function.
⚫New program accesses the arguments of new program which are mentioned in
execl() through its ‘argc’ and ‘argv’ arguments of the main function.
⚫Environment pointed to by ‘environ’ is also passed to the new program.
Return of execl() System Call
⚫Recall that the return address of any function is saved in the stack.
⚫The return address is popped from the stack while a function returns.
⚫But here the stack is re-initialized with the data of the new program and the old
program’s data is lost.
⚫So there is no way to pop the return address and hence there is no way to return
from execl() call if the call is successful.
execl() example to invoke user executable
sum.c TEST.C

int main(int argc,char *argv[]) int main()


{ {
int sum=0; execl("./sum","sum","100","200","300",(char
*)NULL);
int i; printf("execl call unsuccessful\n");
if(argc != 4) }
{
printf("invalid argument\n");
exit(0);} Output:-
for(i=0;i<argc;i++) $ gcc –o sum sum.c
sum = sum + $ gcc –o TEST TEST.c
atoi(argv[i]); $ ./TEST
printf("sum = %d\n",sum); sum = 600
}
execl() Example to invoke UNIX commands

# include<stdio.h>
# include<unistd.h>
int main(int argc, char ** argv){
printf("Hello World!");
execl("/bin/echo","echo","Print","from","execl",NULL);
return 0;
}

Output:- Print from execl


In the above program “Hello World!” is not printed
Reason:
Printf() function in C does not immediately prints the data on stdout but it buffers it till the next
printf() statement or program exit.
execl() System call Example
# include <unistd.h>
int main(void)
{
char *binaryPath = "/bin/ls";
char *arg1 = "-lh";
char *arg2 = “ /home ”;
execl(binaryPath, binaryPath, arg1, arg2, NULL);
return 0;
}

In execl() system function takes the path of the executable binary file (i.e. /bin/ls) as the
first and second argument. Then, the arguments (i.e. -lh, /home) that you want to pass to
the executable followed by NULL. Then execl() system function runs the command and
prints the output. If any error occurs, then execl() returns -1. Otherwise, it returns
nothing.
Other exec system calls

• 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};

execle(binaryPath, binaryPath,arg1, arg2, NULL, env);


return 0;
}

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};

execve(binaryPath, args, env);


return 0;
}

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!\n");
wait(&status); /* parent waits for child to 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") ; exit(1);
int main(){
}
pid_t whichone, first, second ; int howmany, status ;
printf("This is parent\n");
if((first = fork()) == 0) /* Parent spawns 1st child */ {
howmany = 0;
printf("I am the first child, & my ID is %d\n", getpid());
while(howmany < 2) {/* Wait Twice */
sleep(10); exit(0); }
whichone = wait(&status);
else if(first == -1) {
howmany++;
perror("1st fork: something went wrong\n") ; exit(1); }
if(whichone == first)
else if((second = fork()) == 0) /* Parent spawns 2nd child
printf("First child exited\ncorrectly");
*/ {
else
printf("I am the second child, & my ID is %d\n", getpid( ));
printf("Second child exited\ncorrectly");}}
sleep(15); exit(0);
}
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)

bash(8) parent_of_F1(5) xyz(9)

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() {
pid_t pid = vfork(); //creating the child process Output:
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() {
pid_t pid = fork(); //creating the child process
Output:
if (pid == 0) //if this is a child process Now I’m coming back to parent 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());
Pipe Created Successfully
if (close(filedes[1]) == -1) printf("failed to close \n");
else printf("Closed write end, read file descriptor = %d\n",filedes[0]);
filedes[0] = 3,filedes[1] = 4
break; Child Process.......1966
default: /* Parent */ Closed write end, read file descriptor = 3
wait(NULL); Parent Process.......1965
printf("Parent Process.......%d\n",getpid()); Closed read end, write file descriptor = 4
if (close(filedes[0]) == -1) printf("failed to close \n");
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\n",pfd[0],pfd[1]); /* Parent- write to pipe */
switch (fork()) { printf("Parent continues %d\n",getpid());
case -1: if (close(pfd[0]) == -1)/* Read end is unused */
printf("failed to fork\n"); printf("failed to close read end in - parent\n");
if(write(pfd[1],argv[1],strlen(argv[1]))!=strlen(argv[1]))
case 0:
printf("parent - partial/failed write\n");
/* Child - reads from pipe */
else
printf("ChildCreated.........%d\n",getpid());
printf("Parent successfully write to pipe (%d):
numRead = read(pfd[0], buf, BUF_SIZE); %s\n",pfd[1],argv[1]);
printf("text read from pipe by child : %s , numRead if (close(pfd[1]) == -1)
= %d\n",buf,numRead);
printf("failed to close");
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 only fd1 = open(myfifo,O_RDONLY); // Open FIFO for read only
fgets(arr2, 80, stdin); read(fd1, str1, 80);
write(fd, arr2, strlen(arr2)+1); printf("User1: %s\n", str1);
close(fd); close(fd1);
fd = open(myfifo, O_RDONLY); // Open FIFO for read only fd1 = open(myfifo,O_WRONLY); // Open FIFO for write only
read(fd, arr1, sizeof(arr1)); fgets(str2, 80, stdin);
printf("User2: %s\n", arr1); write(fd1, str2, strlen(str2)+1);
close(fd); } close(fd1); }
return 0; return 0;
} }
Message Queues
• Data transfer happens through fixed Message Queue Item
length messages.
Next
• Messages are delimited.
Type
• Messages are transferred in its entirety.
Size
• Transfer of partial message is not
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
.
.

Message Queue Item Message Queue Item Message Queue Item


Next Next Next
Type Type Type
Size Size Size

message message message


System calls used for message queues
1. ftok(): is used to generate a unique key.
2. msgget(): either returns the message queue identifier for a newly created message
queue or returns the identifiers for a queue which exists with the same key value.
3. msgsnd(): Data is placed on to a message queue by calling msgsnd().
4. msgrcv(): messages are retrieved from a message queue.
5. msgctl(): It performs various operations on a queue. Generally, it is used to destroy
message queue.

You might also like