OS LAB PRACTICAL
FILE
Name – Aanya Singh
Registration Number – 23BCE10956
Slot – A14+D11+D12
INDEX: -
Sr Title Practical Date of Date of Sign
No. number completion submission
1 Study of 1 3-12-24 16-12-24
Hardware/Software
requirement of
various operating
system.
2 Implement CPU 2 3-12-24 16-12-24
scheduling policies
3 File Storage 3 3-12-24 16-12-24
Allocation
Techniques
4 Contiguous 4 5-12-24 16-12-24
Allocation
Techniques
5 External and 5 5-12-24 16-12-24
Internal
Fragmentation
6 External and 6 5-12-24 16-12-24
Internal
Fragmentation
7 Resource 7 8-12-24 16-12-24
Allocation Graph
(RAG)
8 Bankers Algorithm 8 12-12-24 16-12-24
9 Wait Graph 9 12-12-24 16-12-24
10 Inter process 10 12-12-24 16-12-24
Communication –
Semaphore
11 FORK and JOIN 11 12-12-24 16-12-24
construct
EXPERIMENT – 1:-
AIM:Defining function inside class
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int main() {
pid_t pid = fork();
if (pid < 0) {
perror("Fork failed");
exit(1);
} else if (pid == 0) { // Child process
char *args[] = {"ls", "-la", NULL};
execvp("ls", args);
perror("execvp failed");
exit(1);
} else { // Parent process
wait(NULL);
cout << "Child process completed.\n";
}
return 0;
}
Experiment – 2: -
Aim: - I. Implementing CPU scheduling policies :-
(a) SJF
(b) Priority
(c) FCFS
(d) Multi-level queue
a) SJF:-
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Process {
int pid;
int burst_time;
int arrival_time;
};
bool compareArrivalTime(Process a, Process b) {
return a.arrival_time < b.arrival_time;
}
bool compareBurstTime(Process a, Process b) {
return a.burst_time < b.burst_time;
}
void SJF(vector<Process> processes) {
// Sort processes by arrival time
sort(processes.begin(), processes.end(), compareArrivalTime);
int time = 0, waiting_time = 0, turnaround_time = 0;
cout << "Gantt Chart:" << endl;
for (int i = 0; i < processes.size(); i++) {
// Print the process name and its execution time
cout << processes[i].pid << " ";
// Calculate waiting time and turnaround time
waiting_time += time - processes[i].arrival_time;
turnaround_time += time + processes[i].burst_time -
processes[i].arrival_time;
// Increment time
time += processes[i].burst_time;
}
cout << endl;
cout << "Average Waiting Time: " << (float)waiting_time / processes.size() <<
endl;
cout << "Average Turnaround Time: " << (float)turnaround_time /
processes.size() << endl;
}
int main() {
// ... (Code to generate processes randomly)
SJF(processes);
return 0;
}
b) Priority: -
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Process {
int pid;
int burst_time;
int arrival_time;
int priority;
};
bool compareArrivalTime(Process a, Process b) {
return a.arrival_time < b.arrival_time;
}
bool comparePriority(Process a, Process b) {
return a.priority < b.priority;
}
void PriorityScheduling(vector<Process> processes) {
// Sort processes by arrival time
sort(processes.begin(), processes.end(), compareArrivalTime);
int time = 0, waiting_time = 0, turnaround_time = 0;
cout << "Gantt Chart:" << endl;
while (!processes.empty()) {
// Find the process with the highest priority among the arrived processes
int highest_priority = INT_MAX;
int highest_priority_index = -1;
for (int i = 0; i < processes.size(); i++) {
if (processes[i].arrival_time <= time && processes[i].priority <
highest_priority) {
highest_priority = processes[i].priority;
highest_priority_index = i;
}
}
if (highest_priority_index 1 == -1) {
// No process has arrived yet
time++;
continue;
}
Process current_process = processes[highest_priority_index];
processes.erase(processes.begin() + highest_priority_index);
// Print the process name and its execution time
cout << current_process.pid << " ";
// Calculate waiting time and turnaround time
waiting_time += time - current_process.arrival_time;
turnaround_time += time + current_process.burst_time -
current_process.arrival_time;
// Increment time
time += current_process.burst_time;
}
cout << endl;
cout << "Average Waiting Time: " << (float)waiting_time / processes.size() <<
endl;
cout << "Average Turnaround Time: " << (float)turnaround_time /
processes.size() << endl;
}
int main() {
// ... (Code to generate processes randomly, including priority)
PriorityScheduling(processes);
return 0;
}
c) FCFS: -
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Process {
int pid;
int burst_time;
int arrival_time;
};
bool compareArrivalTime(Process a, Process b) {
return a.arrival_time < b.arrival_time;
}
void FCFS(vector<Process> processes) {
// Sort processes by arrival time
sort(processes.begin(), processes.end(), compareArrivalTime);
int time = 0, waiting_time = 0, turnaround_time = 0;
cout << "Gantt Chart:" << endl;
for (int i = 0; i < processes.size(); i++) {
// Print the process name and its execution time
cout << processes[i].pid << " ";
// Calculate waiting time and turnaround time
waiting_time += time - processes[i].arrival_time;
turnaround_time += time + processes[i].burst_time -
processes[i].arrival_time;
// Increment time
time += processes[i].burst_time;
}
cout << endl;
cout << "Average Waiting Time: " << (float)waiting_time / processes.size()
<< endl;
cout << "Average Turnaround Time: " << (float)turnaround_time /
processes.size() << endl;
}
int main() {
// ... (Code to generate processes randomly)
FCFS(processes);
return 0;
}
d) Multilevel: -
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct Process {
int pid;
int burst_time;
int arrival_time;
int priority;
};
// Function to simulate FCFS scheduling within a queue
void FCFS(queue<Process>& q, int& time) {
Process p = q.front();
q.pop();
time += p.burst_time;
cout << p.pid << " ";
}
// Function to simulate SJF scheduling within a queue
void SJF(queue<Process>& q, int& time) {
// Sort the queue by burst time
sort(q.begin(), q.end(), [](Process a, Process b) { return a.burst_time <
b.burst_time; });
Process p = q.front();
q.pop();
time += p.burst_time;
cout << p.pid << " ";
}
void MultiLevelQueue(vector<Process> processes) {
// Create multiple queues based on priority levels
queue<Process> queue1, queue2, queue3;
// Assign processes to queues based on priority
for (Process p : processes) {
if (p.priority == 1) {
queue1.push(p);
} else if (p.priority == 2) {
queue2.push(p);
} else {
queue3.push(p);
}
}
int time = 0;
cout << "Gantt Chart:" << endl;
while (!queue1.empty() || !queue2.empty() || !queue3.empty()) {
// Schedule from the highest priority queue
if (!queue1.empty()) {
FCFS(queue1, time); // Or SJF, Priority, etc.
} else if (!queue2.empty()) {
SJF(queue2, time); // Or FCFS, Priority, etc.
} else {
FCFS(queue3, time); // Or SJF, Priority, etc.
}
}
cout << endl;
// Calculate and print performance metrics (e.g., average waiting time,
turnaround time)
}
int main() {
// ... (Code to generate processes randomly, including priority)
MultiLevelQueue(processes);
return 0;
}
Experiment – 3: -
Aim: -
I. Implement file storage allocation techniques:
(a) Contiguous (using array)
(b) Linked –list (using linked list)
(c) Indirect allocation (indexing)
a) Contiguous Allocation
#include <iostream>
#include <vector>
using namespace std;
struct Block {
int block_id;
int size;
bool is_free;
};
void contiguousAllocation(vector<Block>& blocks, int file_size) {
int bestFitIndex = -1;
int bestFitSize = INT_MAX;
// Find the best fit block
for (int i = 0; i < blocks.size(); ++i) {
if (blocks[i].is_free && blocks[i].size >= file_size) {
if (blocks[i].size < bestFitSize) {
bestFitIndex = i;
bestFitSize = blocks[i].size;
}
}
}
if (bestFitIndex != -1) {
// Allocate the block
blocks[bestFitIndex].is_free = false;
cout << "Allocated block " << bestFitIndex + 1 << " of size " <<
bestFitSize << " to the file." << endl;
} else {
cout << "Memory allocation failed. Not enough contiguous
free space." << endl;
}
}
int main() {
vector<Block> blocks = {
{1, 10, true},
{2, 5, true},
{3, 8, false},
{4, 12, true},
{5, 6, true}
};
int file_size = 7;
contiguousAllocation(blocks, file_size);
// Print the updated block status
for (const Block& block : blocks) {
cout << "Block " << block.block_id << ": " << (block.is_free ?
"Free" : "Allocated") << ", Size: " << block.size << endl;
}
return 0;
}
b)Linked Allocation
#include <iostream>
using namespace std;
struct Node {
int block_id;
int size;
bool is_free;
Node* next;
};
void linkedAllocation(Node*& head, int file_size) {
Node* current = head;
int total_allocated = 0;
while (current != nullptr && total_allocated < file_size) {
if (current->is_free && current->size >= file_size) {
// Allocate the block
current->is_free = false;
total_allocated += current->size;
cout << "Allocated block " << current->block_id << " of size "
<< current->size << " to the file." << endl;
return;
}
current = current->next;
}
cout << "Memory allocation failed. Not enough contiguous free
space." << endl;
}
int main() {
// Create a linked list of blocks
Node* head = new Node{1, 10, true, nullptr};
head->next = new Node{2, 5, true, nullptr};
head->next->next = new Node{3, 8, false, nullptr};
head->next->next->next = new Node{4, 12, true, nullptr};
int file_size = 7;
linkedAllocation(head, file_size);
// Print the updated linked list
Node* current = head;
while (current != nullptr) {
cout << "Block " << current->block_id << ": " << (current-
>is_free ? "Free" : "Allocated") << ", Size: " << current->size <<
endl;
current = current->next;
}
// Remember to deallocate the linked list to avoid memory leaks
// ... (code to deallocate the linked list)
return 0;
}
c)Indexed Allocation
#include <iostream>
#include <vector>
using namespace std;
struct Block {
int block_id;
bool is_free;
int file_id; // To identify the file it's allocated to
};
void indexedAllocation(vector<Block>& disk, int file_size, int
index_table_size, int file_id) {
int blocks_needed = (file_size + index_table_size - 1) /
index_table_size;
// Find contiguous free blocks
int start_block = -1;
for (int i = 0; i < disk.size(); i++) {
if (disk[i].is_free) {
if (start_block == -1) {
start_block = i;
}
if (i - start_block + 1 == blocks_needed) {
// Allocate contiguous blocks to the file
for (int j = start_block; j <= i; j++) {
disk[j].is_free = false;
disk[j].file_id = file_id; // Assign the file ID
}
// Update the index table (simple implementation)
int index = 0;
for (int j = start_block; j <= i; j++) {
// Assuming a simple index table as an array
// Replace this with a more suitable data structure if
needed
int index_entry = j;
// ... (Update the index table with index_entry)
}
return;
}
} else {
start_block = -1;
}
}
cout << "Memory allocation failed. Not enough contiguous free
space." << endl;
}
int main() {
const int DISK_SIZE = 10;
vector<Block> disk(DISK_SIZE);
// Initialize disk space
for (int i = 0; i < DISK_SIZE; i++) {
disk[i].block_id = i;
disk[i].is_free = true;
disk[i].file_id = -1;
}
int file_size = 7;
int index_table_size = 3;
int file_id = 1; // Assign a unique file ID
indexedAllocation(disk, file_size, index_table_size, file_id);
// Print the disk status
for (const Block& block : disk) {
cout << "Block " << block.block_id << ": " << (block.is_free ?
"Free" : "Allocated") << ", File ID: " << block.file_id << endl;
}
return 0;
}
Experiment – 4: -
Aim: -
Implementation of Contiguous allocation
techniques:
(a) Worst-Fit
(b) Best-Fit
(c) First-Fit
1. Worst-Fit Allocation
#include <iostream>
using namespace std;
struct Block {
int block_id;
int size;
bool is_free;
};
void worstFit(Block blocks[], int n, int process_size) {
int worst_fit_idx = -1;
int worst_fit_size = 0;
for (int i = 0; i < n; i++) {
if (blocks[i].is_free && blocks[i].size >= process_size) {
if (blocks[i].size > worst_fit_size) {
worst_fit_idx = i;
worst_fit_size = blocks[i].size;
}
}
}
if (worst_fit_idx != -1) {
// Allocate the block
blocks[worst_fit_idx].is_free = false;
cout << "Allocated block " << worst_fit_idx + 1 << " of size " <<
worst_fit_size << " to the process." << endl;
} else {
cout << "Memory allocation failed.\n";
}
}
int main() {
int n = 5;
Block blocks[n] = {{1, 100}, {2, 500}, {3, 200}, {4, 300}, {5, 600}};
int process_size = 210;
worstFit(blocks, n, process_size);
// Print the memory allocation status
for (int i = 0; i < n; i++) {
cout << "Block " << i + 1 << ": " << (blocks[i].is_free ? "Free" :
"Allocated") << endl;
}
return 0;
}
2. Best-Fit Allocation
#include <iostream>
using namespace std;
struct Block {
int block_id;
int size;
bool is_free;
};
void bestFit(Block blocks[], int n, int process_size) {
int best_fit_idx = -1;
int best_fit_size = INT_MAX;
for (int i = 0; i < n; i++) {
if (blocks[i].is_free && blocks[i].size >= process_size) {
if (blocks[i].size < best_fit_size) {
best_fit_idx = i;
best_fit_size = blocks[i].size;
}
}
}
if (best_fit_idx != -1) {
// Allocate the block
blocks[best_fit_idx].is_free = false;
cout << "Allocated block " << best_fit_idx + 1 << " of size " <<
best_fit_size << " to the process." << endl;
} else {
cout << "Memory allocation failed.\n";
}
}
int main() {
int n = 5;
Block blocks[n] = {{1, 100}, {2, 500}, {3, 200}, {4, 300}, {5, 600}};
int process_size = 210;
bestFit(blocks, n, process_size);
// Print the memory allocation status
for (int i = 0; i < n; i++) {
cout << "Block " << i + 1 << ": " << (blocks[i].is_free ? "Free" :
"Allocated") << endl;
}
return 0;
}
3. First-Fit Allocation
#include <iostream>
using namespace std;
struct Block {
int block_id;
int size;
bool is_free;
};
void firstFit(Block blocks[], int n, int process_size) {
for (int i = 0; i < n; i++) {
if (blocks[i].is_free && blocks[i].size >= process_size) {
// Allocate the block
blocks[i].is_free = false;
cout << "Allocated block " << i + 1 << " of size " <<
blocks[i].size << " to the process." << endl;
return;
}
}
cout << "Memory allocation failed.\n";
}
int main() {
int n = 5;
Block blocks[n] = {{1, 100}, {2, 500}, {3, 200}, {4, 300}, {5, 600}};
int process_size = 210;
firstFit(blocks, n, process_size);
// Print the memory allocation status
for (int i = 0; i < n; i++) {
cout << "Block " << i + 1 << ": " << (blocks[i].is_free ? "Free" :
"Allocated") << endl;
}
return 0;
}
Experiment – 5: -
Aim:- Calculation of external and internal
fragmentation.
• External Fragmentation: Occurs when there is enough total
free memory to satisfy a request, but it is not contiguous.
• Internal Fragmentation: Occurs when a process is allocated
more memory than it actually needs.
Implementation in C++
C++
#include <iostream>
#include <vector>
using namespace std;
struct Block {
int block_id;
int size;
bool is_free;
int allocated_to; // Process ID or file ID
};
void calculateFragmentation(vector<Block>& blocks, vector<int>
process_sizes) {
int external_fragmentation = 0, internal_fragmentation = 0;
for (Block block : blocks) {
if (block.is_free) {
external_fragmentation += block.size;
} else {
int wasted_space = block.size -
process_sizes[block.allocated_to - 1];
internal_fragmentation += wasted_space;
}
}
cout << "External Fragmentation: " << external_fragmentation <<
endl;
cout << "Internal Fragmentation: " << internal_fragmentation <<
endl;
}
// ... (Rest of the code for allocation algorithms)
int main() {
// ... (Initialize blocks and processes)
// Perform allocation (e.g., using first-fit, best-fit, or worst-fit)
// Calculate fragmentation
calculateFragmentation(blocks, process_sizes);
return 0;
}
Experiment 6:-
Aim: - Implementation of Compaction for the
continually changing memory layout and
calculate total movement of data.
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
struct Block {
int block_id;
int size;
bool is_free;
int allocated_to; // Process ID or file ID
};
void compactMemory(vector<Block>& blocks) {
int hole_start = 0;
int current_block = 0;
while (current_block < blocks.size()) {
if (blocks[current_block].is_free) {
// Skip to the next allocated block
current_block++;
continue;
}
// Shift the current block to the hole
if (current_block != hole_start) {
// Calculate the amount of data to be moved
int data_size = sizeof(Block);
// Shift the block and update its position
memmove(&blocks[hole_start], &blocks[current_block],
data_size);
blocks[hole_start].block_id = hole_start;
// Update the free space of the previous block
blocks[current_block].is_free = true;
}
hole_start += blocks[hole_start].size;
current_block++;
}
}
int main() {
int n = 5;
vector<Block> blocks = {
{1, 100, false, 1},
{2, 50, true},
{3, 200, true},
{4, 300, false, 2},
{5, 600, true}
};
// Simulate memory allocation (you can implement your own
allocation strategy)
// ...
// Compact the memory
compactMemory(blocks);
// Print the memory status after compaction
for (const Block& block : blocks) {
cout << "Block " << block.block_id << ": " << (block.is_free ?
"Free" : "Allocated to Process " + to_string(block.allocated_to)) <<
endl;
}
return 0;
}
Experiment 7: -
Aim:- Implementation of resource allocation graph
(RAG).
#include <iostream>
#include <vector>
using namespace std;
struct Process {
int id;
vector<int> requested;
vector<int> allocated;
};
struct Resource {
int id;
int instances;
};
void createRAG(vector<Process>& processes, vector<Resource>&
resources, int n, int m) {
int graph[n + m][n + m];
// Initialize the graph with 0s
for (int i = 0; i < n + m; ++i) {
for (int j = 0; j < n + m; ++j) {
graph[i][j] = 0;
}
}
// Create edges for process to resource requests
for (int i = 0; i < n; ++i) {
for (int j = 0; j < processes[i].requested.size(); ++j) {
graph[i][n + processes[i].requested[j]] = 1;
}
}
// Create edges for resource to process allocations
for (int i = 0; i < n; ++i) {
for (int j = 0; j < processes[i].allocated.size(); ++j) {
graph[n + processes[i].allocated[j]][i] = 1;
}
}
// Print the adjacency matrix representation
for (int i = 0; i < n + m; ++i) {
for (int j = 0; j < n + m; ++j) {
cout << graph[i][j] << " ";
}
cout << endl;
}
}
int main() {
// ... (Input process and resource information)
createRAG(processes, resources, n, m);
return 0;
}
Experiment – 8: -
Aim: - Implementation of Banker’s Algorithm.
#include <iostream>
using namespace std;
bool isSafe(int processes, int resources, int available[], int
maxm[][resources], int alloc[][resources], int need[][resources]) {
int finish[processes] = {0};
int work[resources];
// Copy available resources to work
for (int i = 0; i < resources; i++) {
work[i] = available[i];
}
int count = 0;
while (count < processes) {
bool found = false;
for (int p = 0; p < processes; p++) {
if (finish[p] == 0) {
int j;
for (j = 0; j < resources; j++) {
if (need[p][j] > work[j]) {
break;
}
}
if (j == resources) {
for (int k = 0; k < resources; k++) {
work[k] += alloc[p][k];
}
finish[p] = 1;
found = true;
count++;
}
}
}
if (!found) {
return false; // System is in unsafe state
}
}
return true; // System is in safe state
}
int main() {
int processes, resources;
cout << "Enter the number of processes: ";
cin >> processes;
cout << "Enter the number of resources: ";
cin >> resources;
int available[resources], maxm[processes][resources],
alloc[processes][resources], need[processes][resources];
// Input available resources
cout << "Enter available resources: ";
for (int i = 0; i < resources; i++) {
cin >> available[i];
}
// Input maximum demand matrix
cout << "Enter maximum demand matrix:\n";
for (int i = 0; i < processes; i++) {
for (int j = 0; j < resources; j++) {
cin >> maxm[i][j];
}
}
// Input allocation matrix
cout << "Enter allocation matrix:\n";
for (int i = 0; i < processes; i++) {
for (int j = 0; j < resources; j++) {
cin >> alloc[i][j];
}
}
// Calculate need matrix
for (int i = 0; i < processes; i++) {
for (int j = 0; j < resources; j++) {
need[i][j] = maxm[i][j] - alloc[i][j];
}
}
if (isSafe(processes, resources, available, maxm, alloc, need)) {
cout << "System is in a safe state.\n";
} else {
cout << "System is in an unsafe state.\n";
}
return 0;
}
Experiment – 9: -
Aim: - Conversion of resource allocation graph
(RAG) to wait-for-graph (WFG) for each
type of method used for storing graph.
#include <iostream>
#include <vector>
using namespace std;
struct Process {
int id;
vector<int> requested;
vector<int> allocated;
};
struct Resource {
int id;
int instances;
};
void createWaitForGraph(vector<Process>& processes,
vector<Resource>& resources, int n, int m) {
vector<vector<int>> wait_for_graph(n, vector<int>(n, 0));
// Identify waiting processes and create edges
for (int i = 0; i < n; ++i) {
for (int j = 0; j < processes[i].requested.size(); ++j) {
int resource_id = processes[i].requested[j];
for (int k = 0; k < n; ++k) {
if (k != i && processes[k].allocated[resource_id] > 0) {
wait_for_graph[i][k] = 1; // Process i is waiting for process
k
}
}
}
}
// Print the wait-for graph
cout << "Wait-for Graph:\n";
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
cout << wait_for_graph[i][j] << " ";
}
cout << endl;
}
}
int main() {
// Input number of processes and resources
int n, m;
cout << "Enter the number of processes: ";
cin >> n;
cout << "Enter the number of resources: ";
cin >> m;
// Input process information
vector<Process> processes(n);
for (int i = 0; i < n; ++i) {
processes[i].id = i;
cout << "Enter the number of resources requested by process "
<< i + 1 << ": ";
int num_requested;
cin >> num_requested;
for (int j = 0; j < num_requested; ++j) {
int resource_id;
cout << "Enter resource ID: ";
cin >> resource_id;
processes[i].requested.push_back(resource_id);
}
cout << "Enter the number of resources allocated to process " <<
i + 1 << ": ";
int num_allocated;
cin >> num_allocated;
for (int j = 0; j < num_allocated; ++j) {
int resource_id;
cout << "Enter resource ID: ";
cin >> resource_id;
processes[i].allocated.push_back(resource_id);
}
}
// Input resource information (optional, if needed)
vector<Resource> resources(m);
for (int i = 0; i < m; ++i) {
resources[i].id = i;
cout << "Enter the number of instances for resource " << i + 1 <<
": ";
cin >> resources[i].instances;
}
createWaitForGraph(processes, resources, n, m);
return 0;
}
Experiment – 10:-
Aim: - Implement the solution for Bounded Buffer
(Producer-Consumer) problem using
inter process communication technique –
Semaphores.
i. #include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
class BoundedBuffer {
public:
BoundedBuffer(int size) : buffer_size(size), in(0), out(0), count(0)
{
buffer = new int[size];
}
void produce(int item) {
unique_lock<mutex> lock(mutex_);
while (count == buffer_size) {
not_full.wait(lock);
}
buffer[in] = item;
in = (in + 1) % buffer_size;
count++;
not_empty.notify_one();
}
int consume() {
unique_lock<mutex> lock(mutex_);
while (count == 0) {
not_empty.wait(lock);
}
int item = buffer[out];
out = (out + 1) % buffer_size;
count--;
not_full.notify_one();
return item;
}
private:
int *buffer;
int buffer_size;
int in, out, count;
mutex mutex_;
condition_variable not_full, not_empty;
};
// ... (Producer and Consumer threads)
ii. #include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
class SharedResource {
public:
void read() {
unique_lock<mutex> lock(mutex_);
while (writer_count > 0) {
reader_count.wait(lock);
}
reader_count++;
lock.unlock();
// Read the shared resource
lock.lock();
reader_count--;
if (reader_count == 0) {
writer.notify_one();
}
lock.unlock();
}
void write() {
unique_lock<mutex> lock(mutex_);
while (reader_count > 0 || writer_count > 0) {
writer.wait(lock);
}
writer_count++;
// Write to the shared resource
writer_count--;
reader_count.notify_all();
writer.notify_one();
lock.unlock();
}
private:
mutex mutex_;
condition_variable reader_count, writer;
int reader_count = 0, writer_count = 0;
};
Experiment – 11: -
Aim: - FORK and JOIN construct
I. Average of Odd and Even Numbers
#include <iostream>
#include <vector>
using namespace std;
int main() {
int aadhar_number = 123456789012;
vector<int> digits;
while (aadhar_number > 0) {
digits.push_back(aadhar_number % 10);
aadhar_number /= 10;
}
int even_sum = 0, even_count = 0;
int odd_sum = 0, odd_count = 0;
for (int digit : digits) {
if (digit % 2 == 0) {
even_sum += digit;
even_count++;
} else {
odd_sum += digit;
odd_count++;
}
}
if (even_count > 0) {
double even_avg = static_cast<double>(even_sum) /
even_count;
cout << "Average of even digits: " << even_avg << endl;
} else {
cout << "No even digits found." << endl;
}
if (odd_count > 0) {
double odd_avg = static_cast<double>(odd_sum) /
odd_count;
cout << "Average of odd digits: " << odd_avg << endl;
} else {
cout << "No odd digits found." << endl;
}
return 0;
}
II. Additive Primes and Circular Primes
C++
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <vector>
using namespace std;
bool isPrime(int num) {
// ... (Implement prime number checking logic)
}
bool isCircularPrime(int num) {
// ... (Implement circular prime checking logic)
}
int main() {
vector<int> primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
pid_t pid = fork();
if (pid < 0) {
perror("Fork failed");
exit(1);
} else if (pid == 0) { // Child process
// Find circular primes
for (int i = 0; i < primes.size(); i++) {
if (isCircularPrime(primes[i])) {
cout << primes[i] << " is a circular prime.\n";
}
}
exit(0);
} else { // Parent process
// Find additive primes
for (int i = 0; i < primes.size(); i++) {
for (int j = i + 1; j < primes.size(); j++) {
if (isPrime(primes[i] + primes[j])) {
cout << primes[i] << " + " << primes[j] << " = " <<
primes[i] + primes[j] << " is a prime.\n";
}
}
}
wait(NULL); // Wait for child process to finish
}
return 0;
}