Lecture No. 07
Dated: 28-04-2025
The wait()
System Call
The wait()
system call suspends a process
until one of its immediate children
terminates or stops.
It returns early if a signal is received or immediately if no children
are active.
On success, it returns the PID
of a child
.
If the parent
terminates, its children
are reparented to init
, which collects their status.
Zombie Process
A zombie process
is a process
that has terminated but whose exit status has not yet been received by its parent process
or by init
.
The execlp()
System Call
After fork()
, execlp()
is typically used by one process
to replace its memory with a new program
from an executable file or interpreter data.
A successful execlp()
does not return, as the original process
image is replaced.
This allows the two processes
to communicate and then operate independently.
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
int main () {
int pid, status;
pid = fork();
if (pid == -1) {
perror("fork failed"); // Use perror for better error message
exit(1);
}
if (pid == 0) { /* Child */
if (execlp("/bin/ls", "ls", NULL) < 0) {
printf("exec failed\n");
exit(1);
}
}
else { /* Parent */
wait(&status);
printf("Well done, kid!\n");
exit(0);
}
return 0;
}
Cooperating Processes
Processes
in an OS
can be independent or cooperating.
Independent processes
do not affect others, while cooperating processes share data.
Cooperating processes
offer benefits like:
- Information Sharing: Multiple users access shared resources.
- Computation Speedup: Tasks are divided into parallel subtasks on multiprocessor systems.
- Modularity: Systems are organized into separate
processes
orthreads
. - Convenience: Users can perform multiple tasks simultaneously.
Example:
The producer-consumer problem illustrates communication between processes
.
A producer
generates data for a consumer
, requiring a shared buffer.
Synchronization ensures the consumer
waits if the buffer is empty, and the producer
waits if full.
Buffers can be bounded (fixed size) or unbounded (no size limit), implemented via inter-process communication or shared memory.
#define BUFFER_SIZE 10
typedef struct {
// ...
} item;
item buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
The shared buffer is a circular array with two pointers
:
in
points to the next free slot.out
points to the next full slot.
The buffer is empty whenin == out
, and full when(in + 1) % BUFFER_SIZE == out
.
// Producer Process
while (1) {
/* Produce an item in nextProduced */
while (((in + 1) % BUFFER_SIZE) == out) {
/* do nothing */
}
buffer[in] = nextProduced;
in = (in + 1) % BUFFER_SIZE;
}
// Consumer Process
while (1) {
while (in == out) {
/* do nothing */
}
nextConsumed = buffer[out];
out = (out + 1) % BUFFER_SIZE;
/* Consume the item in nextConsumed */
}