0% found this document useful (0 votes)
49 views44 pages

Os Experiments

The document outlines various Linux commands and system calls for file, directory, and process management, including commands for creating, viewing, copying, moving, and deleting files, as well as managing directories and processes. It also describes experiments involving shell scripting, CPU scheduling algorithms, the Banker's Algorithm for deadlock avoidance, and the Producer-Consumer problem using semaphores. Each section includes code examples and concludes with a summary of the implemented concepts.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
49 views44 pages

Os Experiments

The document outlines various Linux commands and system calls for file, directory, and process management, including commands for creating, viewing, copying, moving, and deleting files, as well as managing directories and processes. It also describes experiments involving shell scripting, CPU scheduling algorithms, the Banker's Algorithm for deadlock avoidance, and the Producer-Consumer problem using semaphores. Each section includes code examples and concludes with a summary of the implemented concepts.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Experiment-1

1. File Management

Linux Commands

1. Creating Files:

o touch filename: Creates an empty file or updates its timestamp if it

already exists. o cat > filename: Lets you create a file and add text to
it (press Ctrl+D to save).

2. Viewing File Contents:

1. cat filename: Displays the contents of a file.

2. less filename: Opens the file for scrolling through its content. o head
-n filename: Shows the first n lines of a file.

3. tail -n filename: Shows the last n lines of a

file.

3. Copying, Moving, and Deleting Files:

o cp source destination: Copies a file from one location to another. o

mv source destination: Moves or renames a file. o rm filename:


Deletes a file permanently.

4. File Permissions and Ownership:

1. chmod [permissions] filename: Updates the file permissions (e.g.,


chmod 755 [Link]).

2. chown user:group filename: Changes who


owns a file.

System Calls

1. open(): Used to open a file in a program int fd = open("filename",


O_RDONLY); 2. read(): Reads data from a file descriptor.
1. write(): Writes data to a file descriptor. ssize_t bytes_written = write(fd,
buffer, size);
2. close(): Closes a file once done using it.

int status = close(fd);


3. lseek(): Moves the file pointer to a specific location.

off_t offset = lseek(fd, position, SEEK_SET);

1. Directory Management

Linux Commands

1. Navigating Directories:

1. pwd: Displays the current directory path. o cd


/path/to/directory: Lets you move to another directory.

2. Listing Directory Contents:

1. ls: Lists files and directories in the current folder. o ls -l:


Shows a detailed list of files, including permissions and

sizes. o ls -a: Lists hidden files too.

2. Creating and Removing Directories:

1. mkdir directory_name: Creates a new folder.

2. rmdir directory_name: Deletes an empty folder. o rm -r


directory_name: Deletes a folder and all its contents.

System Calls

1. mkdir(): Used to create a directory programmatically.

int status = mkdir("dirname", 0755);

2. rmdir(): Deletes an empty directory.

int status = rmdir("dirname");

3. opendir(): Opens a directory so you can read its contents.

DIR *dir = opendir("dirname");

4. readdir(): Reads files and folders inside a directory.

struct dirent *entry = readdir(dir);

5. closedir(): Closes a directory after you’re done reading it.

int status = closedir(dir);

3. Process Management

Linux Commands
1. Viewing and Managing Processes:

1. ps: Lists processes currently running on


the system. o top: Shows a live, detailed view of running

processes. o htop: An easier-to-use version of top (if


installed).

2. Starting, Stopping, and Resuming Processes:

1. &: Runs a process in the background (e.g.,


command &).

2. jobs: Lists all running or paused


background processes. o fg: Brings a paused process
to the
foreground. o kill PID: Terminates a process using its

process ID (PID).

System Calls

1. fork(): Creates a new process (child process). pid_t pid = fork();


2. exec(): Replaces the current process with a new program.

execl("/bin/ls", "ls", "-l", NULL);

3. wait(): Makes the parent process wait for its child to finish.

pid_t pid = wait(NULL);

4. exit(): Terminates the current process. exit(0);


5. getpid() and getppid(): Gets the current process ID and its parent process
ID.

pid_t pid = getpid(); pid_t ppid =


getppid();
6. kill(): Sends a signal to a process (usually to stop it).

int status = kill(pid, SIGTERM);


Experiment 2
Write Shell script to do the following:

➢ Display top 10 processes in descending order

➢ Display processes with highest memory usage.

➢ Display current logged in user and log name


➢ Display current shell, Home directory, operating
system type, current path setting, current working
directory.

➢ Display OS version, release number, kernel version.

➢ Illustrate the use of sort,grep,awk,etc.


Conclusion : Hence, we have successfully implemented
the commands
EXPERIMENT NO. 3

Instructions:

A) Create a child process in Linux using the fork system call. From the child process obtain the process
ID of both child and parent by using getpid and getppid system call.

B) Explore the following system calls: open, read, write, close, getpid, setpid, getuid, getgid,getegid,
geteuid etc.

Program:

A)

B)
Output:

A)

B)

Conclusion: Hence we have created a child process in Linux using the fork
system call. And we have also explored the following system calls: open, read,
write, close, getpid, setpid, getuid, getgid,getegid, geteuid etc.
EXPERIMENT-4

Aim: Implement basic commands of Linux like ls, cp,


mv, rm and others using kernel APIs.

Theory:
ls: The ls command is used to list the contents of
a directory. It allows users to see files and
subdirectories within a specified directory, and it
supports various options for detailed output, such
as listing file permissions, file sizes, and
modification timestamps.
cp: The cp command is used to copy files and
directories from one location to another. It can
also be used to overwrite existing files or
preserve file attributes like timestamps during the
copy operation.
mv: The mv command is used for renaming files
and directories or moving them from one location
to another. It performs the operation by either
renaming a file within the same directory or
transferring it to a different directory.

rm: The rm (remove) command is used to delete


files and directories. It can delete single or
multiple files, and with the -r option, it can
recursively remove directories and their contents.

mkdir: The mkdir (make directory) command is


used to create a new directory in the file system.
It supports options for setting directory
permissions and for creating nested directories in
a single command.

ls command:
Code:
Output:
cp command:
Code:
Output:
mv command:
Code:
Output:

rm command:
Code:

Output:
mkdir command:
Code:

Output:
Conclusion: Hence, we have implemented basic
commands of Linux like ls, cp, mv, rm and mkdir using
kernel APIs and system calls.
Experiment No. 5

Aim: Write a C program to implement any CPU scheduling algorithms like FCFS, SJF, or
Priority etc.

Code:
#include <stdio.h>

// Function to calculate waiting time


void calculateWaitingTime(int processes[], int n, int bt[], int wt[]) {
wt[0] = 0; // First process has no waiting time

for (int i = 1; i < n; i++) {


wt[i] = bt[i - 1] + wt[i - 1];
}
}

// Function to calculate turn around time


void calculateTurnAroundTime(int processes[], int n, int bt[], int wt[], int tat[]) {
for (int i = 0; i < n; i++) {
tat[i] = bt[i] + wt[i];
}
}

// Function to calculate average time and display process details


void findAvgTime(int processes[], int n, int bt[]) {
int wt[n], tat[n];
int total_wt = 0, total_tat = 0;

calculateWaitingTime(processes, n, bt, wt);


calculateTurnAroundTime(processes, n, bt, wt, tat);

printf("Processes Burst time Waiting time Turnaround time\n");


for (int i = 0; i < n; i++) {
total_wt += wt[i];
total_tat += tat[i];
printf("%d\t\t%d\t\t%d\t\t%d\n", processes[i], bt[i], wt[i], tat[i]);
}

printf("\nAverage waiting time = %.2f", (float)total_wt / n);


printf("\nAverage turnaround time = %.2f\n", (float)total_tat / n);
}

int main() {
int n;

printf("Enter the number of processes: ");


scanf("%d", &n);

int processes[n], burst_time[n];

printf("Enter the burst time for each process:\n");


for (int i = 0; i < n; i++) {
processes[i] = i + 1;
printf("Process %d: ", i + 1);
scanf("%d", &burst_time[i]);
}

findAvgTime(processes, n, burst_time);

return 0;
}

Output:

Conclusion: Hence we have implemented CPU scheduling algorithms like FCFS, SJF, or
Priority etc.
OS Experiment No. 6

AIM: TO IMPLEMENT BANKERS ALGORITHM IN C.

THEORY:

The Banker's Algorithm, developed by Edsger Dijkstra, is a resource allocation


and deadlock avoidance technique used in operating systems. It ensures that a
system remains in a safe state by simulating the allocation of resources and
checking for potential deadlock conditions before granting requests.

Key Concepts:
7. Safe State: A state where there exists a sequence of processes that can
complete without leading to a deadlock.
8. Resource Allocation: Processes must declare their maximum resource
needs.
9. Need Matrix: Calculates remaining resource requirements for each
process.

How Banker's Algorithm Works:


10. Resource Request: A process requests resources.
11. Safety Check: The algorithm checks if granting the request leads to
a safe state.
12. Resource Allocation: If safe, resources are allocated; otherwise, the
request is denied.

CODE:

#include <stdio.h>

#define MAX 5 // Maximum number of processes

#define RESOURCES 3 // Maximum number of resources

// Function to check if the system is in a safe state


int isSafeState(int processes[], int avail[], int max[][RESOURCES], int allot[]
[RESOURCES], int n, int m) {

int need[MAX][RESOURCES];

int work[RESOURCES];

int finish[MAX];

int safeSeq[MAX];

int count = 0;

// Calculate the need matrix

for (int i = 0; i < n; i++) {

for (int j = 0; j < m; j++) {

need[i][j] = max[i][j] - allot[i][j];

// Initialize work and finish arrays

for (int i = 0; i < m; i++) {

work[i] = avail[i];

for (int i = 0; i < n; i++) {

finish[i] = 0;

// Check for safe sequence


while (count < n) {

int found = 0;

for (int p = 0; p < n; p++) {

if (finish[p] == 0) {

int flag = 1;

// Check if all the needs of the process can be satisfied

for (int r = 0; r < m; r++) {

if (need[p][r] > work[r]) {

flag = 0;

break;

// If the process can be finished

if (flag == 1) {

for (int r = 0; r < m; r++) {

work[r] += allot[p][r]; // Add allocated resources to work

safeSeq[count++] = p; // Add process to the safe sequence

finish[p] = 1; // Mark process as finished

found = 1;

}
}

// If no process can be finished, the system is in an unsafe state

if (found == 0) {

printf("System is in an unsafe state!\n");

return 0;

// If we reach here, the system is in a safe state

printf("System is in a safe state!\nSafe sequence is: ");

for (int i = 0; i < n; i++) {

printf("%d ", safeSeq[i]);

printf("\n");

return 1;

int main() {

int processes[MAX] = {0, 1, 2, 3, 4}; // Process identifiers

int avail[RESOURCES] = {3, 3, 2}; // Available resources

int max[MAX][RESOURCES] = {

{7, 5, 3}, // Max resources required by process 0


{3, 2, 2}, // Max resources required by process 1

{9, 0, 2}, // Max resources required by process 2

{2, 2, 2}, // Max resources required by process 3

{4, 3, 3} // Max resources required by process 4

};

int allot[MAX][RESOURCES] = {

{0, 1, 0}, // Allocated resources to process 0

{2, 0, 0}, // Allocated resources to process 1

{3, 0, 2}, // Allocated resources to process 2

{2, 1, 1}, // Allocated resources to process 3

{0, 0, 2} // Allocated resources to process 4

};

// Run the Banker's Algorithm to check for a safe state

isSafeState(processes, avail, max, allot, 5, 3);

return 0;

erifying link .

OUTPUT:
CONCLUSION:
The Banker's Algorithm is a powerful tool for avoiding deadlocks by ensuring that systems
remain in a safe state. It is particularly useful in environments where predictability and
stability are crucial, such as banking systems. However, it requires detailed knowledge of
maximum resource needs and can be computationally intensive. Despite these limitations, the
Banker's Algorithm remains a foundational technique in operating systems for managing
resources efficiently and preventing deadlocks.
EXPERIMENT NO. 7
Aim: Write a C program to implement solution of Producer
Consumer problem through Semaphore.
Theory:
The Producer-Consumer problem is a classic concurrency
problem that demonstrates the need for synchronization when
multiple processes or threads share a common buffer.
Semaphores are a fundamental tool used to achieve this
synchronization. Here's a breakdown of the theory, suitable for
an assignment:
1. The Producer-Consumer Problem:
Scenario:
A "producer" process generates data and places it into a
shared buffer.
A "consumer" process retrieves data from the same buffer
and processes it.
Challenges:
Buffer Overflow: The producer shouldn't add data when the
buffer is full.
Buffer Underflow: The consumer shouldn't retrieve data
when the buffer is empty.
Race Conditions: Simultaneous access to the buffer by the
producer and consumer must be prevented to avoid data
corruption.
2. Semaphores for Synchronization:
Definition:
A semaphore is an integer variable that, apart from
initialization, is accessed only through two standard atomic
operations: wait() (or P) and signal() (or V).
wait(): Decrements the semaphore value. If the value
becomes negative, the process is blocked.
signal(): Increments the semaphore value. If there are
processes blocked on the semaphore, one is unblocked.
Types:
Counting Semaphore: Can have any non-negative integer
value. Used to control access to resources with multiple
instances.
Binary Semaphore (Mutex): Can have values 0 or 1. Used to
enforce mutual exclusion.
3. Solution Using Semaphores:
To solve the Producer-Consumer problem, we use three
semaphores:
empty:
A counting semaphore that indicates the number of empty
slots in the buffer.
Initialized to the buffer's capacity.
full:
A counting semaphore that indicates the number of filled
slots in the buffer.
Initialized to 0.
mutex:
A binary semaphore (mutex) that ensures mutual exclusion
when accessing the buffer.
Initialized to 1.
4. Producer Process:
do {
// Produce an item
wait(empty); // Decrement empty slots, wait if buffer is full
wait(mutex); // Acquire lock for buffer access
// Add item to buffer
signal(mutex); // Release lock
signal(full); // Increment filled slots
} while (true);
5. Consumer Process:
do {
wait(full); // Decrement filled slots, wait if buffer is empty
wait(mutex); // Acquire lock for buffer access
// Remove item from buffer
signal(mutex); // Release lock
signal(empty); // Increment empty slots
// Consume the item
} while (true);
6. Explanation:
The empty semaphore ensures that the producer doesn't add
items to a full buffer.
The full semaphore ensures that the consumer doesn't
remove items from an empty buffer.
The mutex semaphore ensures that only one process
(producer or consumer) accesses the buffer at a time,
preventing race conditions.
Program:
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>

#define N 5
#define M 20

int b[N], in = 0, out = 0, c = 0;


sem_t e, f;
pthread_mutex_t m;

void* p(void* a) {
while (1) {
sem_wait(&e); // Wait for space in buffer
pthread_mutex_lock(&m);
if (c >= M) {
pthread_mutex_unlock(&m);
sem_post(&f);
break;
}
b[in] = rand() % 100;
printf("Producer produced an item %d\n", b[in]);
in = (in + 1) % N;
c++;
pthread_mutex_unlock(&m);
sem_post(&f); // Signal consumer to consume
}
return NULL;
}

void* c_(void* a) {
while (1) {
sem_wait(&f); // Wait for an item to consume
pthread_mutex_lock(&m);
if (c >= M && out == in) { // If all items have been
produced and consumed
pthread_mutex_unlock(&m);
sem_post(&e);
break;
}
printf("Consumer com
nsumed an item %d\n", b[out]);
out = (out + 1) % N;
pthread_mutex_unlock(&m);
sem_post(&e); // Signal producer to produce
}
return NULL;
}

int main() {
pthread_t pt, ct;
sem_init(&e, 0, N); // N spaces in the buffer
sem_init(&f, 0, 0); // No items in the buffer initially
pthread_mutex_init(&m, NULL);
pthread_create(&pt, NULL, p, NULL);
pthread_create(&ct, NULL, c_, NULL);
pthread_join(pt, NULL);
pthread_join(ct, NULL);
sem_destroy(&e);
sem_destroy(&f);
pthread_mutex_destroy(&m);
return 0;
}
Output:

Conclusion: Hence, we have successfully implemented


solution of producer consumer problem through Semaphore.
OS EXP:8
Aim: Write a C program to implement solution of Dining Philosophers' problem
through Semaphore
Theory:
The Dining Philosophers Problem is a well-known synchronization problem in
computer science, introduced by Edsger Dijkstra in 1965. It serves as a classic
example to illustrate the challenges of resource sharing and process
synchronization in concurrent computing environments.
Problem Description
In this scenario, five philosophers are seated around a circular table. Each
philosopher alternates between thinking and eating. The eating process requires
two chopsticks (or forks), one from their left and one from their right. The key
constraints are:
[Link] Exclusion: A chopstick can only be used by one philosopher at a
time.
[Link] Holding: A philosopher must hold one chopstick while waiting
for the other.
[Link]-preemption: A philosopher cannot forcibly take a chopstick from
another philosopher.
[Link] Wait: Each philosopher may be waiting for a chopstick held by
another philosopher, forming a circular chain.
Several strategies have been proposed to resolve the issues presented by this
problem:
[Link] Approach: Introduce a waiter (or arbitrator) who controls access
to the chopsticks. Philosophers must request permission from the waiter to
pick up both chopsticks, ensuring that only one philosopher can eat at a
time.
[Link] the Number of Philosophers: Allowing only four out of five
philosophers to sit at the table at any given time can prevent deadlock. If
one philosopher is always left out, at least one will always be able to eat.
[Link]/Misra Solution: This distributed approach allows philosophers to
communicate indirectly through request messages for forks, thus avoiding
central control while still managing resource allocation effectively.
[Link] Chopstick Order: Philosophers can adopt a strategy where
even-numbered philosophers pick up the right chopstick first and then the
left, while odd-numbered ones do the opposite. This breaks the circular
wait condition.
Program:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#define NUM_PHILOSOPHERS 5
sem_t chopsticks[NUM_PHILOSOPHERS];
void* philosopher(void* num) {
int id = *(int*)num;
while (1) {
printf("Philosopher %d is thinking.\n", id);
sleep(1); // Simulate thinking time
// Pick up the left chopstick
sem_wait(&chopsticks[id]);
printf("Philosopher %d picked up left chopstick.\n", id);
// Pick up the right chopstick
sem_wait(&chopsticks[(id + 1) % NUM_PHILOSOPHERS]);
printf("Philosopher %d picked up right chopstick.\n", id);
// Eat
printf("Philosopher %d is eating.\n", id);
sleep(2); // Simulate eating time
// Put down the right chopstick
sem_post(&chopsticks[(id + 1) % NUM_PHILOSOPHERS]);
printf("Philosopher %d put down right chopstick.\n", id);
// Put down the left chopstick
sem_post(&chopsticks[id]);
printf("Philosopher %d put down left chopstick.\n", id);
}
}
int main() {
pthread_t philosophers[NUM_PHILOSOPHERS];
int philosopher_ids[NUM_PHILOSOPHERS];
// Initialize semaphores for each chopstick
for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
sem_init(&chopsticks[i], 0, 1); // Binary semaphore for each
chopstick
}
// Create philosopher threads
for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
philosopher_ids[i] = i;
pthread_create(&philosophers[i], NULL, philosopher,
&philosopher_ids[i]);
}
// Wait for philosopher threads to finish (they won't in this case)
for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
pthread_join(philosophers[i], NULL);
}
// Destroy semaphores (not reached in this case)
for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
sem_destroy(&chopsticks[i]);
}
return 0;
}
Output:

Conclusion: The Dining Philosophers Problem exemplifies critical issues in concurrent


programming, such as deadlock, resource contention, and synchronization. Understanding
these principles is vital for designing robust concurrent algorithms that facilitate smooth
operation in complex computing environments.
Experiment 9

AIM: Write a program in C to implement dynamic partitioning algorithms i.e. first


fit, best fit, Worst fit, etc.

THEORY:

Dynamic partitioning algorithms are used in memory management to allocate


memory efficiently among processes. These algorithms include First Fit, Best Fit,
Worst Fit, and Next Fit.

21. First Fit: Allocates the first available partition that is large enough to
accommodate a process. It is simple and fast but may lead to
fragmentation.
22. Best Fit: Allocates the smallest partition that can fit a process, minimizing
internal fragmentation but requiring more time to search for the optimal
partition.
23. Worst Fit: Allocates the largest available partition, which maximizes internal
fragmentation and is generally inefficient.
24. Next Fit: Similar-to First Fit but starts searching from the last allocation
point, potentially leading to more fragmentation than First Fit.

Dynamic partitioning allows for variable-sized memory blocks to be allocated as


needed, reducing internal fragmentation and improving memory utilization.
However, the choice of algorithm depends on the trade-off between speed and
efficiency in minimizing fragmentation.

PROGRAM:

#include <stdio.h>
#include <stdlib.h>

#define MAX_BLOCKS 100


#define MAX_PROCESSES 100

// Structure to represent a memory block


typedef struct {
int size;
int isFree;
} MemoryBlock;
// Structure to represent a process
typedef struct {
int id;
int size;
} Process;

// Function prototypes
void firstFit(MemoryBlock blocks[], int blockCount, Process processes[], int
processCount);
void bestFit(MemoryBlock blocks[], int blockCount, Process processes[], int
processCount);
void worstFit(MemoryBlock blocks[], int blockCount, Process processes[], int
processCount);

int main() {
MemoryBlock blocks[MAX_BLOCKS];
Process processes[MAX_PROCESSES];
int blockCount, processCount, choice;

// Input memory blocks


printf("Enter the number of memory blocks: ");
scanf("%d", &blockCount);
for (int i = 0; i < blockCount; i++) {
printf("Enter size of block %d: ", i + 1);
scanf("%d", &blocks[i].size);
blocks[i].isFree = 1; // Initially, all blocks are free
}

// Input processes
printf("Enter the number of processes: ");
scanf("%d", &processCount);
for (int i = 0; i < processCount; i++) {
printf("Enter size of process %d: ", i + 1);
scanf("%d", &processes[i].size);
processes[i].id = i + 1; // Assign process ID
}
// Menu for allocation algorithms
printf("\nChoose the allocation algorithm:\n");
printf("1. First Fit\n");
printf("2. Best Fit\n");
printf("3. Worst Fit\n");
printf("Enter your choice: ");
scanf("%d", &choice);

switch (choice) {
case 1:
firstFit(blocks, blockCount, processes, processCount);
break;
case 2:
bestFit(blocks, blockCount, processes, processCount);
break;
case 3:
worstFit(blocks, blockCount, processes, processCount);
break;
default:
printf("Invalid choice!\n");
break;
}

return 0;
}

void firstFit(MemoryBlock blocks[], int blockCount, Process processes[], int


processCount) {
printf("\nFirst Fit Allocation:\n");
for (int i = 0; i < processCount; i++) {
for (int j = 0; j < blockCount; j++) {
if (blocks[j].isFree && blocks[j].size >= processes[i].size) {
blocks[j].isFree = 0; // Mark block as allocated
printf("Process %d allocated to Block %d\n", processes[i].id, j + 1);
break;
}
}
}
}
void bestFit(MemoryBlock blocks[], int blockCount, Process processes[], int
processCount) {
printf("\nBest Fit Allocation:\n");
for (int i = 0; i < processCount; i++) {
int bestIndex = -1;
for (int j = 0; j < blockCount; j++) {
if (blocks[j].isFree && blocks[j].size >= processes[i].size) {
if (bestIndex == -1 || blocks[j].size < blocks[bestIndex].size) {
bestIndex = j; // Update best index
}
}
}
if (bestIndex != -1) {
blocks[bestIndex].isFree = 0; // Mark block as allocated
printf("Process %d allocated to Block %d\n", processes[i].id, bestIndex +
1);
} else {
printf("Process %d could not be allocated\n", processes[i].id);
}
}
}

void worstFit(MemoryBlock blocks[], int blockCount, Process processes[], int


processCount) {
printf("\nWorst Fit Allocation:\n");
for (int i = 0; i < processCount; i++) {
int worstIndex = -1;
for (int j = 0; j < blockCount; j++) {
if (blocks[j].isFree && blocks[j].size >= processes[i].size) {
if (worstIndex == -1 || blocks[j].size > blocks[worstIndex].size) {
worstIndex = j; // Update worst index
}
}
}
if (worstIndex != -1) {
blocks[worstIndex].is

OUTPUT:
CONCLUSION:

Hence, we have successfully implemented dynamic partitioning algorithms i.e.


first fit, best fit, Worst fit, etc.
EXPERIMENT :10

Aim: Write a C program to implement solution of Dining Philosophers' problem


through Semaphore
Theory:
FIFO-based Solution to the Dining Philosophers Problem
25. The Dining Philosophers Problem is a synchronization challenge where
philosophers alternate between thinking and eating. To eat, a philosopher
needs two chopsticks, one from their left and one from their right. The key
constraints are mutual exclusion, resource holding, non-preemption, and
circular wait, which can lead to deadlock.

26. Solution Using FIFO (First-In-First-Out) Policy

27. In the provided code, the FIFO page replacement policy is applied to
resolve resource contention among philosophers:
28. Philosophers: Represented as processes trying to acquire chopsticks
(resources).
29. Chopsticks (Frames): There are 3 chopsticks available, represented by
frames that store whether a chopstick is available or not (-1 for empty).
30. Page Faults: If a philosopher cannot get both chopsticks, a page fault
occurs, which simulates waiting for resources.
31. FIFO Replacement: When all chopsticks are occupied, the oldest
chopstick (FIFO order) is replaced, allowing one philosopher to always
eat.

32. Key Points:

33. Mutual Exclusion: A chopstick can only be used by one philosopher at a


time.
34. Resource Holding: A philosopher can hold one chopstick while waiting
for the other.
35. Non-preemption: Philosophers cannot forcibly take chopsticks.
36. Circular Wait: Avoided by using the FIFO replacement strategy, ensuring
resources are allocated in a fixed order.
Program:

#include <stdio.h>

int main() {
int incomingStream[] = {4, 1, 2, 4, 5};
int pageFaults = 0;
int frames = 3;
int m, n, s, pages;

pages = sizeof(incomingStream) / sizeof(incomingStream[0]);

printf("Incoming \t Frame 1 \t Frame 2 \t Frame 3\n");

int temp[frames];
for (m = 0; m < frames; m++) {
temp[m] = -1;
}

for (m = 0; m < pages; m++) {


s = 0;

for (n = 0; n < frames; n++) {


if (incomingStream[m] == temp[n]) {
s++;
pageFaults--;
}
}
pageFaults++;

if ((pageFaults <= frames) && (s == 0)) {


temp[m] = incomingStream[m];
} else if (s == 0) {
temp[(pageFaults - 1) % frames] = incomingStream[m];
}

printf("\n");
printf("%d\t\t", incomingStream[m]);
for (n = 0; n < frames; n++) {
if (temp[n] != -1) {
printf(" %d\t\t", temp[n]);
} else {
printf(" - \t\t");
}
}
}

printf("\nTotal Page Faults:\t%d\n", pageFaults);


return 0;
}

Output:
Conclusion:

This approach mimics FIFO page replacement to handle resource allocation in


the Dining Philosophers Problem, preventing deadlock and ensuring fair access
to chopsticks.

You might also like