[go: up one dir, main page]

0% found this document useful (0 votes)
5 views75 pages

Module 4 (UNIX)

Module 4 covers process control in UNIX, detailing mechanisms for managing processes, including their creation, execution, and termination. It explains process identifiers, the fork and vfork functions for creating new processes, and various exit and wait functions for handling process termination and status. The document also discusses file sharing between processes and the inheritance of properties from parent to child processes.

Uploaded by

shwethal.gmitcse
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)
5 views75 pages

Module 4 (UNIX)

Module 4 covers process control in UNIX, detailing mechanisms for managing processes, including their creation, execution, and termination. It explains process identifiers, the fork and vfork functions for creating new processes, and various exit and wait functions for handling process termination and status. The document also discusses file sharing between processes and the inheritance of properties from parent to child processes.

Uploaded by

shwethal.gmitcse
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/ 75

Module 4

Process Control
Introduction
• Process control in UNIX refers to the set of mechanisms and
system calls provided by the operating system to manage
processes throughout their lifecycle including their creation,
execution and termination.
• The system tracks different types of Ids for each process:
• Real, effective ,user IDs and group IDs
• The process IDs are properties of each process and how they
are affected by process control functions.
Process Identifiers
• Process identifiers in UNIX are special IDs assigned to every process
for tracking and control purposes.
• Every process has a unique process ID, a non negative integer
• There are some processes:
• Process ID 0 is usually the scheduler process known as swapper
• Process ID 1 is usually the init process and is invoked by the kernel at
the end of the bootstrap procedure
• Process ID 2 is the pagedeamon responsible for supporting the paging
of the virtual memory system.
• In addition to the process ID, there are other identifiers for every
process.
#include<unistd.h>
pid_t getpid(void);
returns: process ID of calling process
pid_t getppid(void);
returns: parent process ID of calling process
uid_t getuid(void);
returns: real user ID of calling process
uid_t geteuid(void);
returns: effective user ID of calling process
gid_t getgid(void);
returns: real group ID of calling process
gid_t getegid(void);
returns: effective group ID of calling process
fork Function
• An existing process can create a new one by calling the fork function
#include<unistd.h>
pid_t fork(void);
returns: 0 in child, process ID of child in parent, -1 on error
• The new process crated by fork is called the child process
• This function is called once but returns twice.
• The only difference in the returns is that the return value in the child is 0,
whereas the return value in the parent is the process Id of the new child.
• 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
• The reason fork returns 0 to the child is that a process can have only a
single parent and the child can always call getppid to obtain the process ID
of its parent. (process ID 0 is reserved for use by the kernel, so it’s not
possible for 0 to be the process Id of a child)
• Both the child and the parent continue executing with the instruction that
follows the call to fork
• The child is the copy of the parent
• For example, the child gets a copy of the parent’s data space, heap and
stack.
• Note that this is a copy for the child; the parent and the child do not share
these portions of memory.
• The parent and the child share the text segment.
Example programs
File sharing
• It is the context of OS referring to the ability of multiple processes to access the
same open file.
• Consider a process that has three different files opened for standard input,
standard output and standard error. On return from fork, we have the arrangement
shown:
• It is important that the parent and the child share the same file offset.
• Consider a process that forks a child, then waits for the child to complete.
• Assume that both processes write to standard output as part of their normal
processing.
• If the parent has its standard output redirected (by a shell) it is essential that
the parent’s file offset be updated by the child when the child writes to
standard output.
• In this case, the child can write to standard output while the parent is waiting
for it; on completion of the child, the parent can continue writing to standard
output, knowing that its output will be appended to whatever the child wrote.
• If the parent and the child did not share the same file offset (the position within
an open file that indicates where the next read or write operators will start) , this type
of interaction would be more difficult to accomplish and would require
explicit actions by the parent.
There are two normal cases for handling the descriptors
after a fork.
1. The parent waits for the child to complete. In this case, the
parent does not need to do anything with its descriptors.
When the child terminates, any of the shared descriptors that
the child read from or write to will have their file offsets
updated accordingly.
2. Both the parent and the child go their own ways. Here, after
the fork, the parent closes the descriptors that it doesn’t need
and the child does the same thing. This way, neither
interferes with the other’s open descriptors.
There are numerous other properties of the parent that are inherited by the child:

• Real user ID, real group ID, effective • File mode creation mask
user ID, effective group ID • Signal mask and dispositions
• Supplementary group IDs • The close-on-exec flag for any open
• Process group ID file descriptors
• Session Id • Environment
• Controlling terminal • Attached shared memory segments
• The set-user-ID and set-group-ID flags • Memory mappings
• Current working directory • Resource limits
• Root directory
The differences between the parent and child are:
• The return value from fork
• The process IDs are different
• The two processes have different parent process IDs: the parent
process ID of the child is the parent; the parent process ID of the
parent doesn’t change
• The child’s tms_utime(user CPU time), tms_stime(system (kernel)
CPU time), tms_cstime(user CPU time of terminated children) values
are set to 0
• File locks set by the parent are not inherited by the child
• Pending alarms are cleared for the child
• The set of pending signals for the child is set to the empty set.
The two main reasons for fork to fail are
a. If too many processes are already in the system, which usually
means that something else is wrong or
b. If the total number of processes for this real user ID exceeds the
system’s limit.
There are two uses for fork:
• Fork is used when a process wants to make a copy of itself. Both the
parent and child can then do different tasks at the same time. For
example, servers use fork to let the parent wait for new requests while
the child handles each request.
• Fork is also used when a process wants to run a completely new
program. This is common for shell programs, where the child process
runs a new program after the fork is done.
vfork function
• 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 crates the new process just like fork, without copying
the address space of the parent into the child, as the child wont 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( replaces its
current program with a new one) 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 “apue.h” glob++;
int glob=6; var++;
int main(void) _exit(0);
{ }
/*parent continues here
int var; */
pid_t pid; printf(“pid=%d,glob=%d, var=%d\n”,
var=88; getpid(),glob, var);
printf(“before vfork\n”); exit(0);
if((pid=vfork())<0) {//process creation }
failed Output
err_sys(“vfork error”); $./a.out
} before vfork
else if(pid ==0) {//child process pid=29039, glob=7, var=89
exit Functions
A process can terminate normally in five ways:
• Executing a return from the main function
• Calling the exit function (end a running program with a status code)
• Calling the _exit or _Exit function (performs cleanup)
in most UNIX system implementations, exit(3) is a function in the
standard C library, whereas _exit(2) is a system call.
• Executing a return from the start routine of the last thread in the
process. When the last thread returns from its start routine, the process
exits with a termination status of 0.
• Calling the pthread_exit function from the last thread in the process.
The three forms of abnormal termination are as follows:
1. Calling abort(): The program itself calls the abort() function,
which causes it to end abnormally. Sends the SIGABRT signal
to the process.
2. Receiving certain signals: The program is forcefully killed
by the OS because it did something wrong or the user sent a
signal like (SIGKILL-immediate kill, SIGSEGV-segmentation
fault, SIGINT-interrupt signal)
3. Thread cancellation request: in multi-threaded programs,
one thread can request another thread to be cancelled.
• When any process ends, the kernel runs the same closing operations for it:
closing files, freeing its memory, and cleaning up its resources.
• The parent process can find out how its child ended using special exit
functions (exit, _exit, Exit)
• An “exit status” is given to the parent to show how the child terminated
• Exit status: what the child processes gives when it ends.
• Termination status: what the parent sees, converted by the system from
exit status to show exactly how the child finished, especially in case of
errors
• If a parent process ends before its child has finished, a special process
called init (process ID 1) becomes the new parent. This ensures every
process always has a parent, and system can always track them.
wait and waitpid functions
• When a process terminates, either normally or abnormally, the kernel notifies
the parent by sending the SIGCHLD signal to the parent. Because the
termination of a child is an asynchronous event- it can happen at any time
while the parent is running-this signal is the asynchronous notification from
the kernel to the parent.
• The parent can choose to ignore this signal or it can provide a function that is
called when the signal occurs: a signal handler
• A process that calls wait or waitpid can:
• Block, if all of its children are still running
• Returns immediately with info if at least one child has ended and its status
is available. All children have ended and their statuses have already been
collected.
• Returns an error if the process has no child process.
#include<sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
Both return: ID if Ok, 0 or -1 on error
The difference between these two functions are:
• 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.
Macros to examine the termination status returned by wait and waitpid
Macro Description
WIFEXITED(status) Returns true if the child process terminated normally (i.e., called exit() or
(Wait If Exited) returned from main)

WIFSIGNALED(status) Returns true if the child process was killed by a signal(like SIGKILL or
(Wait If Signaled) SIGSEGV not a normal exit)
Check if the child was terminated by an error or external kill

WIFSTOPPED(status) Returns true if the child was stopped (not ended ) by a signal
(Wait If Stopped) It is used to detect if the child is paused.

WIFCONTINUED(status) Returns true if a stopped child has been resumed (continued)


(Wait If Continued)
Program to demonstrate various exit status
#include “apue.h” else if(pid==0) /*child*/
#include <sys/wait.h> abort();
int main(void) if(wait(&status)!=pid) /*wait for child*/
{ err_sys(“wait error”);
pid_t pid;
pr_exit(status); /*print its status*/
int status;
if((pid=fork())<0) //third fork
if((pid=fork())<0) //first fork
err_sys(“fork error”);
err_sys(“fork error”);
else if(pid==0)
else if(pid==0) /*child*/
exit(7); //child process terminates normally with status /=0; /*divide by 0
exit status of 7 if(wait(&status)!=pid) /*wait for child*/
if(wait(&status)!=pid) /*wait for child*/ err_sys(“wait error”);
err_sys(“wait error”); pr_exit(status);
pr_exit(status); /*print its status*/
exit(0);
if((pid=fork())<0) //second fork
}
err_sys(“fork error”);
The interpretation of the pid argument for waitpid depends on its value:
pid==1 waits for any child process. In this respect, waitpid is
equivalent to wait.
pid>0 waits for the child whose process ID exactly equals the given
pid.
pid==0 waits for any child whose process group ID equals that of the
calling process.
pid<1 waits for any child whose process group ID equals the
absolute value of pid.
The options constants for waitpid
Constant Description
WCONTINUED Allows you to detect if a stopped child
process was continued

WNOHANG Makes waitpid non-blocking; if no child has


changed state, it just returns immediately.

WUNTRACED Also return when a child has stopped


(paused) not just finished.
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 nonblocking 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.
waitid function
• It is an additional function to retrieve the exit status of a process. The
waitid function is similar to waitpid, but provides extra flexibility.
#include<sys/wait.h>
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
returns: 0 if ok, -1 on error
The idtype constants for waited are:
Constant Description
P_PID Wait for a particular process: id contains the
process ID of the child to wait for
P_PGID Wait for any child process in a particular process
group: id contains the process group ID of the
children to wait for
P_ALL Wait for any child process: id is ignored.
The options argument is a bitwise OR of the flags: these flags indicate
which state changes the caller is interested in
Constant Description
WCONTINUED Wait for a process that has previously stopped and has
been continued, and whose status has not yet been
reported.
WEXITED Wait for processes that have exited
WNOHANG Return immediately instead of blocking if there is no
child exit status available.
WNOWAIT Don’t destroy the child exit status. The child’s exit status
can be retrieved by a subsequent call to wait, waited or
waitpid.
WSTOPPED Wait for a process that has stopped and whose status has
not yet been reported.
wait3 and wait4 functions
• The only feature provided by these two functions that isn't provided by the
wait, waitid, and waitpid functions is an additional argument that allows the
kernel to return a summary of the resources used by the terminated process
and all its child processes.
• The prototypes of these functions are:
#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
Race conditions
• 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 process run.
• The fork function is a lively ground for race conditions, if any of the
logic after the fork either explicitly or implicitly depends on whether the
parent or child runs first after the fork.
• In general, it cant be predicted which process runs first. Even if we knew
which process would run first, what happens after that process starts
running depends on the system load and the kernel’s scheduling
algorithm.
• The program contains a race condition because the output depends on the
order in which the processes are run by the kernel and how long each
process runs.
#include “apue.h” TELL_CHILD(pid); /*tell child we are
done*/
TELL_WAIT(); //set things for tell & wait
WAIT_CHILD(); /*wait for the child*/
if((pid=fork())<0) {
*/and the parent continues on its way*/
err_sys(“fork error”);
exit(0);
} else if(pid==0) {
/*child does whatever is necessary
TELL_PARENT (getppid()); //tell parent
we are done
WAIT_PARENT(); //and wait for parent
/* and the child continue its way */
exit(0);
}
/*parent does whatever is necessary*/
Program with a race condition
#include “apue.h” {
static void charatatime(char *); charatatime(“output from parent\n”);
int main(void) }
exit(0);
{
static void
pid_t pid;
charatatime(char str)
if((pid=fork())<0) { {
err_sys(“fork error”); char *ptr;
} else if (pid==0) { int c;
charatatime(“output from child\n”); setbuf(stdout, NULL);
} for(ptr=str;(c=*ptr++)!=0)
else putc(c,stdout);
}
Program to use TELL and WAIT functions
#include “apue.h” charatatime(“output from parent\n”);
static void charatatime(char *); + TELL_CHILD(pid);
int main(void) }
exit(0);
{
}
pid_t pid;
static void
+ TELL_WAIT();
charatatime(char *str)
if((pid=fork()<0) { {
err_sys(“fork error”); char *ptr;
} else if(pid==0) { int c;
+ WAIT_PARENT(); /*parent goes setbuf(stdout,NULL);
first*/ for(ptr=str;(c=*ptr++)!=0;)
charatatime(“output from child\n”); putc(c,stdout);
} else { }
exec functions
• 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 and disk.
• There are 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.
• The difference in these functions is that the first four take a pathname
argument, whereas the last two take a filename argument. When a filename
argument is specified.
If filename contains a slash, it is taken as a pathname.
Otherwise, the executable file is searched for in the directories specified by the
PATH environment variable.
• The next difference concerns the passing of the argument list (1 stands for
list and v stands for vector).
• The functions execl, execlp and execle require each of the command-line
arguments to the new program to be specified as separate arguments.
• For the other three functions(execv,execvp and execve), we have to build
an array of pointers to the arguments and the address of this array is the
argument to these three functions.
• The final difference is the passing of the environment list to the new
program. The two functions whose names end in an e(execle and execve)
allow us to pass a pointer to an array of pointers to the environment
existing environment for the new program.

The above table shows the differences among the 6 exec functions.
We’ve mentioned that the process ID does not change after an exec, but
the new program inherits additional properties from the calling process:
• Process ID and parent process • Root directory
ID • File mode creation mask
• Real user ID and real group ID • File locks
• Supplementary group IDs • Process signal mask
• Process group ID • Pending signals
• Session ID • Resource limits
• Controlling terminal • Value for tms_utime,
• Time left until alarm clock tms_stime, tms_cutime and
• Current working directory tms_cstime
Example of exec functions
#include “apue.h” }
#include<sys/wait.h> if (waitpid(pid,NULL,0)<0)
char *env_init[]= {“USER=unknown”, err_sys(“wait error”);
”PATH=/tmp”,NULL};
if((pid=fork())<0) {
int main(void){
pid_t pid; err_sys(“fork error”);
if((pid=fork())<0) { } else if(pid==0) {
err_sys(“fork error”); if (execlp(“echoall”,”echoall”,”only
} else if(pid==0) { 1 arg”,(char*)0)<0)
if(execle(“/home/sar/bin/ err_sys(execlp error”);
echoall”,”echoall”,”myarg1”,”MY ARG2”, } exit(0); }
(char *)0,env_init<0)
err_sys(“execle error”);
Output:
$ ./a.out
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
Chapter 2
Interprocess communication
Interprocess communication
Overview of IPC methods:
• Pipes
• Popen and pclose functions
• Coprocesses
• FIFOs
• System V IPC
• Message Queues
• Semaphores
Introduction
• Interprocess communication(IPC) is a set of techniques that allow processes
to exchange data and information with each other during execution. This is
essential in modern operating systems where multiple processes need to
coordinate or share resources.
• Common IPC methods are:
1) Half duplex pipes 6) Shared memory
2) FIFOs 7) Semaphores
3) Full duplex pipes 8) Sockets
4) Named full duplex pipes 9) STREAMS
5) Message queues
• The first seven forms of IPC are usually restricted to IPC between processes
on the same host.
• The final two i.e., Sockets and STREAMS are the only two that are generally
supported for IPC between processes on different hosts.
Pipes
• Pipes are the oldest form of UNIX system IPC and are provided by all
UNIX systems. Pipes have two limitations:
• Historically, they have been half duplex(i.,data flows in only one
direction). Some systems now provide full-duplex pipes.
• Pipes can be used only between processes that have a common ancestor.
• Normally, a pipe is created by a process, that process calls fork, and the
pipe is used between the parent and the child.
• A pipe is created by calling the pipe function.
#include<unistd.h>
int pipe(int filedes[2]);
Returns:0 if ok, -1 on error
• Two file descriptors are returned through the filedes argument: filedes[0] is
open for reading and filedes[1] is open for writing.
• The output of filedes[1] is the input for filedes[0].

• The left half of the figure shows the two ends of the pipe connected in a single
process. The right half of the figure emphasizes that the data in the pipe flows
through the kernel.
• A pipe in a single process is next to useless
• Normally, the process that calls pipe then calls fork, creating an IPC
channel from the parent to the child or vice versa.
• What happens after the fork depends on which direction of data flow we
want
• For a pipe from the parent to the child, the parent closes the read end of
the pipe (fd[0]), and the child closes the write end (fd[1]).
• When using a pipe for communication from the child to the
parent:
• The parent closes the writing end (fd[1])
• The child closes the reading end (fd[0]).
• When the end of a pipe is closed, the following rules will apply:
• If we try to read from a pipe and the other side (the writing end) is
closed, the read operation will return 0.
• This means “end of file” (EOF), there is no more data to read.
• If we try to write to a pipe and the other side (the reading end) is
closed, the system send a signal called SIGPIPE to the process.
• If it is ignored or handle the signal, the write function returns -1 and
sets an error indicating EPIPE.
Program to create a pipe between a parent and its
child and to send data down the pipe
#include “apue.h” close(fd[0]);
int main(void) write (fd[1], “hello world\n”,12);
{ } else { /*child */
int n; close(fd[1]);
int fd[2]; n=read(fd[0], line,MAXLINE);
pid_t pid; write(STDOUT_FILENO, line,n);
char line[MAXLINE]; }
if(pipe(fd)<0) exit(0);
err_sys(“pipe error”); }
} else if (pid>0) { /*parent */
popen and pclose functions
• popen and pclose are functions in C that make it easy to run another
program and read its output or send input to it. They automatically
take care of pipes, child process creation and cleanup.
popen:
• popen starts a new process (like running a shell command).
• You can choose to read what the process prints or write to this process
as its input.
pclose:
• It closes the pipe and waits for the process to finish
• pclose should be called after using popen.
#include<stdio.h>
FILE *popen(const *cmdstring, const char *type);
returns: file pointer if ok, NULL on error
int pclose(FILE *fp);
returns: termination status of cmdstring, or -1 on
error
• cmdstring is the command to run
• type is “r” (read from the process” or “w” (write to the process)
• The function popen does a fork and exec to execute the
cmdstring, and returns a standard I/O file pointer.
• If type is “r”, the file pointer is connected to the
standard output od cmdstring
result of fp=popen(cmdstring, “r”)
• If type is “w”, the file pointer is connected to the standard input of
cmdstring
result of fp=popen(cmdstring, “w”)
Coprocesses
• A Unix system filter is a program that reads from standard input
and writes to the standard output.
• Normally, filters are connected in a straight line (pipeline) in a
shell pipelines.
• A filter becomes a coprocess when the same program generates the
filters output.
• A coprocess normally runs in the background from a shell, and its
standard input and standard output are connected to another
program using a pipe.
• The process creates a two pipes: standard input and standard
output of the coprocess.
Driving a coprocess by writing its standard
input and reading its standard output
Program of simple filter to add two numbers
#include “apue.h”
int main(void)
{
int n, int1,int2;
char line[MAXLINE];
while((n=read(STDIN_FILENO, line,MAXLINE))>0)
{ line[n]=0; /*null terminate*/
if(sscanf(line, “%d%d”, &int1, &int2)==2){
sprintf(line, “5d\n”,int1+int2);
n=strlen(line);
if(write(STDOUT_FILENO, line,n)!=n)
err_sys(“write error”);
} else {
if(write(STDOUT_FILENO, “invalid args\n”,13)!=13)
err_sys(“write error”);
}
}
exit(0);
}
FIFOs
• FIFOs are sometimes called named pipes. Pipes can be used only
between related processes when a common ancestor has created the pipe.
#include<sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
returns: 0 if ok, -1 on error
• mkfifo is a function used to create a FIFO (named pipe) in UNIX
systems. After creating a FIFO with mkfifo, it should be opened with a
open function.
• const char *pathname: This is a string pathname that specifies the
name(path) of the FIFO file
• mode_t mode: it sets the permission for the FIFO file(read, write or
execute)
Once we have used mkfifo to create a FIFO, we open it using open. When
we open a FIFO, the non-blocking flag(O_NONBLOCK) affects
Normal case(O_NONBLOCK not set):
• If you open the FIFO for reading, it blocks (waits) until another process
opens it for writing.
• If you open it for writing, it blocks until another process opens it for
reading.
Nonblocking case(O_NONBLOCK set):
• If you open for reading, it returns immediately even if no one opened it for
writing.
• If you open for writing, it only returns with an error if no one opened it for
reading.
There are two uses for FIFOs
• FIFOs are used by shell commands to pass data from one
shell pipeline to another without crating intermediate
temporary files.
• FIFOs act like meeting places where a client and a server can
safely exchange messages or data.
Example using FIFOs to duplicate Output
Streams
• FIFOs can be used to duplicate an output stream in a series of shell
commands.
• This prevents writing the data to an intermediate disk file.

Procedure that processes a filtered input stream twice


With a FIFO and the UNIX program tee(1),
• The tee program in UNIX copies its standard input to both its standard
output and to the file named on its command line).
• If you use a FIFO with tee, you don’t need to create a temporary file to
share data

mkfifo fifo1
prog3 <fifo1 &
prog1 < infile | tee fifo1 | prog2
• We create the FIFO and then start prog3 in the background, reading from
the FIFO.
• We then start prog1 and use tee to send its input to both the FIFO and
prog2.

Using a FIFO and tee to send a stream to two different processes


Example- Client-Server communication
using FIFO
• FIFOs (named pipes) let clients send data to a server easily.
• Each client can write its request to one well-known FIFO
crated by the server.
• The messages sent by clients should be short enough to avoid
mixing them up.
• If all clients use the same FIFO, its hard to know which
response is meant for each client.
• A solution is to have each client tell the server its unique process
ID. The server then makes a separate FIFO for each client using
that ID in the name.
• For example, the server might make FIOs with names like
/vtu/ser.12345, where 12345 is the client’s process ID.
• There is a small problem: if the client crashes or disconnects, its
FIFO file could be left behind in the system.
• The server should also handle the case(using SIGPIPE) when a
client disconnects before reading its response, so the server
doesn’t get stuck writing to a FIFO with no reader.
Clients sending requests to a server using a
FIFO
Client-server communication using FIFOs
System V IPC
• Each IPC structure (like message queue, semaphore or shared
memory) in UNIX gets a special ID called an identifier
• This identifier is just a non-negative number used by the kernel as an
internal name for the IPC object.
• If different programs want to share the same IPC object, they use a key
as an external name.
• When crating any IPC, you must give a key, which helps link
programs to the same resource.
• The key is basic data type called key_t (often a long integer from
<sys/types.h>
• The key gets converted into the internal identifier by the kernel for
actual use.
Ways for client and server to use same IPC
structure
• The server can create a new IPC object (like a message queue or
shared memory) using the special key IPC_PRIIVATE and save the
identifier in a place like a file for the client to find later.
• Using IPC_PRIVATE always crats a new, unique IPC structure.
However, the server must write the identifier somewhere for clients to
get it afterward.
• IPC_PRIVATE is good for parent-child process use: the parent creates
the IPC, and after a fork, the child can use the same identifier passed
as an argument.
Sharing a key between client and server
• The client and server can both use a shared key, agreed upon
beforehand (for example, in a header file) to create or access
the same IPC object.
• If the key is already used by another resource, creating the
IPC will fail. The server should check for this error, delete
the old IPC and try again.
• Another method is to agree on a file path and a project ID(a
number 0-255). Both call the ftok() function to combine these
and generate the same key.
#include<sys/ipc.h>
key_t ftok(const char *path, int id);
Returns: key if ok, -1 on error
• ftok(const char *path, int id) crates a key from an existing file path and a
project ID (only the lower 8 bits of the ID are used).
• ftok() works by using parts of the file’s inode data and the project ID to
make the key.
• Even if two different paths have the same name, they can generate
different keys if they refer to different files.
• If two filenames point to the same file (even with different names generate
different names or paths) and the project ID is the same, ftok() generates
the same key.
Permission structure
• Every IPC structure (like message queue, semaphore etc) has a
permission structure called ipc_perm, which stores info about who owns
it and what permissions it has.
• The ipc_perm structure members:
Struct ipc_perm {
uid_t uid; /*owner’s effective user id*/
gid_t gid; /*owner’s effective group id*/
uid_t cuid; /*creator’s effective user id*/
gid_t cgid; /*creator’s effective group id*/
mode_t mode; /*access modes*/
};
Initializing and changing permissions
• All fields in ipc_perm are set when the IPC structure is created.
• You can later change these fields (uid, gid, mode) using special system
calls like msgctl, semctl or shmctl.
• Only the creator of the IPC or the superuser can change these fields,
similar to how you use chmod files.
Advantages of System V IPC
• It supports multiple types of interprocess communication: message
queues, semaphores and shared memory making it versatile for
different needs.
• Reliable and flow-controlled: message queues are reliable, allow flow
control and can be configured so messages are processed in a non-
FIFO order, which increases flexibility.
• Permission control: you can set read and write permissions for users,
groups and others like files, allowing controlled access.
• Portability: System V IPC is widely supported and available on many
UNIX systems, which makes programs using it easier to run on
different platforms.
Disadvantages of System V IPC
• IPC structures are system-wide and do not have a reference count:
• If a process sends messages and then finishes, the queue and messages stay in
the system until someone reads or deletes them using msgrcv or msgctl, or a
system reboot.
• IPC structures don’t have names in the file system:
• You cant see them with ls or delete with rm or change permissions with
chmod. Instead, new commands (ipcs and ipcrm) and system calls(like
msgget, semop, shmat) were created to manage them.
• No file descriptors are used for IPC structures:
• File descriptors are for regular files, sockets, pipes and other stream like
resources. For System V IPC we use API like msgget, msgsnd etc

You might also like