Transcript Parent

Shell
Process
r A process is an instance of an application
running
r If there are two instances of an
application running then there are two
processes
r Example: Let us say there are two users on
gaul
m
m
Both run grep on a file
This results in two processes
Process
A process is more than the program code
(sometimes known as text section)
Process information includes:
Process stack: Includes temporary data
(function parameters, return addresses and
local variables)
Data Section: Includes global variables
Heap: Memory that is dynamically allocated
during process run-time
Process in Memory
Process State
As a process executes it changes state.
Possible states:
New: The process is created
Running: Instructions are being executed
Waiting: The process is waiting for some event
to occur e.g., I/O completion
Terminated: Process has finished execution
Process Control Block
Each process is represented in the
operating system by a process control
block
Information related to program execution
Context information including:
• Process Identifier (PID)
• Process state
• Program counter and other program related
information
• CPU registers
• CPU-Scheduling information
• Memory-Management information
• I/O status information
Operating System Services
An operating system provides an
environment for the execution of programs
Services are provided to programs and
users of programs
Operating System Services
OS services (User perspective)
User interface: Command-Line (CLI), Graphics
User Interface (GUI)
Program execution: OS needs to load a program
into memory, runs that program, end execution,
either normally or abnormally (indicating error)
I/O operations: A running program may require
I/O, which may involve a file or an I/O device
File-system manipulation: Need to read and
write files and directories, create and delete
them, search
Operating System Services
OS provides operations for ensuring
efficient operation. Examples:
Resource allocation
Accounting
Protection and Security
The Shell
The command interpreter is a program that
accepts commands from the user and interprets
the commands (type of user interface)
The shell refers to an executing command
interpreter
Accepts commands such as “ls” or “ps”
The commands make use of system calls.
A system call allows a command to request a
service from the operating system.
A shell is a process
A process e.g., a program being executed
• We will overuse the term “shell” to refer to the program as well as
the process
The Shell
There are different shells that you can use
in a Unix-based system including:
bourne shell
C shell
bash shell
tcsh shell
and many more
The Shell
When a user logs in, a shell is started up.
The shell has the terminal as standard
input and standard output
The shell starts out by typing the prompt,
a character such as a dollar sign or
percentage sign e.g.,
hanan%
User enters a command e.g.,
date
The Shell
The user can specify that standard output
be redirected to a file e.g.,
date > file
Standard input can be redirected e.g.,
sort < file1 > file2
The output of one program can be used as
the input to another program:
cat file1 file2 file3 | sort >/dev/lp &
High-Level View of Shell Code
while (1) {
get a line from the user
Execute command found in
line
}
Details Not Highlighted
Making sure that the line
from the user is correct
How is shell termination
handled?
The execution of a
command is done by a
separate process (child
process) from the shell
process
For simple commands, the
shell process waits for the
child process to terminate
so that it can print the
prompt
If a child process is put in
the background (using &)
then the shell process can
continue without waiting
for the child process to
terminate
The Concept of Fork
The Unix system call for process creation is called
fork().
The fork system call creates a child process that
is a duplicate of the parent.
Child inherits state from parent process
• Same program instructions, variables have the same values,
same position in the code
Parent and child have separate copies of that state
Child has the same open file descriptors from the parent.
• Parent and child file descriptors point to a common entry in
the system open file descriptor table. More on this later
fork() as a diagram
Parent
pid = fork()
Returns a new PID:
e.g. pid == 56
Data
Child
pid == 0
Shared
Program
Data
Copied
Process Creation Using Fork
int main ()
{
pid_t pid;
int status = 0;
pid = fork();
if (pid < 0)
perror(“fork()”);
if (pid > 0) {
/* parent */
printf(“I am parent\n”);
wait(0);
} else {
/* child */
printf(“I am child\n”);
}
return 0;
}
The fork system call returns
twice: it returns a zero to the
child and the child process ID
(pid) to the parent.
The perror function produces
a message on the standard
error output describing the
last error encountered during
a call to a system or library
function (man page)
The wait function is used to
terminate the parent process
when the child terminates
pid is zero which indicates a
child process
Fork System Call
If fork () succeeds it returns the child PID to the
parent and returns 0 to the child
If fork() fails, it returns -1 to the parent (no child
is created) and sets errno
A program almost always uses this difference to
do different things in the parent and child
processes.
Failure occurs when the limit of processes that
can be created is reached.
pid_t data type represents process identifiers
Other calls:
pid_t getpid() – returns the PID of calling process
Pid_t getppid() – returns the PID of parent process
fork() Example 1
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
int i;
sum = 0;
pid = fork();
if( pid > 0 )
}
{
/* parent */
for( i=0; i < 10; i++ )
sum = sum + i;
printf(“parent: sum is %\n”,sum);
wait(0);
}
else
{
/* child */
for( i=0; i < 10; i++ )
sum = sum - i;
printf(“child: sum is %d\n”,sum);
}
return 0;
fork() Example 1
What is the value of sum in the parent and
child processes after pid = fork()?
0
What is the value of sum in the parent and
child processes at the print statements?
parent: sum is 45
child: sum is -45
fork() Example 1
Remember that sum was 0 before the fork
took place
When the fork took place the process was
duplicated which means that a copy is made
of each variable; sum was duplicated
Since sum was 0 just before the fork then
sum is 0 right after the fork in both the
child and parent processes
fork() Example 2
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
int i;
pid = fork();
if( pid > 0 )
{
/* parent */
for( i=0; i < 1000; i++ )
printf(“\t\t\tPARENT %d\n”, i);
}
wait(0);
What is the possible output?
else
{
/* child */
for( i=0; i < 1000; i++ ) {
printf( “CHILD %d\n”, i );
}
}
return 0;
}
fork () Example 2:Possible
Output
PARENT 0
PARENT
PARENT
PARENT
PARENT
PARENT
PARENT
PARENT
PARENT
PARENT
CHILD
CHILD
CHILD
CHILD
CHILD
CHILD
CHILD
CHILD
CHILD
CHILD
0
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
fork () Example 2:Possible
Output
PARENT
PARENT
PARENT
PARENT
PARENT
PARENT
PARENT
CHILD 0
CHILD 1
CHILD 2
CHILD
CHILD
CHILD
CHILD
CHILD
CHILD
CHILD
0
1
2
3
4
5
6
PARENT 7
PARENT 8
PARENT 9
3
4
5
6
7
8
9
Lots of possible outputs!!
Execution
Processes get a share of the CPU before
giving it up to give another process a turn
The switching between the parent and
child depends on many factors:
machine load, system process scheduling
Output interleaving is nondeterministic
Cannot determine output by looking at code
How many Processes are
Created by this Program?
#include <stdio.h>
#include <unistd.h>
int main()
{
fork();
fork();
fork();
}
Process Structure?
What does the
process structure
look like?
Is this a tree or a
chain of processes
pid_t childpid = 0;
for (i=1;i<n;i++)
if (childpid = fork())
break;
Process Structure?
pid_t childpid = 0;
What does the
process structure
look like?
for (i=1;i<n;i++)
Is this a tree or a
if (childpid = fork() <= 0)
chain of processes? break;
Process File Descriptor Table
Every process has a process file descriptor
table
Each entry represents something that can
be read from or written to e.g.,
file
Screen
pipe (later)
System File Descriptor Table
The OS maintains a system file descriptor
table in the OS's memory space.
Every open file in the system has an entry in
this table.
One file can be associated with many entries in
this table (if opened by many processes).
These entries contain among other things:
• the file descriptor's permissions
• # links
• the file offset which is moved by read and write.
An entry is removed when 0 links point to it.
OS File Structures
0
1
2
3
4
stdin
stdout
stderr
in_file
File info e.g.,
read offset
Parent File Descriptor table
Assume that there was something
like this in program
System file table
FILE *in_file;
in_file = fopen("list.txt", "r");
Fork and Files
In a fork what gets copied is the file
descriptor table;
Thus the parent and child point to the
same entry in the system file table
Fork and Files
0
1
2
3
4
stdin
stdout
stderr
in_file
File info e.g.,
read offset
Parent File Descriptor table
0
1
2
3
4
stdin
stdout
stderr
in_file
Child File Descriptor table
System file table
Fork and Files
Open a file before a fork
The child process gets a copy of its parent's
file descriptor table.
The child and parent share a file offset
pointer because the open came before the fork.
Open a file after a fork
Assume that parent and child each open a file
after the fork
They get their own entries in the System File
Descriptor table
• This implies that the file position information is
different
Question
Suppose that foobar.txt consists of the 6
ASCII characters foobar. Then what is the
output of the following program?
int main()
{
FILE *fd1, *fd2;
char c;
fd1 = fopen("foobar.txt", “r”);
fd2 = fopen("foobar.txt", “r”);
fscanf(fd1, “%c”, &c);
fscanf(fd2, “%c”, &c);
printf("c = %c\n", c);
}
Answer
The descriptors fd1 and fd2 each have
their own open file table entry, so each
descriptor has its own file position for
foobar.txt. Thus, the read from fd2 reads
the first byte of foobar.txt, and the output
is
c=f
and not
c=o
Fork and Files
Be careful
It is much better to open files after forks.
Even so you need to be careful if there is
writing
This often requires that a process has mutual
exclusive access during the write process (more
later in the course)
Wait
Parents waits for a child (system call)
Blocks until a child terminates
Returns pid of the child process
Returns -1 if no child process exists (already
exited)
status
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status)
Parent waits for a specific child to
terminate
pid_t waitpid(pid_t pid, int *status, int options)
Process Creation Using Fork
int main ()
{
pid_t pid;
int status = 0;
pid = fork();
if (pid < 0)
perror(“fork()”);
if (pid > 0) {
/* parent */
printf(“I am parent\n”);
pid = wait(&status);
} else {
/* child */
printf(“I am child\n”);
exit(status);
}
}
The fork syscall returns
twice: it returns a zero to the
child and the child process ID
(pid) to the parent.
Parent uses wait to sleep until
the child exits; wait returns child
pid and status.
Wait variants allow wait on a
specific child, or notification of
stops and other signals.
More about Process Operations
In Unix-based systems, a
hierarchy of processes is
formed
In Unix, we can obtain a listing
of processes by using the ps
command
ps –el will list complete
information for all processes
Exec
The term exec refers to a family of functions
where each of the functions replace a process’s
program (the one calling one of the exec
functions) with a new loaded program
A call to a function from exec loads a binary file
into memory (destroying the memory image of the
program calling it)
The new program starts executing from the
beginning (where main begins)
On success, exec never return; on failure, exec
returns -1.
The different versions are different primarily in
the way parameters are passed
Exec
The exec family consists of these
functions: execvp, execlp, execv, execve,
execl, execle
Functions with p in their name (execvp, execlp)
search for the program in the path indicated by
the PATH environment variable; functions
without p must be given full path.
Functions with v in their name (execv, execvp,
execve) differ from functions with l (execl,
execlp, execle) in the way arguments are passed
Functions with e accept array of environment
variables
Versions of exec
Versions of exec offered by C library:
int execl( const char *path, const char *arg, ... );
int execlp( const char *file, const char *arg, ... );
int execle( const char *path, const char *arg
, ..., char *const envp[] );
int execv( const char *path, char *const argv[] );
int execvp( const char *file, char *const argv[] );
int execve( const char *filename, char *const argv [], char *const
envp[] );
Exec Example
Program A:
int i = 5;
printf(“%d\n”,i);
execl(“B”, “B”, NULL);
printf(“%d\n”,i);
Program B:
main()
{
printf(“hello\n”);
}
What is the output of
program A?
5
hello
Why is it not this?
5
hello
5
The execl command
replaces the instructions in
the process with
instructions for program B.
It starts at the first
instruction (starts at main)
Exec Example
Program A:
int i = 5;
prog_argv[0] = "B";
prog_argv[1] = NULL;
printf(“%d\n”,i);
execv(prog_argv[0],
prog_argv);
printf(“%d\n”,i);
Program B:
main()
{
printf(“hello\n”);
}
Same functionality as the
program on the previous
slide
Used execv instead of
execl
execv uses an array to pass
arguments
execl uses a list to pass
arguments
Exec Example
int main(int argc, char *argv[])
{
int i = 5;
int status;
status = execlp(“ls”, “ls”, “-l”, “a.c”,
NULL);
if (status !=0) {
perror(“you goofed\n");
printf(“errno is %d\n”,errno);
}
printf("%d\n",i);
}
In this example, note
that the command is
ls –l a.c
Each argument is in
the list
Question:
What would cause the
perror function to be
executed?
Exec Example
int main(int argc, char *argv[])
{
char *prog1_argv[4];
int i = 5;
prog1_argv[0] = "ls";
prog1_argv[1] = "-l";
prog1_argv[2] = "a.c";
prog1_argv[3] = NULL;
execvp(prog1_argv[0],
prog1_argv);
…
printf("%d\n",i);
}
Same example as that
on the previous side
but execvp is used
which requires an
array
Fork and Exec
Child process may choose to execute some
other program than the parent by using one
of the exec calls.
Exec overlays a new program on the existing
process.
Child will not return to the old program unless
exec fails. This is an important point to
remember.
File descriptors are preserved
fork() and execv()
execv(new_program, argv[ ])
Initial process
Fork
Returns a
new PID
Original
process
Continues
fork() returns pid=0 and runs as a cloned
parent until execv is called
New
Copy of
Parent
new_Program
(replacement)
execv(new_program)
Example
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid = fork();
if (pid < 0)
perror("fork()");
if (pid > 0)
{
wait(NULL);
printf("Child Complete");
} else{
if (pid == 0)
execlp("ls","ls", “-l, “a.c”,
NULL);
}
}
Exec and Shell
As we can see from the example on the
previous slide a process, which can be a
shell, can fork a child and the child can
execute a command using exec
Interprocess Communication
Consider the following command:
ls | more
There are two commands
The output of ls is the input to more
This means that the process executing ls
and the process executing more need to
communicate
But these commands assume standard input and
standard output
Interprocess Communication
Pipes
Used to allow processes on the same machine to
communicate with each other
One process spawns the other
Sockets
Processes can be on different machines
You will learn much more about sockets in CS
3357
Pipes
A pipe is a section of memory that is shared
This memory allows for a limited amount of data to
be stored in a first-in-first-out (FIFO) manner
One process will write to the pipe (as if it
were a file), while another process will read
from the pipe
The system provides the synchronization
between writing and reading processes
The file descriptor table includes pipes
Pipes
Pipes can be used between processes that
have a common ancestor
Typical use:
Pipe created by a process
Process calls fork()
Pipe used between parent and child
Allows for communication between processes
Pipes
By default, if a writing process attempts to
write to a full pipe, the system will
automatically block the process until the
pipe is able to receive the data
Likewise, if a read is attempted on an
empty pipe, the process will block until
data is available
In addition, the process will block if a
specified pipe has been opened for reading,
but another process has not opened the
pipe for writing
Creating a Pipe
#include <unistd.h>
int pipe(int filedes[2]);
Returns 0 if ok, -1 on error
Returns two file descriptors
filedes[0] is open for reading
filedes[1] is open for writing
Output of filedes[1] is input to filedes[0]
Example
#include <unistd.h>
#include <stdio.h>
int main(void){
int n;
int fd[2];
pid_t pid;
char line[80];
// track of num bytes read
// hold fds of both ends of pipe
// pid of child process
// hold text read/written
Continued …
if (pipe(fd) < 0)
perror("pipe error");
// create the pipe
if ((pid = fork()) < 0) {
// fork off a child
perror("fork error");
} else if (pid > 0) {
// parent process
close(fd[0]);
// close read end
write(fd[1], "hello world\n", 12); // write to it
wait(NULL);
}…
continued
else {
close(fd[1]);
n = read(fd[0], line, 80);
write(1, line, n);
}
exit(0);
}
// child process
// close write end
// read from pipe
// echo to screen
Fork and Pipes
Pipe takes up two entries in the file
descriptor table. These are referenced as
i and i+1
There are two entries in the system table
One entry has a read pointer
The other entry has a write pointer
Fork and Pipes
A fork copies the file descriptor table to
the child
The parent should close one of the file
descriptors while the child should close the
other
Fork and Pipes
0
1
2
3
4
stdin
stdout
stderr
fd[0]
fd[1]
Parent File Descriptor table
0
1
2
3
4
stdin
stdout
stderr
fd[0]
pipe info e.g.,
read offset
pipe info e.g.,
write offset
System file table
fd[1]
Child File Descriptor table
After Fork
Fork and Pipes
0
1
2
3
4
stdin
stdout
stderr
fd[0] = NULL
fd[1]
Parent File Descriptor table
0
1
2
3
4
stdin
stdout
stderr
fd[0]
fd[1] = NULL
Child File Descriptor table
pipe info e.g.,
read offset
pipe info e.g.,
write offset
System file table
After Closing Ends
After One End of the Pipe is
closed …
Reading from a empty pipe whose write end
has been closed returns 0 (indicating EOF)
Writing to a pipe whose read end has been
closed generates a SIGPIPE signal
If we ignore the signal or catch and return,
handler returns -1, and errno set to EPIPE
Pipe Capacity
The OS has a limit on the buffer space
used by the pipe
If you hit the limit, write will block
dup() and dup2
#include <unistd.h>
int dup(int filedes1);
int dup2(int filedes1, int filedes2);
Both will duplicate an existing file descriptor
dup() returns lowest available file descriptor, now
referring to whatever filedes1 refers to
dup2() - filedes2 (if open) will be closed and then
set to refer to whatever filedes1 refers to
Implementing I/O Redirection
How does a shell implement I/O
redirection?
ls > foo.txt
By calling the dup2(fd1,fd2) function
• Copies descriptor table entry fd1 to entry fd2
File Descriptor table
before dup2(4,1)
stdin fd0
stdout fd1
stderr fd2
fd3
fd4
a
b
File Descriptor table
after dup2(4,1)
stdin fd0
stdout fd1
stderr fd2
fd3
fd4
b
b
Example
int main(int argc, char **argv) {
int fds[2];
pid_t pid;
/* attempt to create a pipe */
if (pipe(fds)<0) {
perror("Fatal Error");
exit(1);
}
Example
/* create another process */
pid = fork();
if (pid<0) {
perror("Problem forking");
exit(1);
} else if (pid>0) {
/* parent process */
close(fds[0]);
/* close stdout, reconnect to the writing end of the pipe */
if ( dup2(fds[1],STDOUT_FILENO)<0) {
perror("can't dup");
exit(1);
}
execlp("ps","ps","-le", NULL);
perror("exec problem");
exit(1);
Example
} else {
/* child process */
}
}
close(fds[1]);
if (dup2(fds[0],STDIN_FILENO) < 0) {
perror("can't dup");
exit(1);
}
execlp("sort","sort",NULL);
perror("exec problem");
exit(1);
return(0);
Question
How would you use dup2 to redirect
standard input to descriptor 5?
int dup2(int fd1, int fd2);
copies descriptor table entry fd1 to descriptor
table entry fd2
Answer
dup2(5,0);
or
dup2(5,STDIN_FILENO);
Summary
The fork, wait, exec, pipe dup2 gives you all
the functionality for creating a shell
Not just shells
Example: Apache servers forks processes
to handle requests