! Operating system-7 semaphores and monitors achieve synchronization and mutual exclusion

1 background

How to study the semaphore and the tube to solve problems of synchronization and mutex
lock mechanism to resolve mutually exclusive
needs more advanced synchronization mutex semantics, can also be achieved by means of higher-level mutual exclusion synchronization hardware atomic operation
Insert picture description here
Insert picture description here
into the process or thread if the critical section Just do a read operation, there is no need to restrict only one thread or process to execute, a more advanced means of synchronization and mutual exclusion are required to complete this mechanism, so the semaphore is introduced

2 Semaphore

Insert picture description here
Insert picture description here
The value of the semaphore can be interpreted as the number of certain types of resources. The
Insert picture description here
semaphore railway will have multiple roads (concurrent execution). The
lock railway is
an example of only one semaphore in the road map. 3 If you want to divide it into 2 through this The way, you can only wait for 1 or 2 to pass, as shown in the figure below:
Insert picture description here
Enter the critical section to perform the P operation and
exit the critical section to perform the V operation, and the waiting will be notified to execute
Insert picture description here

3 Semaphore use

Insert picture description here
P operation will block the process and hang.
V operation will wake up the process.
Wake up which process involves fairness issues.
Wake up a process will use FIFO,
but is the lock mechanism used FIFO?
Insert picture description here

3.1 Using binary semaphores

3.1.1 Realizing mutual exclusion

The initial value is set to 1. The
Insert picture description here
above can be used to replace the lock mechanism.
Note: The P operation is to first subtract 1 and then determine the size

3.1.2 Realize conditional synchronization

The initial value is set to 0.
Insert picture description here
Why is the initial value set to 0?
Thread A must wait until thread B has executed a certain statement before it can continue to execute. To ensure this, semaphores can be used. As you can see in the figure above, in order to achieve synchronization, thread A must wait until thread B finishes V() before executing P()

3.2 More complex synchronous mutual exclusion cannot be solved with simple binary semaphores

Insert picture description here

  • limited buffer
  • When the producer writes data, the consumer cannot fetch the data; multiple producers (consumers) are allowed to write (read) data to the buffer (Lock only allows one producer\consumer to write data into it), the above 2 can indicate Are mutually exclusive
  • When the buffer is empty, the consumer should sleep and wait until the producer finishes writing data to the buffer before waking up the consumer (synchronization)
    Insert picture description here
    Insert picture description here
    Two general semaphores are used to achieve synchronization.
    Code implementation:
    Insert picture description here
    Note:
  • Mutex is initialized to 1, used to achieve mutual exclusion
  • fullBuffers is initialized to 0, indicating that there is no one in the buffer at the beginning
  • emptyBuffers is initialized to n
  • Depsdit() producer, add to buffer
  • Remove() consumers,
    Insert picture description here
    add mutex->PV operation to the buffer to ensure mutual exclusion, only one process can access (the operation of the buffer maintains mutual exclusion)
    Insert picture description here
    increase the empty and full two to ensure synchronization (see buffer If it is full or not, there can be n producer processes executing Deposit; notify consumers and producers whether they can perform operations)
    If the step of emptyBuffers->p is blocked (<0), it means that the producer is full and you have to wait Consumers can only continue to execute
    fullBuffers->V()> 0. This step can inform consumers that the buffer already has data.

Can the order of P and V operations be changed?

  • For example, the two V operations of the producer can swap the order, because he just wakes up
  • If the producer's 2 P operations interchange the order, it may cause deadlock or other problems. Suppose fullBuffer=n, then another producer process will be executed, mutex->P is executed first, and emptyBuffer->p is already full at this time, so the process is suspended to the blocking process. But look at the consumer process again, first execute fullBuffers->p, and it cannot be executed at the step of mutex->P, because the producer process above is already occupied, and in this case, the consumer cannot wake up the suspended production. Process, causing deadlock

4 Semaphore realization

Insert picture description here
P: sem first, if it is less than 0 after the subtraction, the whole thread will be suspended to the waiting queue and let him sleep.
V: P's sem<0, since V executes sem++ first, it is judged that sem<=0, let’s see Is there a process waiting on the waiting queue that the
Insert picture description here
PV operation sequence is different will cause deadlock and other problems

5 tube

The degree of abstraction is higher, and it is easier to use. The
earliest proposal was to achieve synchronous mutual exclusion in high-level languages

5.1 What is a monitor

The
monitor first appeared in the language (java), which is aimed at the concurrent operation of the language. The monitor is a combination (module) that contains a series of shared variables and functions that operate on these variables.
1 lock is required: ensure There is only one process that accesses the monitor. To ensure mutual exclusion,
multiple condition variables are required (how many condition variables are determined according to the number of conditions): a large number of shared resources will be accessed, and it is possible that some threads (processes) will Because some conditions are not met and shared resources cannot be obtained, these threads (processes) must be suspended to the corresponding condition variables at this time,
Insert picture description here
Insert picture description here
entry queue: queue
x, y to send the process to the monitor : condition queue
wait x : Let the process wait for x (hang in the x queue)
signal x: wake up x, so that the process hanging on x has the opportunity to continue to execute
Insert picture description here
Lock: ensure that the functions in the monitor are mutually exclusive, you can write those 2 operations In the function, it can also be guaranteed by language-level mechanisms.
Condition variables:
Insert picture description here
numWaiting: the number of threads waiting for the condition
sem: the number of semaphores.
PV operations must have addition and subtraction operations, but Signal does not necessarily perform subtraction operations.
A problem : Why do we need to release first and then require lock in Wait?

  • There will be an explanation later: it is possible to call wait after require in Deposit. At this time, if there is no release lock in wait, then other threads will not enter the monitor.
    Insert picture description here
    count: Buffer is idle, count=0 empty, count =n full
    lock->Acquire(), lock->Release() is different from the semaphore: the reason is that the monitor is defined as allowing only one thread to enter

When one thread wakes up another thread, does the execution wake up or the execution wakes up? There are the following two ideas:
Insert picture description here
Hansen: Although T2 executes signal, it continues to execute T2, and executes T1 until the end (implementation is simpler, more commonly used)

Hoare: When T2 executes the signal (wake-up) operation, continue to execute the awakened T1, and then execute T2 after T1 is finished (implementation is difficult, and more complex mechanisms are needed to achieve effectiveness)
Insert picture description here
Hansen: When a thread does the signal During the operation, it will continue to execute. There may be multiple threads waiting on the condition variable to be awakened. They will grab the CPU, but there is only one CPU. When the awakened thread executes, the count may not be n , So we have to use while to do another confirmation

Hoare: After the signal operation is completed, the CPU is handed over to the awakened thread. At this time, only one thread is awakened (there will be no more than one, because only one can be awakened), and the count must not be n at this time, because The signal operation is only performed when count is less than n. After the use is awakened, the operation of count<n is still satisfied and will not be destroyed.
Insert picture description here
Insert picture description here
There is error non-reproducibility, and the use should be designed well.

6 classic synchronization problems

6.1 Reader-writer problem

Insert picture description here
Multiple writers are not allowed to operate at the same time (it will cause data inconsistency); multiple readers are
allowed to operate at the same time Readers and writers are not allowed to operate at the same time
Insert picture description here
When there is a reader reading operation, the writer must wait until the readers have read all , The writer can write
. The reader and other writers must wait until the writer finishes the writing operation.
Readers are given priority: when a reader is reading, a writer comes, and then another Readers, the later readers can skip the waiting writers. The read operation does not damage the data, you can skip the waiting writer to complete the corresponding operation
Rcount: the number of readers (because there can only be one writer operation when writing, so there is no need to count the number of writers)
CountMutex: Ensure that reading or writing to Rcount is mutually exclusive.
WriteMutex: Writers are also mutually exclusive.

Reader first

Insert picture description here
writer: ensure that only one writer can enter the operation
sem_wait() = P operation
sem_post() = read operation
reader: ensure that once a writer writes, the reader cannot enter to read, and once a reader reads again, the writer cannot enter Write
Insert picture description here
Rcount to record the number of readers. If Rcount=0, it means that there are no readers at present. Readers must perform an operation sem_wait() to see if there is a writer before reading. Make sure that there is no writer before starting to perform the read operation
Rcount != 0, it means that there are readers currently reading data, even if there are writers, they will not be able to enter.
Then start reading Rcount++ before reading operation, perform read operation, Rcount-
if after the operation is over, if Rcount=0, it means that the reader is the last one Readers, if there are other writers waiting outside, perform the V operation of WriteMutex to wake up one writer.
Insert picture description here
To protect Rcount++ and Rcount-mutually exclusive, so add the PV operation of CountMutex

Writers first

As long as there are writers, readers cannot read. Write first.
Insert picture description here
Insert picture description here
Before reading, make sure that there are no writers. Such writers include: 1. Writers who are performing writing operations, 2. Writers in the waiting queue. As long as the above two kinds of writers exist, the reader cannot perform the read operation.
After reading, wake up the waiting reader
Insert picture description here
. Before the writer writes again, make sure that there are no readers who are reading and no writers who are writing before they can
write. Wake up waiting writers or readers after
Insert picture description here
finishing AR: the number of readers who are reading
AW: the number of writers who are writing
WR: the number of readers who are waiting
WW: the number of writers who are waiting for
synchronization Variable okToRead: time to read; okToWrite: time to write
Mutex lock

reader

Insert picture description here
lock.acquire() and lock.release() ensure that only one thread enters the monitor and
can enter the monitor, indicating that there are running readers, that is, AR++
while represents that there is a writer and the reader needs to wait, first WR++, and then hang it to okToRead On the waiting queue, once awakened, WR will be executed-
but how to judge that there is a writer? (that is, the condition of the while loop) uses AW and WW to ensure that there is no waiting writer and no executing writer (+WW reflects the writer Priority)
Insert picture description here
startRead(): Allow multiple readers to read

Insert picture description here
DoneRead(): What to do after performing the read operation
Insert picture description here
AR==0 && WW>0 There is no reader that is reading and there is a waiting writer to wake up a writer

Writer

Insert picture description here
okToWrite.signal(): Only one writer is woken up, because only one writer is allowed to operate at the same time, so only one can be woken up.
okToRead.broadcast(): wake up multiple readers, because multiple readers are allowed to execute at the same time
without a writer. To wake up the writer, reflect the writer first

6.2 The philosopher's meal problem

Five philosophers gathered in a circle, five pairs of chopsticks, thinking, eat when you are hungry, you must take two pairs of chopsticks to eat, how to coordinate so that every philosopher can eat?
Insert picture description here
The five forks fork[5] use semaphores, initialized to 1, picked up as P, and put down as V.
1 philosopher represents 1 thread

Option 1:
Insert picture description here
Question:

  • The above code is applicable to 1 scientist,
  • However, if all five scientists pick up the fork on their left hand at the same time, no one can give up the fork in their left hand at this time, resulting in deadlock.

Option 2: After picking up the fork on the left hand side, if you can pick up the fork on the right hand side, pick it up, otherwise, put down the fork on the left hand side, and then wait for a certain event.
Insert picture description here
Problem: When all 5 scientists picked up the fork on the left hand side, they found The fork on the right hand side is not there, so put down the fork on the left hand at the same time, wait for the same event, and then repeat the above operation

Option 3: Random waiting time
Insert picture description here
Problem: Random events cannot guarantee that every philosopher will have a chance to eat

Solution 4: Add PV operation so that only one philosopher can enter the critical zone.
Insert picture description here
Problem: There will be no deadlock, but only one person is allowed to eat (in fact, two people can eat at the same time)
Insert picture description here

Scheme 5:
Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
P(s[i]): If you have eaten it, the V operation has been performed in test_take_left_right_forks(), that is, s[i]=1, and then P operation is performed at this time, then s[i]=0, Will not block
Insert picture description here
oneself, only eat when you are hungry and neither left nor right are eating
V(s[i]): the initial value of s[i] is 0, after the operation of V s[i]=s[i]+ 1=1 to inform you that you can eat.
Insert picture description here
Insert picture description here
Insert picture description here
When the test_take_left_right_forks(LEFT) is executed, if the left neighbor is ready to eat, the V operation will be executed. This operation corresponds to the P(s[i]) operation
state in the take_forks() of the left neighbor [i] Will change, and judge the state of other scientists, so do PV (mutex) operation to protect it.
Insert picture description here
Look at the scientist function.

  • Think first
  • Try to take 2 forks. If the left and right neighbors are not eating, you can pick up the fork to eat, and set state[i] to eating. If the condition is not weak, you can pick up 2 forks and perform P operation to make yourself Blocked
  • Once the fork is picked up (not blocked), eat()
  • After eating, put the fork back and notify the neighbor. If the neighbor is hungry and can pick up 2 forks, set the scientist who can pick up 2 forks and hungry to eating, if the scientist is in Sleep, and wake him up so that it can continue to execute eat()

Insert picture description here
Eat does not need to be further implemented. During the eat operation, a critical section
think operation needs to be further implemented. It needs to set its own state to thinking, and this operation needs to be wrapped in PV to ensure that it is a critical resource and to ensure mutual exclusion

The semaphore in the above method can also be realized by using the tube process

Guess you like

Origin blog.csdn.net/qq_42713936/article/details/105309536