Operating system - concurrent programming based on semaphore mechanism

1. Experimental topic

Concurrent Program Design Based on Semaphore Mechanism

2. The purpose of the experiment

(1) Review the related concepts of operating system processes and threads, and conduct concurrency for classic synchronization, mutual exclusion, deadlock and starvation problems

programming.

(2) Understand the mutex object, use the mutual exclusion and synchronization operation to write the concurrent program of the reader - writer problem, and deepen the understanding of P ( ie

semWait) , V ( namely semSignal) primitives and the understanding of inter-process synchronization and mutual exclusion operations using P and V primitives.

(3) Understand the information volume mechanism supported by Linux , and use the semaphore system call programming of IPC to realize the philosopher's dining problem.

3. Overall design (including background knowledge or basic principles and algorithms, or module introduction, design steps, etc.)

Reader-writer problem:

* In the reader-writer problem, multiple readers can read the shared resource at the same time, but only one writer can write to the shared resource.

* Use a mutex object (mutex) to achieve mutual exclusion access to shared resources.

* Use semaphore (semaphore) to achieve synchronization and mutual exclusion between readers and writers.

There is a data area shared by multiple processes. This data area can be a file or a piece of memory space, or even a set of registers. Some processes (readers) only read the data in this data area, and some processes (writers) only read data in the data area. Write data in the data area. In addition, must also meet:

1. Any number of reading processes can read this file at the same time.

2. Only one writer process can write files at a time

3. If the writing process is writing the file, prohibit any reading process from reading the file.

The reader-first algorithm idea implemented in the book is:

The writing process is relatively simple, and the semaphore WriteSemaphore is used to implement exclusion. As long as a writing process is accessing the data area, other writing processes and reading processes cannot access it. Readers also use the WriteSemaphore to implement mutual exclusion, but to allow multiple readers, the first reader to attempt to read needs to wait on the WriteSemaphore when no reader is reading. When at least one reading process is reading, subsequent reading processes can directly enter without waiting. readcount is used to record the number of read processes, and the semaphore XSemaphore is used to ensure that readcount is updated correctly.

4. Detailed design (including main data structure, program flow chart, key code, etc.)

Concurrent Programming for Reader-Writer Problem

 

Use a mutex object mutex to guarantee exclusive access to shared resources.

Use two semaphore readers and writers to control the synchronization and mutual exclusion between readers and writers.

Reader thread:

Use sem_wait(&readers) to wait for other readers to finish reading before entering the critical section.

Enter the critical section to read shared resources.

Use sem_post(&readers) to release the reader semaphore.

Writer thread:

Before entering the critical section, use sem_wait(&writers) to wait for other writers to complete the write operation.

Enter the critical section to write shared resources.

Use sem_post(&writers) to release the writer semaphore.

code:

#include <windows.h>

#include <iostream>

#define MAX_READER_NUM 512

#define READER_NUM 3

#define WRITER_NUM 2

#define MOD 100

using namespace std;

int readcount = 0;

HANDLE WriteSemaphore; //Realize read and write mutual exclusion

HANDLE XSemaphore; // Mutex for modifying the number of people

DWORD WINAPI reader(LPVOID); //reader thread

DWORD WINAPI writer(LPVOID); //writer thread

bool p_ccontinue = true; //control program ends

int test = 0;

int main()

{

    //Initialize two semaphores

    /*Structure pointer; initial count of semaphore objects; maximum count of semaphore objects; name of semaphore objects*/

    WriteSemaphore = CreateSemaphore(NULL,1,MAX_READER_NUM,NULL);

    XSemaphore = CreateSemaphore(NULL,1,MAX_READER_NUM,NULL);

    //Use WindowsAPI to simulate textbook parbegin(reader,writer);

    //Total number of threads

    HANDLE hThreads[READER_NUM + WRITER_NUM]; //handle of each thread

    DWORD readerID[READER_NUM]; //Identifier of reader thread

    DWORD writerID[WRITER_NUM]; //Identifier of the writer thread

    //Create reader thread

    for (int i=0; i<READER_NUM; i++)

    {

        hThreads[i]=CreateThread(NULL,0,reader,NULL,0,&readerID[i]);

        if (hThreads[i]==NULL)

            return -1;

    }

    //Create a writer thread

    for (int i=0; i<WRITER_NUM; i++)

    {

        hThreads[READER_NUM+i]=CreateThread(NULL,0,writer,NULL,0,&writerID[i]);

        if (hThreads[i]==NULL)

            return -1;

    }

    while(p_ccontinue)

    {

        if(getchar()) //Press Enter to terminate the program

        {

            p_ccontinue = false;

        }

    }

    return 0;

}

//reader read

void READUNIT()

{

    cout<<"A reader starts reading:";

    cout<<test<<endl;

}

//The writer writes

void WRITEUNIT()

{

    cout<<"The writer starts writing:";

    test = (test+1) % MOD;

    cout<<test<<endl;

}

//reader

DWORD WINAPI reader(LPVOID lpPara)

{

    while(p_ccontinue)

    {

        WaitForSingleObject(XSemaphore,INFINITE); //semWait(x);

        readcount++;

        if(readcount == 1) //The first reader has come

            WaitForSingleObject(WriteSemaphore,INFINITE); //semWait(wsem);

        ReleaseSemaphore(XSemaphore,1,NULL); //semSignal(x);

        READUNIT();

        // finished reading

        WaitForSingleObject(XSemaphore,INFINITE); //semWait(x);

        readcount--;

        //No readers, release resources

        if(readcount == 0)

            ReleaseSemaphore(WriteSemaphore,1,NULL); //semSignal(wsem);

        ReleaseSemaphore(XSemaphore,1,NULL); //semSignal(x);

        Sleep(3000);

    }

    return 0;

}

//writer

DWORD WINAPI writer(LPVOID lpPara)

{

    while(p_ccontinue)

    {

        WaitForSingleObject(WriteSemaphore,INFINITE); //semWait(wsem);

        WRITEUNIT();

        ReleaseSemaphore(WriteSemaphore,1,NULL); //semSignal(wsem);

        Sleep(2000);

    }

    return 0;

}

5. Experimental results and analysis

The experimental result of this code is to simulate the concurrent access of readers and writers to shared resources. Due to the synchronization and mutual exclusion between readers and writers, readers and writers will alternately read and write to shared resources. The behavior of readers and writers can be observed by outputting information.

It is worth noting that the scheduling between readers and writers is non-deterministic, so the results may vary slightly from run to run. The execution order of readers and writers will vary according to the scheduling policy of the operating system and the contention of threads.

Overall, this code demonstrates the basic idea and implementation of the reader-writer problem using mutexes and semaphores. By properly controlling mutually exclusive access and synchronization operations, correct access and modification of shared resources among multiple readers and writers can be ensured.

 

6. Summary and experience

In this experiment, we successfully designed a concurrent program to solve the reader-writer problem by using the mutex object and the semaphore mechanism. Through this experiment, I came to the following summary and experience:

1. Familiar with the process and thread concept of the operating system: Before designing a concurrent program, we need to have a certain understanding of the process and thread of the operating system. Understanding the concepts, characteristics and relationship between processes and threads can help us better understand the problems and solutions of concurrent programming.

2. Understand the importance of synchronization and mutual exclusion: When multiple threads access shared resources at the same time, it is necessary to ensure that access to shared resources is synchronized and mutually exclusive. By using mutex objects and semaphores, we can achieve synchronization and mutual exclusion between threads, avoiding data races and inconsistent results.

3. Learn to use the semaphore mechanism: The semaphore is a commonly used concurrent programming mechanism, which can be used to control access and synchronization between concurrent threads. In this experiment, we use semaphores to control access and modification of shared resources between readers and writers. Learn to use semaphore primitives, such as sem_wait and sem_post, to effectively achieve synchronization and mutual exclusion between threads.

4. Pay attention to deadlock and starvation problems: When designing concurrent programs, you need to pay attention to the possibility of deadlock and starvation problems. Deadlock refers to the situation in which multiple threads wait for each other to release resources and cannot continue to execute, while starvation refers to the situation in which some threads cannot be executed due to uneven resource allocation. In this experiment, we avoided the occurrence of deadlock and starvation by rationally designing mutual exclusion and synchronization operations.

5. The challenge of debugging concurrent programs: The debugging of concurrent programs is relatively complicated, because the execution order of threads is uncertain, there may be race conditions and errors that are difficult to reproduce. In this experiment, we can observe and verify the behavior of readers and writers, as well as the correct access and modification of shared resources, by outputting information. At the same time, using debugging tools and techniques can help us better understand the execution process of the program and discover potential problems.

By completing this experiment, I gained a deeper understanding of concurrent programming. At the same time, I also realized that in actual development, it is very important to design concurrent programs reasonably, which involves cooperation between threads and resource management. Proper use of synchronization and mutual exclusion mechanisms can improve program performance, reliability, and correctness, and avoid data races and inconsistent results. Concurrent programming is a challenge, but an interesting and important skill that is essential to developing efficient, scalable applications.

Guess you like

Origin blog.csdn.net/CSH__/article/details/131383075