Wednesday, February 11, 2026

OS LAB Programs

5. Control the number of ports opened by the operating system with 

a) Semaphore b) Monitors.

A. Using Semaphore 

  • Suppose only 2 ports are available.
  • If more than 2 students request, others must wait.

Program:

#include <stdio.h>

#include <pthread.h>

#include <semaphore.h>

#include <unistd.h>

 

sem_t port;   // Semaphore variable

 

void* openPort(void* arg)

{

    int id = *(int*)arg;

 

    sem_wait(&port);   // Request a port

 

    printf("Student %d is using a port\n", id);

    sleep(2);  // Using port

 

    printf("Student %d released the port\n", id);

 

    sem_post(&port);   // Release port

 

    return NULL;

}

 

int main()

{

    pthread_t t1, t2, t3;

    int id1 = 1, id2 = 2, id3 = 3;

 

    sem_init(&port, 0, 2);  // Only 2 ports available

 

    pthread_create(&t1, NULL, openPort, &id1);

    pthread_create(&t2, NULL, openPort, &id2);

    pthread_create(&t3, NULL, openPort, &id3);

 

    pthread_join(t1, NULL);

    pthread_join(t2, NULL);

    pthread_join(t3, NULL);

 

    sem_destroy(&port);

 

    return 0;

}

       

OUTPUT:

Student 1 is using a port

Student 2 is using a port

Student 1 released the port

Student 2 released the port

Student 3 is using a port

Student 3 released the port

 

 

B. Monitor Concept in C

Suppose only 2 ports are available.

 

 

Program:

 

#include <stdio.h>

#include <pthread.h>

#include <unistd.h>

 

#define MAX_PORTS 2

 

int available_ports = MAX_PORTS;

 

pthread_mutex_t mutex;

pthread_cond_t condition;

 

void* use_port(void* arg)

{

    int id = *(int*)arg;

 

    pthread_mutex_lock(&mutex);   // Enter monitor

 

    while (available_ports == 0)

    {

        printf("Student %d waiting for port...\n", id);

        pthread_cond_wait(&condition, &mutex);

    }

 

    available_ports--;

    printf("Student %d opened a port\n", id);

 

    pthread_mutex_unlock(&mutex);   // Exit monitor

 

    sleep(2);  // Using port

 

    pthread_mutex_lock(&mutex);

 

    available_ports++;

    printf("Student %d closed the port\n", id);

 

    pthread_cond_signal(&condition);  // Wake one waiting thread

    pthread_mutex_unlock(&mutex);

 

    return NULL;

}

 

int main()

{

    pthread_t t1, t2, t3;

    int id1 = 1, id2 = 2, id3 = 3;

 

    pthread_mutex_init(&mutex, NULL);

    pthread_cond_init(&condition, NULL);

 

    pthread_create(&t1, NULL, use_port, &id1);

    pthread_create(&t2, NULL, use_port, &id2);

    pthread_create(&t3, NULL, use_port, &id3);

 

    pthread_join(t1, NULL);

    pthread_join(t2, NULL);

    pthread_join(t3, NULL);

 

    pthread_mutex_destroy(&mutex);

    pthread_cond_destroy(&condition);

 

    return 0;

}

 

OUTPUT:

Student 1 opened a port

Student 3 opened a port

Student 2 waiting for port...

Student 1 closed the port

Student 3 closed the port

Student 2 opened a port

Student 2 closed the port

 

 

 

 

6. Write a program to illustrate concurrent execution of threads using pthreads library.

 

Threads Using Pthreads Library

 

What is a Thread?

A thread is the smallest unit of execution inside a process.
A process can contain multiple threads that run concurrently.

Threads share:

  • Same memory
  • Same global variables
  • Same resources of the process

But each thread has:

  • Its own stack
  • Its own program counter
  • Its own registers

 

What is Pthreads?

 

Pthreads (POSIX Threads) is a standard C library used for creating and managing threads in Unix/Linux systems.

It allows:

  • Creating threads
  • Synchronizing threads
  • Controlling execution of threads

Header file used:

#include <pthread.h>

 

Important Pthread Functions

Function

Purpose

pthread_create()

Creates a new thread

pthread_exit()

Terminates a thread

pthread_join()

Waits for a thread to finish

pthread_mutex_lock()

Locks a mutex

pthread_mutex_unlock()

Unlocks a mutex

 

 

Syntax of Creating a Thread

 

pthread_create(&thread_id, NULL, function_name, argument);

  • thread_id → Thread variable
  • function_name → Function executed by thread
  • argument → Data passed to thread

 

 

Life Cycle of a Thread

  1. Thread Creation
  2. Thread Execution
  3. Thread Termination

 

 

Advantages of Threads

·         Faster execution

·         Better CPU utilization

·         Resource sharing

·         Efficient communication

 

Example Structure:

#include <stdio.h>

#include <pthread.h>

 

void* myThread(void* arg)

{

    printf("Thread is running\n");

    return NULL;

}

 

int main()

{

    pthread_t t;

    pthread_create(&t, NULL, myThread, NULL);

    pthread_join(t, NULL);

    return 0;

}

 

 

OUTPUT:

 

Thread is running

 

Program:

 

#include <stdio.h>

#include <pthread.h>

#include <unistd.h>

 

// Function executed by Thread 1

void* task1(void* arg)

{

    for(int i = 1; i <= 5; i++)

    {

        printf("Thread 1: %d\n", i);

        sleep(1);   // Delay to show concurrency

    }

    return NULL;

}

 

// Function executed by Thread 2

void* task2(void* arg)

{

    for(int i = 1; i <= 5; i++)

    {

        printf("Thread 2: %d\n", i);

        sleep(1);

    }

    return NULL;

}

 

int main()

{

    pthread_t t1, t2;

 

    // Create threads

    pthread_create(&t1, NULL, task1, NULL);

    pthread_create(&t2, NULL, task2, NULL);

 

    // Wait for threads to finish

    pthread_join(t1, NULL);

    pthread_join(t2, NULL);

 

    printf("Both threads finished execution.\n");

 

    return 0;

}

 

OUTPUT:

 

Thread 1: 1

Thread 2: 1

Thread 1: 2

Thread 2: 2

Thread 1: 3

Thread 2: 3

Thread 1: 4

Thread 2: 4

Thread 1: 5

Thread 2: 5

Both threads finished execution.

 

 

 

 

 

7. Write a program to solve producer-consumer problem using Semaphores.

 

 

Program:

 

👉 Buffer size = 3
👉 Produce & consume only 5 items

 

#include <stdio.h>

#include <pthread.h>

#include <semaphore.h>

#include <unistd.h>

 

#define SIZE 3

 

int buffer[SIZE];

int in = 0, out = 0;

 

sem_t empty, full;

pthread_mutex_t mutex;

 

// Producer

void* producer(void* arg)

{

    for(int i = 1; i <= 5; i++)

    {

        sem_wait(&empty);              // Check empty space

        pthread_mutex_lock(&mutex);    // Lock buffer

 

        buffer[in] = i;

        printf("Produced: %d\n", i);

        in = (in + 1) % SIZE;

 

        pthread_mutex_unlock(&mutex);  // Unlock buffer

        sem_post(&full);               // Increase full count

 

        sleep(1);

    }

    return NULL;

}

 

// Consumer

void* consumer(void* arg)

{

    for(int i = 1; i <= 5; i++)

    {

        sem_wait(&full);               // Check if item exists

        pthread_mutex_lock(&mutex);    // Lock buffer

 

        int item = buffer[out];

        printf("Consumed: %d\n", item);

        out = (out + 1) % SIZE;

 

        pthread_mutex_unlock(&mutex);  // Unlock buffer

        sem_post(&empty);              // Increase empty count

 

        sleep(2);

    }

    return NULL;

}

 

int main()

{

    pthread_t p, c;

 

    sem_init(&empty, 0, SIZE);  // All slots empty

    sem_init(&full, 0, 0);      // No items initially

    pthread_mutex_init(&mutex, NULL);

 

    pthread_create(&p, NULL, producer, NULL);

    pthread_create(&c, NULL, consumer, NULL);

 

    pthread_join(p, NULL);

    pthread_join(c, NULL);

 

    return 0;

}

 

 

OUTPUT:

 

Produced: 1

Consumed: 1

Produced: 2

Consumed: 2

Produced: 3

Produced: 4

Consumed: 3

Produced: 5

Consumed: 4

Consumed: 5

 

 

 

 

 

8. Implement the following memory allocation methods for fixed partition First fit b) Worst fit c) Best fit

 

Memory Allocation Methods (Fixed Partition)

In fixed partition memory management:

  • Memory is divided into fixed size blocks.
  • Processes request memory.
  • OS allocates blocks using different strategies.

Given in programs:

 

Memory Blocks:

Block1 = 100

Block2 = 500

Block3 = 200

Block4 = 300

Block5 = 600

Processes:

P1 = 212

P2 = 417

P3 = 112

P4 = 426

 

First Fit – Explanation

Rule:

Allocate the first block that is large enough.

 

Step-by-Step Execution:

P1 = 212

  • Block1 (100)
  • Block2 (500) ✅ Allocate
  • Remaining Block2 = 500 − 212 = 288

 

P2 = 417

  • Block1 (100)
  • Block2 (288)
  • Block3 (200)
  • Block4 (300)
  • Block5 (600) ✅ Allocate
  • Remaining Block5 = 600 − 417 = 183

 

P3 = 112

  • Block1 (100)
  • Block2 (288) ✅ Allocate
  • Remaining Block2 = 288 − 112 = 176

 

 

 

P4 = 426

  • No block large enough ❌ Not Allocated

 

 

First Fit Result:

Process

Block

P1

Block2

P2

Block5

P3

Block2

P4

Not Allocated

 

 

 

Best Fit – Explanation

Rule:

Allocate the smallest block that fits.

 

P1 = 212

Suitable blocks:

  • 500
  • 300
  • 600

Smallest = 300 → Allocate
Remaining Block4 = 300 − 212 = 88

 

P2 = 417

Suitable blocks:

  • 500
  • 600

Smallest = 500 → Allocate
Remaining Block2 = 83

 

P3 = 112

Suitable blocks:

  • 200
  • 600

Smallest = 200 → Allocate
Remaining Block3 = 88

 

P4 = 426

Suitable block:

  • 600 → Allocate
    Remaining Block5 = 174

 

 

 

 Best Fit Result:

Process

Block

P1

Block4

P2

Block2

P3

Block3

P4

Block5

 

Worst Fit – Explanation

Rule:

Allocate the largest available block.

 

P1 = 212

Largest block = 600 → Allocate
Remaining = 388

 

P2 = 417

Largest block = 500 → Allocate
Remaining = 83

 

P3 = 112

Largest block = 388 → Allocate
Remaining = 276

 

P4 = 426

No block large enough ❌ Not Allocated

 

 

 Worst Fit Result:

Process

Block

P1

Block5

P2

Block2

P3

Block5

P4

Not Allocated

 

Comparison

Method

Advantage

Disadvantage

First Fit

Fast

More fragmentation

Best Fit

Less wasted space

Slower

Worst Fit

Avoids small fragments

May waste large blocks

 

 

Program for First Fit:

 

#include <stdio.h>

 

int main()

{

    int blocks[5] = {100, 500, 200, 300, 600};

    int process[4] = {212, 417, 112, 426};

    int i, j;

 

    printf("First Fit Allocation:\n");

 

    for(i = 0; i < 4; i++)

    {

        for(j = 0; j < 5; j++)

        {

            if(blocks[j] >= process[i])

            {

                printf("Process %d allocated to Block %d\n", i+1, j+1);

                blocks[j] -= process[i];

                break;

            }

        }

        if(j == 5)

            printf("Process %d not allocated\n", i+1);

    }

 

    return 0;

}

 

OUTPUT:

 

First Fit Allocation:

Process 1 allocated to Block 2

Process 2 allocated to Block 5

Process 3 allocated to Block 2

Process 4 not allocated

 

 

Program for Best Fit:

 

#include <stdio.h>

 

int main()

{

    int blocks[5] = {100, 500, 200, 300, 600};

    int process[4] = {212, 417, 112, 426};

    int i, j, best;

 

    printf("Best Fit Allocation:\n");

 

    for(i = 0; i < 4; i++)

    {

        best = -1;

 

        for(j = 0; j < 5; j++)

        {

            if(blocks[j] >= process[i])

            {

                if(best == -1 || blocks[j] < blocks[best])

                    best = j;

            }

        }

 

        if(best != -1)

        {

            printf("Process %d allocated to Block %d\n", i+1, best+1);

            blocks[best] -= process[i];

        }

        else

            printf("Process %d not allocated\n", i+1);

    }

 

    return 0;

}

 

OUTPUT:

 

Best Fit Allocation:

Process 1 allocated to Block 4

Process 2 allocated to Block 2

Process 3 allocated to Block 3

Process 4 allocated to Block 5

 

 

Program for Worst Fit:

 

#include <stdio.h>

 

int main()

{

    int blocks[5] = {100, 500, 200, 300, 600};

    int process[4] = {212, 417, 112, 426};

    int i, j, worst;

 

    printf("Worst Fit Allocation:\n");

 

    for(i = 0; i < 4; i++)

    {

        worst = -1;

 

        for(j = 0; j < 5; j++)

        {

            if(blocks[j] >= process[i])

            {

                if(worst == -1 || blocks[j] > blocks[worst])

                    worst = j;

            }

        }

 

        if(worst != -1)

        {

            printf("Process %d allocated to Block %d\n", i+1, worst+1);

            blocks[worst] -= process[i];

        }

        else

            printf("Process %d not allocated\n", i+1);

    }

 

    return 0;

}

 

OUTPUT:

 

Worst Fit Allocation:

Process 1 allocated to Block 5

Process 2 allocated to Block 2

Process 3 allocated to Block 5

Process 4 not allocated

 

 

9. Simulate the following page replacement algorithms FIFO b) LRU c) LFU

 

Explanation:

Reference string:

1 2 3 4 1 2 5 1 2 3 4 5

 

 

A. FIFO (First In First Out)

Idea:

  • Oldest page is removed first.
  • Like a queue.

 

 

#include <stdio.h>

 

int main()

{

    int pages[] = {1,2,3,4,1,2,5,1,2,3,4,5};

    int frames[3];

    int i, j, k = 0, flag, faults = 0;

 

    for(i = 0; i < 3; i++)

        frames[i] = -1;

 

    for(i = 0; i < 12; i++)

    {

        flag = 0;

 

        for(j = 0; j < 3; j++)

        {

            if(frames[j] == pages[i])

            {

                flag = 1;

                break;

            }

        }

 

        if(flag == 0)

        {

            frames[k] = pages[i];

            k = (k + 1) % 3;

            faults++;

        }

    }

 

    printf("Total Page Faults (FIFO) = %d", faults);

    return 0;

}

 

OUTPUT:

 

Total Page Faults (FIFO) = 9

 

 

 

Rule:

Remove the page that entered first (oldest).

 

Page

Frames

Fault?

1

1 - -

Yes

2

1 2 -

Yes

3

1 2 3

Yes

4

4 2 3

Yes (1 removed)

1

4 1 3

Yes (2 removed)

2

4 1 2

Yes (3 removed)

5

5 1 2

Yes (4 removed)

1

5 1 2

No

2

5 1 2

No

3

5 3 2

Yes (1 removed)

4

5 3 4

Yes (2 removed)

5

5 3 4

No

 

Total Page Faults = 9

👉 FIFO keeps replacing oldest page, even if it is frequently used

 

 

B. LRU (Least Recently Used)

Idea:

  • Remove the page that was not used for the longest time.

 

#include <stdio.h>

 

int main()

{

    int pages[] = {1,2,3,4,1,2,5,1,2,3,4,5};

    int frames[3], time[3];

    int i, j, faults = 0, counter = 0, pos, min;

 

    for(i = 0; i < 3; i++)

        frames[i] = -1;

 

    for(i = 0; i < 12; i++)

    {

        int flag = 0;

 

        for(j = 0; j < 3; j++)

        {

            if(frames[j] == pages[i])

            {

                counter++;

                time[j] = counter;

                flag = 1;

                break;

            }

        }

 

        if(flag == 0)

        {

            min = 0;

            for(j = 1; j < 3; j++)

                if(time[j] < time[min])

                    min = j;

 

            frames[min] = pages[i];

            counter++;

            time[min] = counter;

            faults++;

        }

    }

 

    printf("Total Page Faults (LRU) = %d", faults);

    return 0;

}

 

 

OUTPUT:

 

Total Page Faults (LRU) = 8

 

 

Rule:

Remove the page that was least recently used.

 

Page

Frames

Fault?

1

1 - -

Yes

2

1 2 -

Yes

3

1 2 3

Yes

4

4 2 3

Yes (1 removed)

1

4 1 3

Yes (2 removed)

2

4 1 2

Yes (3 removed)

5

5 1 2

Yes (4 removed)

1

5 1 2

No

2

5 1 2

No

3

3 1 2

Yes (5 removed)

4

3 4 2

Yes (1 removed)

5

3 4 5

Yes (2 removed)

 

 

Total Page Faults = 8

👉 LRU performs better because it removes pages that are not used recently.

 

 

 

C. LFU (Least Frequently Used)

Idea:

  • Remove the page with lowest frequency count.

 

#include <stdio.h>

 

int main()

{

    int pages[] = {1,2,3,4,1,2,5,1,2,3,4,5};

    int frames[3], freq[3];

    int i, j, faults = 0, pos;

 

    for(i = 0; i < 3; i++)

    {

        frames[i] = -1;

        freq[i] = 0;

    }

 

    for(i = 0; i < 12; i++)

    {

        int flag = 0;

 

        for(j = 0; j < 3; j++)

        {

            if(frames[j] == pages[i])

            {

                freq[j]++;

                flag = 1;

                break;

            }

        }

 

        if(flag == 0)

        {

            pos = 0;

            for(j = 1; j < 3; j++)

                if(freq[j] < freq[pos])

                    pos = j;

 

            frames[pos] = pages[i];

            freq[pos] = 1;

            faults++;

        }

    }

 

    printf("Total Page Faults (LFU) = %d", faults);

    return 0;

}

 

OUTPUT:

 

Total Page Faults (LFU) = 9

 

 

Rule:

Remove the page that was least recently used.

 

Page

Frames

Fault?

1

1 - -

Yes

2

1 2 -

Yes

3

1 2 3

Yes

4

4 2 3

Yes (1 removed)

1

4 1 3

Yes (2 removed)

2

4 1 2

Yes (3 removed)

5

5 1 2

Yes (4 removed)

1

5 1 2

No

2

5 1 2

No

3

3 1 2

Yes (5 removed)

4

3 4 2

Yes (1 removed)

5

3 4 5

Yes (2 removed)

 

 

Total Page Faults = 8

👉 LRU performs better because it removes pages that are not used recently.

 

 

10. Simulate Paging Technique of memory management.

 

Concept of Paging

What is Paging?

Paging is a memory management technique in which:

  • Physical memory is divided into fixed size blocks called Frames.
  • Logical memory (process) is divided into same size blocks called Pages.
  • A Page Table is used to map pages to frames.

 

Why Paging?

Avoids external fragmentation
Allows non-contiguous memory allocation
Efficient memory utilization

 

 

Address Translation in Paging

 

Logical Address =

Page Number (p) + Offset (d)

Physical Address =

Frame Number + Offset

Formula:

Physical Address = Frame_Number × Frame_Size + Offset

 

Example

Suppose:

  • Page size = 100 bytes
  • Page table:

Page

Frame

0

2

1

0

2

1

3

3

Logical Address = 250

Step 1:
Page number = 250 / 100 = 2
Offset = 250 % 100 = 50

Step 2:
From page table → Page 2 → Frame 1

Step 3:
Physical Address = 1 × 100 + 50 = 150

 

Program for Paging

👉 This program converts logical address to physical address.

 

#include <stdio.h>

 

int main()

{

    int page_size, n, i;

    int page_table[10];

    int logical_address, page_number, offset, physical_address;

 

    printf("Enter number of pages: ");

    scanf("%d", &n);

 

    printf("Enter page size: ");

    scanf("%d", &page_size);

 

    printf("Enter frame number for each page:\n");

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

    {

        printf("Page %d -> Frame: ", i);

        scanf("%d", &page_table[i]);

    }

 

    printf("Enter logical address: ");

    scanf("%d", &logical_address);

 

    page_number = logical_address / page_size;

    offset = logical_address % page_size;

 

    if(page_number >= n)

    {

        printf("Invalid Page Number!\n");

    }

    else

    {

        physical_address = page_table[page_number] * page_size + offset;

 

        printf("Page Number = %d\n", page_number);

        printf("Offset = %d\n", offset);

        printf("Physical Address = %d\n", physical_address);

    }

 

    return 0;

}

 

 

OUTPUT:

 

Enter number of pages: 4

Enter page size: 100

Enter frame number for each page:

Page 0 -> Frame: 2

Page 1 -> Frame: 0

Page 2 -> Frame: 1

Page 3 -> Frame: 3

Enter logical address: 250

Page Number = 2

Offset = 50

Physical Address = 150

 

 

How Program Works

  1. User enters:
    • Number of pages
    • Page size
    • Page table mapping
  2. Enter logical address.
  3. Program calculates:
    • Page number
    • Offset
  4. Finds frame from page table.
  5. Calculates physical address.

 

 

11. Implement Bankers Algorithm for Dead Lock avoidance and prevention

12. Simulate the following file allocation strategies Sequential b) Indexed c) Linked

 


No comments:

Post a Comment

OS LAB Programs

5. Control the number of ports opened by the operating system with  a) Semaphore b) Monitors. A. Using Semaphore  Suppose only ...