0% found this document useful (0 votes)
37 views32 pages

CS 4

networking programming lecture at bits

Uploaded by

jarison_mander
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
37 views32 pages

CS 4

networking programming lecture at bits

Uploaded by

jarison_mander
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 32

Vfork() System call

• The function vfork has the same calling sequence and same return values as fork.

• The vfork function is intended to create a new process when the purpose of the
new process is to exec a new program.

• The vfork function creates the new process, just like fork, without copying the
address space of the parent into the child, as the child won't reference that
address space; the child simply calls exec (or exit) right after the vfork.

• Instead, while the child is running and until it calls either exec or exit, the child
runs in the address space of the parent.

• Another difference between the two functions is that vfork guarantees that the
child runs first, until the child calls exec or exit. When the child calls either of
these functions, the parent resumes.
#include <unistd.h>
pid_t vfork(void);

In parent: returns process ID of child on success,


or –1 on error;
in successfully created child: always returns 0
Example 1:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
int pid;
pid=vfork();
if(pid==-1)
{
perror("vfork error"); exit(0);
}
if(pid==0)
{
printf("I am child process created using vfork system call and I execute first always\n");
_exit(0);
}
else
{
printf("I am parent executing after my child\n");
exit(0);
}
return 0;
}
Example 2:
int var=5;
int main() OutPut:
{ I am in child
int pid; My ID=2040
pid=vfork(); Value of var in my address space=6
if(pid==-1)
{ I am parent
perror("vfork error");
My id=2039 and my child pid=2040
exit(0);
} My parent id=2021
if(pid==0) Value of var in my address space=6
{
var++;
printf("I am child\n");
printf("My id=%d\n",getpid());
printf("Value of var in my address space=%d\n",var);
_exit(0);
}
else
{
printf("I am parent\n");
printf("My id=%d and my child pid=%d\n",getpid(),pid);
printf("My parent id=%d\n",getppid());
printf("Value of var in my address space=%d\n",var);
}
return 0; }
Race Condition
• A race condition occurs when multiple
processes are trying to do something with
shared data and the final outcome depends on
the order in which the processes run.
• The program below outputs two strings: one
from the child and one from the parent. The
program contains a race condition because the
output depends on the order in which the
processes are run by the kernel and for how
long each process runs.
Example on Race Condition:
static void charatatime(char *);
int main(void) Output:
{
$ ./a.out
pid_t pid;
if ((pid = fork()) < 0)
ooutput from child
{ utput from parent
err_sys("fork error");
} $ ./a.out
else if (pid == 0) ooutput from child
{ utput from parent
charatatime("output from child\n");
}
$ ./a.out
else
{
output from child
charatatime("output from parent\n"); output from parent
}
exit(0);
}
static void charatatime(char *str)
{
char *ptr; int c;
setbuf(stdout, NULL); /* set unbuffered */
for (ptr = str; (c = *ptr++) != 0; )
putc(c, stdout);
}
wait AND waitpid FUNCTIONS

#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
Both return: process ID if OK.

The differences between these two functions are as follows.

 The wait function can block the caller until any one of its child
process terminates first, whereas waitpid has an option that
prevents it from blocking.

 The waitpid function doesn't wait for the child that terminates
first; it has a number of options that control which process it waits
for.
The options constants for waitpid

• WCONTINUED: The status of any child specified by


pid that has been continued after being stopped, but
whose status has not yet been reported, is returned.

• WNOHANG : The waitpid function will not block if a


child specified by pid is not immediately available. In
this case, the return value is 0.
• WUNTRACED : The status of any child specified by
pid that has stopped, and whose status has not been
reported since it has stopped, is returned.
Macros to examine the termination status returned by wait and waitpid

• WIFEXITED(status) : True if status was returned for a child that terminated


normally. In this case, we can execute WEXITSTATUS (status) to fetch the
low-order 8 bits of the argument that the child passed to exit, _exit,or
_Exit.

• WIFSIGNALED (status) : True if status was returned for a child that


terminated abnormally, by receipt of a signal that it didn't catch. In this
case, we can execute WTERMSIG (status) to fetch the signal number that
caused the termination.

• WIFSTOPPED (status) : True if status was returned for a child that is


currently stopped. In this case, we can execute WSTOPSIG (status) to fetch
the signal number that caused the child to stop.

• WIFCONTINUED (status) : True if status was returned for a child that has
been continued after a job control stop.
Program to Demonstrate various exit statuses :
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); }
Void pr_exit(int status)

if (WIFEXITED(status))

printf("normal termination, exit status = %d\n", WEXITSTATUS(status));

else if (WIFSIGNALED(status))

printf("abnormal termination, signal number = %d%s\n", WTERMSIG(status);

else if (WIFSTOPPED(status))

printf("child stopped, signal number = %d\n", WSTOPSIG(status));

}
The waitpid function provides three features that aren't provided
by the wait function.

 The waitpid function lets us wait for one particular process,


whereas the wait function returns the status of any terminated
child.
 The waitpid function provides a non-blocking version of wait.
There are times when we want to fetch a child's status, but we
don't want to block.

 The waitpid function provides support for job control with the
WUNTRACED and WCONTINUED options.
Program to Avoid zombie processes by calling fork twice :
Int main(void)
{
pid_t pid;
if ((pid = fork()) < 0)
{
err_sys("fork error");
}
else if (pid == 0)
{
/* first child */
if ((pid = fork()) < 0)
err_sys("fork error");
else if (pid > 0)
exit(0);
/* parent from second fork == first child */
/* * We're the second child; our parent becomes init as soon * as our real parent calls exit() in the statement
above. * Here's where we'd continue executing, knowing that when * we're done, init will reap our status. */
sleep(2);
printf("second child, parent pid = %d\n", getppid());
exit(0);
}
if (waitpid(pid, NULL, 0) != pid) /* wait for first child */
err_sys("waitpid error");
/* * We're the parent (the original process); we continue executing, * knowing that we're not the parent of the
second child. */
exit(0);
}
Output: $ ./a.out
second child, parent pid = 1
wait3 AND wait4 FUNCTIONS
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.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);
Both return: process ID if OK,-1 on error
• The resource information includes such statistics as the amount of user
CPU time, the amount of system CPU time, number of page faults, number
of signals received etc.
• The resource information is available only for terminated child process not
for the process that were stopped due to job control.
Avoiding Race Condition Using Signals
static void charatatime(char *);
int main(void)
{ Static void charatatime(char *str)
pid_t pid; {
TELL_WAIT(); char *ptr; int c;
if ((pid = fork()) < 0) setbuf(stdout, NULL); /* set unbuffered */
{ for (ptr = str; (c = *ptr++) != 0; )
err_sys("fork error"); putc(c, stdout);
} }
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);
}
Program Execution
• When a process calls one of the exec
functions, that process is completely replaced
by the new program, and the new program
starts executing at its main function.
• The process ID does not change across an
exec, because a new process is not created;
exec merely replaces the current process - its
text, data, heap, and stack segments - with a
brand new program from disk.
6 exec functions:
#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 []);

All six return: -1 on error, no return on success.


Example on execl ()
First.C
int main()
{
int pid;
printf("Before execing : My Pid=%d\n”,getpid());
execl("/home/CSE/CS054/second","second",(char*)0)<0)
printf("This will not be printed\n");
return 0;
}
OutPut:
Second.c Before execing: My Pid=2041
#include<stdio.h> After execing My Pid=2041
#include<unistd.h>
int main()
{
printf("After execing ”);
printf("My pid=%d\n",getpid());
return 0;
}
Example on execl ()
First.C
Second.c
int main()
{
#include<stdio.h>
int pid; #include<unistd.h>
pid=vfork(); int main()
if(pid==-1) {
{ printf("After execing\n");
perror("fork error"); printf("My pid=%d\n",getpid());
exit(0); return 0;
}
}
if(pid==0)
{
printf("Before execing\n");
if(execl("/home/CSE/CS054/second","second",(char*)0)<0)
{
perror("execl error");
exit(0);
}
_exit(0);
}
else
printf(“Parent prints and this will not be printed by child\n");
return 0;
}
Example on execle () and execlp()
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/sar/bin/echoall", "echoall", "myarg1", "MY ARG2", (char *)0, env_init) < 0)
err_sys("execle error");
} Output:
if (waitpid(pid, NULL, 0) < 0) $ ./a.out
err_sys("wait error"); argv[0]: echoall
if ((pid = fork()) < 0) argv[1]: myarg1
{ argv[2]: MY ARG2
err_sys("fork error"); USER=unknown
} PATH=/tmp
else if (pid == 0) { /* specify filename, inherit environment */ argv[0]: echoall
if (execlp("echoall", "echoall", "only 1 arg", (char *)0) < 0) argv[1]: only 1 arg
err_sys("execlp error"); USER=sar
} LOGNAME=sar
exit(0); SHELL=/bin/bash
} 47 more lines that aren't shown
HOME=/home/sar
Process Termination
• Terminating a Process: _exit() and exit()
-- A process may terminate in two general ways.
1. Abnormal termination: caused by the
delivery of a signal whose default action is to
terminate the process
2. Normal Termination: caused using the
_exit() system call.
_exit()
#include <unistd.h>
void _exit(int status);
• The status argument given to _exit() defines the
termination status of the process, which is available to the
parent of this process when it calls wait().
• Although defined as an int, only the bottom 8 bits of
status are actually made available to the parent.
• By convention, a termination status of 0 indicates that a
process completed successfully, and a nonzero status
value indicates that the process terminated
unsuccessfully.
exit()
• Programs generally don’t call _exit() directly, but instead call the
exit() library function, which performs various actions before calling
_exit().

#include <stdlib.h>
void exit(int status);

The following actions are performed by exit():


• Exit handlers (functions registered with atexit() and on_exit()) are
called, in reverse order of their registration
• The stdio stream buffers are flushed.
• The _exit() system call is invoked, using the value supplied in status.
Exit handlers
• Sometimes, an application needs to automatically
perform some operations on process termination.
• An exit handler is a programmer-supplied function
that is registered at some point during the life of
the process and is then automatically called during
normal process termination via exit().
• Exit handlers are not called if a program calls
_exit() directly or if the process is terminated
abnormally by a signal.
Registering exit handlers
There are two ways of registering exit handlers.
• The first method, is by using the atexit() function.
#include <stdlib.h>
int atexit(void (*func)(void));
Returns 0 on success, or nonzero on error

• The atexit() function adds func to a list of functions that are called when the process
terminates.
• The function func should be defined to take no arguments and return no value, thus having
the following general form:

void
func(void)
{
/* Perform some actions */
}

• It is possible to register multiple exit handlers (and even the same exit handler multiple times).
Limitations of atexit()
• The first is that when called, an exit handler
doesn’t know what status was passed to exit().
Occasionally, knowing the status could be useful
for example, we may like to perform different
actions depending on whether the process is exiting
successfully or unsuccessfully.
• The second limitation is that we can’t specify an
argument to the exit handler when it is called.
On_exit()
• To address the limitations of atexit(), there is an alternative method of registering
exit handlers: using on_exit().

#include <stdlib.h>
int on_exit(void (*func)(int, void *), void *arg);
Returns 0 on success, or nonzero on error

• The func argument of on_exit() is a pointer to a function of the following type:


void
func(int status, void *arg)
{
/* Perform cleanup actions */
}

• func() is passed with two arguments:


 the status argument supplied to exit(), and a copy of the arg argument supplied
to on_exit() at the time the function was registered. arg could be used as a pointer to
some structure;
Example on using atexit() and on_exit():
static void atexitFunc1(void) $ ./exit_handlers
{ on_exit function called: status=2, arg=20
printf("atexit function 1 called\n"); atexit function 2 called
} atexit function 1 called
static void atexitFunc2(void)
on_exit function called: status=2, arg=10
{
printf("atexit function 2 called\n");
}
static void onexitFunc(int exitStatus, void *arg)
{
printf("on_exit function called: status=%d, arg=%ld\n",
exitStatus, (long) arg);
}
Int main(int argc, char *argv[])
{
if (on_exit(onexitFunc, (void *) 10) != 0)
Perror("on_exit 1");
if (atexit(atexitFunc1) != 0)
Perror("atexit 1");
if (atexit(atexitFunc2) != 0)
Perror("atexit 2");
if (on_exit(onexitFunc, (void *) 20) != 0)
Perror("on_exit 2");
exit(2);
}
File Sharing Between Parent and Child

You might also like