PROGRAM 1: Write a program in C to create a child process that starts looping and
then terminates.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork(); // Create child process
if (pid < 0) {
// Fork failed
fprintf(stderr, "Fork failed!\n");
exit(1);
} else if (pid == 0) {
// Child process
printf("Child process started (PID: %d). Looping...\n", getpid());
// Loop 5 times
for (int i = 0; i < 5; i++) {
printf("Child iteration %d\n", i);
sleep(1); // Wait 1 second per iteration
}
printf("Child process terminating.\n");
exit(0); // Terminate child
} else {
// Parent process
printf("Parent process (PID: %d) waiting for child...\n", getpid());
wait(NULL); // Wait for child to exit
printf("Child process terminated. Parent exiting.\n");
}
return 0;
}
Explanation:
1. Headers Included:
o <stdio.h>: Input/output operations.
o <stdlib.h>: Exit functions.
o <unistd.h>: Provides fork(), sleep(), and getpid().
o <sys/wait.h>: wait() system call.
2. Creating Child Process:
o fork() splits execution into parent (returns child's PID) and child (returns 0).
o Handle fork failure with error message and exit.
3. Child Process Behavior:
o Prints its PID using getpid().
o Loops 5 times, printing iteration numbers.
o sleep(1) pauses for 1 second per iteration.
o Terminates with exit(0) after the loop.
4. Parent Process Behavior:
o Prints its PID and waits for the child using wait(NULL).
o Resumes after child termination and prints a confirmation before exiting.
Key Concepts Demonstrated:
• Process Creation: fork() duplicates the process.
• Process Termination: Child ends with exit(0), parent ends naturally.
• Process Synchronization: Parent uses wait() to pause until child exits.
• Concurrent Execution: Child runs independently during its loop.
Compilation & Execution:
1. Save as fork_demo.c.
2. Compile: gcc fork_demo.c -o fork_demo
3. Run: ./fork_demo
PROGRAM 2: Write a C Program to create a child process using fork system call and
print PID and PPID of the processes.
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork(); // Create child process
if (pid < 0) {
// Fork failed
perror("fork() failed");
return 1;
}
if (pid == 0) {
// Child process
printf("Child Process:\n");
printf(" PID = %d\n", getpid()); // Child's PID
printf(" PPID = %d\n", getppid()); // Parent's PID
} else {
// Parent process
wait(NULL); // Wait for child to finish
printf("\nParent Process:\n");
printf(" PID = %d\n", getpid()); // Parent's PID
printf(" PPID = %d\n", getppid()); // Parent's parent (usually shell)
printf(" Child PID = %d\n", pid); // Child's PID
}
return 0;
}
Explanation:
1. Headers:
o <stdio.h>: Input/output functions
o <unistd.h>: Provides fork(), getpid(), getppid()
o <sys/wait.h>: wait() system call
2. Process Creation:
o fork() creates a duplicate process (child)
o Returns:
▪ -1: Fork failed (error)
▪ 0: Child process context
▪ >0: Parent process context (child's PID)
3. Child Process Block:
o Prints its own PID using getpid()
o Prints parent's PID using getppid()
o Automatically terminates after execution
4. Parent Process Block:
o wait(NULL) pauses parent until child exits
o Prints its own PID and parent's PID (PPID)
o Prints child's PID (return value from fork())
5. Key Functions:
o getpid(): Returns current process ID
o getppid(): Returns parent process ID
o wait(NULL): Suspends parent until child terminates
Key Points:
1. Process Hierarchy:
o Child's PPID = Parent's PID
o Parent's PPID = PID of the process that launched it (usually your shell)
2. Synchronization:
o wait(NULL) ensures child completes before parent continues
o Without this, parent might print before child finishes
3. Output Order:
o Child's output appears first because parent waits
o Without wait(), output order might vary
4. Real-world Values:
o Actual PID numbers will vary each run
o PPID of parent will be your terminal/shell's PID
This program demonstrates fundamental process management concepts in Unix/Linux
systems, showing how processes relate to each other in the system hierarchy.
PROGRAM 3: Write a C program to show that the child can be set up to ignore a signal
from its parent.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork(); // Create child process
if (pid < 0) {
perror("fork failed");
exit(1);
}
if (pid == 0) { // Child process
printf("Child: Setting up to ignore SIGINT (Ctrl+C)\n");
// Set up to ignore SIGINT signal
struct sigaction sa;
sa.sa_handler = SIG_IGN; // Ignore signal
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGINT, &sa, NULL) {
perror("Child: sigaction failed");
exit(1);
}
printf("Child: Now ignoring SIGINT. PID = %d\n", getpid());
printf("Child: Waiting for 10 seconds. Try sending SIGINT (Ctrl+C)\n");
sleep(10); // Wait to receive signals
printf("Child: Exiting\n");
} else { // Parent process
sleep(1); // Give child time to set up signal handler
printf("\nParent: Sending SIGINT to child (PID %d)\n", pid);
// Send SIGINT signal to child
if (kill(pid, SIGINT) {
perror("Parent: kill failed");
}
printf("Parent: Signal sent. Waiting for child to exit...\n");
wait(NULL); // Wait for child to terminate
printf("Parent: Child exited normally\n");
}
return 0;
}
Explanation:
1. Signal Handling Setup:
o The child process uses sigaction() to configure SIGINT (Ctrl+C) to be ignored
o SIG_IGN is a special handler that tells the process to ignore the signal
o The child sleeps for 10 seconds to demonstrate it's ignoring signals
2. Parent Process:
o Waits 1 second to ensure child sets up its signal handler
o Sends SIGINT to child using kill(pid, SIGINT)
o Waits for child to exit normally
3. Key Functions:
o fork(): Creates child process
o sigaction(): Configures signal handling
o kill(): Sends signal to a specific process
o sleep(): Pauses execution
o wait(): Parent waits for child termination
4. Demonstration:
o During execution, you can press Ctrl+C while the child is sleeping
o The child will ignore both:
▪ Signals from the terminal (user pressing Ctrl+C)
▪ Signals sent programmatically by the parent
o The child completes its sleep and exits normally
Key Points:
1. Signal Inheritance:
o By default, child processes inherit signal handlers from parents
o This program shows how a child can override inherited handlers
2. Independent Signal Handling:
o Each process has its own signal disposition
o Changing handlers in child doesn't affect parent, and vice versa
3. Practical Applications:
o Daemon processes that need to ignore terminal signals
o Critical processes that should not be interrupted
o Custom signal handling hierarchies
4. Behavior During Execution:
o If you press Ctrl+C while child is sleeping:
▪ Parent process will terminate (default SIGINT behavior)
▪ Child process continues running until completion
o To test properly, run in background or separate terminal
Compilation and Execution:
gcc signal_ignore.c -o signal_ignore
./signal_ignore
PROGRAM 4: Write a program in C in which a parent process accepts a list of integers
to be sorted. Parent process uses the fork system call to create a new process called a child
process. Both the processes use shared memory for the list of integers. Now use the parent
process to sort the integers using bubble sort and the child process to sort the integers
using selection sort. Use semaphore variable for process synchronization
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <semaphore.h>
#define MAX_SIZE 100
// Shared data structure
typedef struct {
int size;
int array[MAX_SIZE];
sem_t sem;
} SharedData;
// Bubble sort implementation
void bubble_sort(int arr[], int n) {
for (int i = 0; i < n-1; i++) {
for (int j = 0; j < n-i-1; j++) {
if (arr[j] > arr[j+1]) {
// Swap elements
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
// Selection sort implementation
void selection_sort(int arr[], int n) {
for (int i = 0; i < n-1; i++) {
int min_idx = i;
for (int j = i+1; j < n; j++) {
if (arr[j] < arr[min_idx]) {
min_idx = j;
}
}
// Swap elements
int temp = arr[i];
arr[i] = arr[min_idx];
arr[min_idx] = temp;
}
}
// Print array elements
void print_array(int arr[], int n) {
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
// Create shared memory
SharedData *shared = mmap(NULL, sizeof(SharedData),
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS,
-1, 0);
if (shared == MAP_FAILED) {
perror("mmap failed");
exit(EXIT_FAILURE);
}
// Initialize semaphore
if (sem_init(&shared->sem, 1, 1) != 0) {
perror("sem_init failed");
exit(EXIT_FAILURE);
}
// Get input from user
printf("Enter number of integers (max %d): ", MAX_SIZE);
scanf("%d", &shared->size);
if (shared->size > MAX_SIZE || shared->size < 1) {
fprintf(stderr, "Invalid size! Must be between 1 and %d\n", MAX_SIZE);
exit(EXIT_FAILURE);
}
printf("Enter %d integers:\n", shared->size);
for (int i = 0; i < shared->size; i++) {
scanf("%d", &shared->array[i]);
}
// Save original array for restoration
int original[MAX_SIZE];
memcpy(original, shared->array, shared->size * sizeof(int));
// Create child process
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
exit(EXIT_FAILURE);
}
if (pid > 0) { // Parent process (Bubble Sort)
sem_wait(&shared->sem); // Lock semaphore
printf("\nParent sorting (Bubble Sort)...\n");
bubble_sort(shared->array, shared->size);
printf("Parent sorted array: ");
print_array(shared->array, shared->size);
// Restore original array for child
memcpy(shared->array, original, shared->size * sizeof(int));
sem_post(&shared->sem); // Unlock semaphore
wait(NULL); // Wait for child to finish
// Cleanup
sem_destroy(&shared->sem);
munmap(shared, sizeof(SharedData));
printf("\nParent exiting.\n");
}
else { // Child process (Selection Sort)
sem_wait(&shared->sem); // Lock semaphore
printf("Child sorting (Selection Sort)...\n");
selection_sort(shared->array, shared->size);
printf("Child sorted array: ");
print_array(shared->array, shared->size);
sem_post(&shared->sem); // Unlock semaphore
printf("Child exiting.\n");
exit(EXIT_SUCCESS);
}
return 0;
}
Explanation:
1. Shared Memory Setup:
o Uses mmap with MAP_ANONYMOUS | MAP_SHARED to create memory
shared between parent and child
o Shared memory contains:
▪ Integer array and its size
▪ Semaphore for synchronization
2. Semaphore Initialization:
o sem_init(&shared->sem, 1, 1) creates a process-shared semaphore
o Initial value = 1 (unlocked)
o Second argument 1 enables sharing between processes
3. Process Workflow:
o Parent Process:
1. Locks semaphore with sem_wait()
2. Sorts array using bubble sort
3. Prints sorted array
4. Restores original array
5. Unlocks semaphore with sem_post()
6. Waits for child to finish
o Child Process:
1. Waits for semaphore lock
2. Sorts array using selection sort
3. Prints sorted array
4. Unlocks semaphore
5. Exits
4. Synchronization:
o Semaphore ensures only one process accesses the shared array at a time
o Parent restores original array after sorting for child to use
o wait() ensures parent cleans up after child exits
5. Key System Calls:
o fork(): Creates child process
o mmap(): Allocates shared memory
o sem_init(): Initializes semaphore
o sem_wait()/sem_post(): Semaphore lock/unlock
o wait(): Parent waits for child termination
o exit(): Child terminates
Compilation and Execution:
gcc -o sort_demo sort_demo.c -lpthread
./sort_demo