Reading Notes of Modern Operating System-inter-process communication 2

Source: Internet
Author: User

7. Several solutions to achieve process mutex-TSL commands
 
We have introduced several solutions to achieve mutual exclusion through software. The following methods require the help of hardware design to achieve mutual exclusion. This is particularly common in the design of multi-CPU computers. This solution requires the introduction of an instruction:
 
[Plain]
Tsl rx, LOCK
This command reads the content in the memory unit LOCK to the Register RX and resets a non-zero value for the memory unit LOCK. The operation of the TSL command is designed to be inseparable. That is to say, before the entire read/write operation is completed, other processes cannot access the LOCK memory unit. This is achieved by locking the memory bus.
We mentioned how to disable interruption. This method of locking the memory bus is very different from that of the former. As we mentioned earlier, disabling interrupt is only valid for the current CPU and is not valid for other CPUs, therefore, it is impossible to achieve mutex in the case of multiple CPUs. The locking memory bus is different. Once the memory bus is locked, other CPU processes cannot access the LOCK memory until the entire TSL command is executed.
 
The following code uses the TSL command to implement mutex: Compile the code.
 
[Plain]
Enter_region:
Tsl register, LOCK | copy the LOCK value to the REGISTER
Cmp register, #0 | compare and determine whether the value of REGISTER is 0
JNE enter_region | if the value is not 0, the lock is successfully set, and the loop is returned to the call and enters the critical section.
RET

Leave_region:
Move lock, #0 | set the LOCK value to 0
RET | return
In fact, the idea of this solution is similar to that of the previous Peterson solution. The specific descriptions are in the annotations. When a process needs to enter its critical section, execute enter_region and wait until the lock is available. Then, the process obtains the lock. It should be noted that, like all mutually exclusive solutions using the busy wait method, the process must call enter_region and leave_region in the correct way. If any process cheat, this method will not be useful.
The TSL command also has a similar command XCHG, which can automatically exchange the content of two addresses. The XCHG solution is almost the same as the TSL solution. The Intel X86CPU of the kernel uses the XCHG command to provide underlying synchronization. The Code implemented using XCHG is as follows:
 
[Plain]
Enter_region:
Move register, #1
Xchg register, LOCK
Cmp register, #0
JNE enter_region
RET

Leave_region:
Move lock, #0
RET
8. Sleep and Wakeup
The above mentioned several mutually exclusive solutions. Essentially, they all adopt a busy wait method and constantly check whether the current condition allows them to run. This method is not only a waste of CPU time, but also a big problem. Assume that a computer has two processes running, one is H, with a high priority and the other is L, with a low priority. Now it is assumed that as long as H is in ready state, the CPU will schedule H to run. Now, assume that at a certain time point, there are both negative and positive errors, or something. L enters the critical section, but it is running halfway, and the time slice expires. H is in ready state, but L is still in the critical section, in fact, H cannot run, and L does not have a time slice. The result is that everyone is doing this. This is called the Priority Inversion Problem (Priority Inversion Problem ).
 
The following describes the two most basic primitives for inter-process communication. Sleep and wakeup. sleep is a system call that can block the caller until another process wakes up. A parameter of wakeup is the number of the wake-up process.
 
9. producer-consumer issues
 
As an example of the previous pair of primitives, we will introduce the producer-consumer issue. Or bounded-buffer problem: two processes share a fixed-size buffer, where the Producer will generate information into a certain unit of the buffer, and the Consumer (Consumer) the messages in the buffer zone are extracted. (This problem can be evolved into m producers and n consumers, but here we only use one producer and one consumer as an example ).
 
If the producer needs to buffer the information, and the buffer is full, or the consumer needs to take out the information and the buffer is empty, the problem arises. In the first case, we can let the producer sleep and let the consumer wake up the producer when the consumer gets out the message and has an empty cache unit. The latter case is similar. To avoid the problem similar to the preceding print pool directory, we need a count variable to record several messages in the current buffer. When producing messages, the producer must first check whether count is equal to the buffer size N. Similarly, when consuming messages, the consumer must check whether count is 0.
 
The code for explaining this problem is as follows:
 
[Cpp]
# Define N 100
Int count 0;
 
Void producer (void ){
Int item;
While (TRUE ){
Item = produce_item ();
If (count = N ){
Sleep ();
}
Insert_item (item );
Count = count + 1;
If (count = 1 ){
Wakeup (consumer );
}
}
}
 
Void consumer (void ){
Int item;
While (TRUE ){
If (count = 0 ){
Sleep ();
}
Item = remove_item ();
If (count = N-1 ){
Wakeup (producer );
}
Consume_item (item );
}
 
}
However, there may still be problems. Because the access to count is unrestricted, in other words, it is not atomic. Let's assume the following situation. At the beginning, the buffer is empty, and the consumer monitors the count and finds it to be 0. At this time, the consumer process expires, and the consumer process changes to the runnable state. The producer is scheduled to run at this time, which detects count, <N is found, so a message is generated and put into the buffer. Now count is equal to 1, so the producer calls wakeup (consumer). The problem is that the consumer process is in the Runnable state, and wakeup does not work for it. Then it is the consumer's turn to run. The count value that the consumer has previously checked is 0. When the consumer looks at it, he thinks there is still no message in the buffer, so he sleep himself. At a certain point in the end, the producer will eventually fill up the buffer and sleep, but the producer cannot wait for the consumer to wake up because both of them are sleeping.
A temporary solution to this problem is to set a wakeup waiting bit flag.
10. Signal Semaphore
 
Dijkstra proposed the semaphore concept in 1965. A semaphore is a variable type. It can be 0, indicating that there are no processes waiting for wakeup, or a positive integer, representing the number of processes waiting for the current wakeup. Dijkstra proposes two signal types: up and down (Dijkstra proposes P and V Operations ). The up and down operations are actually an implementation of sleep and wakeup primitives.
 
For the down operation, it first checks whether the value is greater than 0. If yes, the value is reduced by 1 and the operation continues. If not, the process will be sleep, at this moment, the down operation cannot be completed. Check value, reduce value, or possible sleep is packaged into an atomic operation, that is, it cannot be divided. It ensures that once a signal operation starts, other processes will not be able to access this signal unless it completes or sleep.
 
For the up operation, it adds 1 to the value. If one or more processes on the current signal are sleeping due to the previous down operation being incomplete (the value was 0 at the time, then the system randomly selects a process and wakes it up. If multiple processes sleep on the signal, the value of the signal volume after an up operation is executed is still 0, but the number of processes that sleep on the signal is less than one. Value added, and the operation to wake up and sleep on this signal is also inseparable. The up operation will not cause blocking.
 
11. Use signals to solve producer-consumer problems
 
Semaphores are used to solve the consumer problem. Three semaphores are involved: full -- indicates how many buffer units are full, and empty -- indicates how many buffer units are empty, mutex -- used to ensure that the producer consumer cannot access a buffer unit at the same time. The principle code is as follows:
 
[Cpp]
# Defone N 100/* buffer size */
Typedef int semaphore;
Semaphore mutex = 1;
Semaphore full = 0;
Semaphore empty = N;
 
Void producer (void ){
Int item;
While (TRUE ){
Item = produce_item ();
Down (& empty );
Down (& mutex );
Insert_item (item );
Up (& mutex );
Up (& full );
}
}
 
Void consumer (void ){
Int item;
Wile (TRUE ){
Down (& full );
Down (& mutex );
Item = remove_item ();
Up (& mutex );
Up (& empty );
Consume_item (item );
}
}
Like mutex, the initial Initialization is 1 and is used between multiple processes to ensure that only one signal can enter its critical section at a time is called Binary Semaphore ). If each process performs a down operation before entering the critical section and an up operation after exiting the critical section, mutual exclusion can be guaranteed.
 
In the above Code, semaphores are used in two different ways: the first method is used to ensure mutual exclusion. mutex is used to ensure that only one process enters its critical section at the same time; the second method is used to ensure synchronization. The use of full and empty ensures that the producer does not run when the buffer is full and the consumer does not run when the buffer is empty.
 

From wawlian:

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.