Process Control: Prepared by Chandrika Prasad Dept of CSE RIT
Process Control: Prepared by Chandrika Prasad Dept of CSE RIT
Prepared By
Chandrika Prasad
Dept of CSE
RIT
• The child can always call getppid to obtain the process ID of its
parent.
• The reason the child’s process ID is returned
to the parent is that → a process can have
more than one child, and there is no function
that allows a process to obtain the process IDs
of its children.
wait and waitpid both return two values: the return value of
the function is the process ID of the terminated child,
and the termination status of the child (an integer) is
returned through the statloc pointer.
The differences between these two functions
are as follows:
• The wait function can block the caller until a
child process terminates, 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.
• waitpid supports job control.
• waitpid lets us wait for one particular process.
If we have more than one child, wait returns on
termination of any of the children.
5. what if we want to wait for a specific process
to terminate (assuming we know which process
ID we want to wait for)?
Ans: In older versions of the UNIX System, we
would have to call wait and compare the
returned process ID with the one we’re
interested in.
If the terminated process wasn’t the one we
wanted, we would have to save the process ID
and termination status and call wait again.
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);
}
$ ./a.out
normal termination, exit status = 7
abnormal termination, signal number = 6
(core file generated)
abnormal termination, signal number = 8
(core file generated
Print a description of the exit status
#include "apue.h"
#include <sys/wait.h>
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),
#ifdef WCOREDUMP
WCOREDUMP(status) ? " (core file generated)" : "");
#else
"");
#endif
else if (WIFSTOPPED(status))
printf("child stopped, signal number = %d\n",
WSTOPSIG(status));
}
Note: if we have more than one child, wait returns on
termination of any of the children. This limitation can
be overcome by using waitpid.
The options constants for waitpid
Calculate number of times hello is printed.
#include <stdio.h>
#include <sys/types.h>
int main()
{
fork();
fork();
fork();
printf("hello\n");
return 0;
}
TODO
#include <stdio.h>
#include <unistd.h>
int main()
{ if (fork()) {
if (!fork()) {
fork();
printf("1 ");
} else {
printf("2 ");
}
} else {
printf("3 ");
}
printf("4 ");
return 0;
}
Explanation:
1. It will create two process one parent P (has process ID
of child process) and other is child C1 (process ID = 0).
arl@arl-Lenovo-H30-50:~/usp_2018_arl$ ./race_condition
output froomu tppaurte nftr
om child
exec()
In computing, exec is a functionality of an operating
system that runs an executable file in the context
of an already existing process, replacing the
previous executable. This act is also referred to as
an overlay.
• 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.
• There are six different exec functions, but we’ll
often simply refer to ‘‘the exec function’’.
int execl(const char *pathname, const char
*arg0, ... /* (char *)0 */ );
• The first difference in these functions is that the first four take
a pathname argument, the next two take a filename
argument.
Note: (2) The real user ID and the real group ID remain the same across the
exec, but the effective IDs can change, depending on the status of the set-
user-ID and the set- group-ID bits for the program file that is executed.
If the set-user-ID bit is set for the new program, the effective user ID
becomes the owner ID of the program file.
Otherwise, the effective user ID is not changed (it's not set to the real user
ID).
The group ID is handled in the same way.
Relationship among all exec()
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");
}
if (waitpid(pid, NULL, 0) < 0)
err_sys("wait error");
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) { /* specify filename, inherit environment */
if (execlp("echoall", "echoall", "only 1 arg", (char *)0) < 0)
err_sys("execlp error");
}
exit(0);
}
echoall program
int main(int argc, char *argv[])
{
int i; char **ptr;
extern char **environ;
for (i = 0; i < argc; i++) /* echo all command-line args */
printf("argv[%d]: %s\n", i, argv[i]);
for (ptr = environ; *ptr != 0; ptr++) /* and all env strings */
printf("%s\n", *ptr);
exit(0);
}
output
argv[0]: echoall
argv[1]: myarg1
argv[2]: MY ARG2
USER=unknown
PATH=/tmp
$ argv[0]: echoall
argv[1]: only 1 arg
USER=sar
LOGNAME=sar
SHELL=/bin/bash
47 more lines that aren’t shown
HOME=/home/sar
Changing User IDs and Group IDs
echoarg.c file
int main(int argc,char *argv[])
{ int i;
for(i=0;i<argc;i++)
printf("argv[%d]=%s",i,argv[i]);
return(0);
}
How to compile the file
cc echoarg.c -o echoarg
1. They hide that certain programs are scripts in some other language.
For example, to execute an awk script, we just say
awkexample optional-arguments
instead of needing to know that the program is really an awk script
that we would otherwise have to execute as
awk -f awkexample optional-arguments
3. Interpreter scripts let us write shell scripts using shells other than
/bin/sh. When it finds an executable file that isn't a machine
executable, execlp has to choose a shell to invoke, and it always uses
/bin/sh. Using an interpreter script, however, we can simply write
#!/bin/csh
(C shell script follows in the interpreter file)
system()
The C library function
int system(const char *command)
passes the command name or program name specified
by command to the host environment to be
executed by the command processor and returns
after the command has been completed.
Because system is implemented by calling fork, exec, and
waitpid, there are three types of return values.
1. If either the fork fails or waitpid returns an error other than
EINTR (EINTR is one of the POSIX errors that you can get from
different blocking functions ), system returns −1 with errno
set to indicate the error.
2. If the exec fails, implying that the shell can’t be executed, the
return value is as if the shell had executed exit(127).
3. Otherwise, all three functions—fork, exec, and waitpid—
succeed, and the return value from system is the termination
status of the shell, in the format specified for waitpid.
Example
#include <sys/wait.h>
int main(void)
{ int status;
if ((status = system("date")) < 0)
err_sys("system() error");
pr_exit(status);
if ((status = system("nosuchcommand")) < 0)
err_sys("system() error");
pr_exit(status);
if ((status = system("who; exit 44")) < 0)
err_sys("system() error");
pr_exit(status);
exit(0);
}
Process Accounting
• When process accounting is enabled, the kernel writes an
accounting record each time a process terminates.
• These accounting records are typically a small
amount of binary data with
the name of the command,
the amount of CPU time used,
the user ID and group ID,
the starting time, and so on.
• A superuser executes accton with a pathname argument to
enable accounting.
• The accounting records are written to the specified file, which
is usually /var/account/acct on FreeBSD
and Mac OS X, /var/account/pacct on Linux, and /var/adm/pacct
on Solaris.
• Accounting is turned off by executing accton without any
arguments..
• The data required for the accounting record, such as CPU times and
number of characters transferred, is kept by the kernel in the process
table and initialized whenever a new process is created, as in the
child after a fork.
• Each accounting record is written when the process terminates.
• This means that the order of the records in the accounting file
corresponds to the termination order of the processes, not the order
in which they were started.
• The accounting records correspond to processes, not programs. A
new record is initialized by the kernel for the child after a fork, not
when a new program is executed. Although exec doesn't create a
new accounting record, the command name changes, and the
AFORK flag is cleared.
• This means that if we have a chain of three programs A execs B,
then B execs C, and C exits only a single accounting record is
written.
• The command name in the record corresponds to program C, but the
CPU times, for example, are the sum for programs A, B, and C.
User Identification
Process Times
We can measure three kinds of times: wall clock time, user CPU
time, and system CPU time. A process can call the times function to
obtain these values for itself and any terminated children.
The function returns the wall clock time as the value
of the function, each time it's called.
This value is measured from some arbitrary point in
the past, so we can't use its absolute value; instead,
we use its relative value.
For example, we call times and save the return
value. At some later time, we call times again and
subtract the earlier return value from the new return
value. The difference is the wall clock time.