Mutex lock between semaphores and threads

Source: Internet
Author: User

Control Methods for mutual exclusion of four processes or threads
1. critical section: accesses public resources or code segments through multi-thread serialization, which is fast and suitable for controlling data access.
2. mutex: designed to coordinate separate access to a shared resource.
3. semaphore: designed to control a limited number of user resources.
4. Event: it is used to notify the thread that some events have occurred and start subsequent tasks.

Critical Section)
A convenient way to ensure that only one thread can access data at a certain time point. Only one thread is allowed to access Shared resources at any time. If multiple threads attempt to access the critical section at the same time, all other threads attempting to access the critical section will be suspended and will continue until the thread enters the critical section. After the critical section is released, other threads can continue to seize it and use the atomic method to share resources.

Mutex)
The mutex is similar to that in the critical section. Only threads with mutex objects have the permission to access resources. Because there is only one mutex object, therefore, it is determined that the shared resource will not be accessed by multiple threads at the same time under any circumstances. The thread occupying the resource should hand over the mutex object after the task is processed, so that other threads can access the resource after obtaining it. Mutex is more complex than that in the critical section. Because mutex can not only achieve secure resource sharing in different threads of the same application, but also achieve secure resource sharing among threads of different applications.
The mutex function is similar to that of the critical section, but the mutex can be named, that is, it can be used across processes. Therefore, creating mutex requires more resources. Therefore, if you only use it within a process, using the critical section will bring speed advantages and reduce resource occupation.

Semaphores)

Event)
Event objects can also be synchronized by means of notification operations. In addition, threads in different processes can be synchronized.

 

 

 

I have never paid much attention to this issue before. When I interviewed a company a few days ago, the interviewer mentioned the implementation of pthread_cond_wait/pthread_cond_signal. At that time, the answer was not very good, then we checked the nptl code. The day before yesterday, someone asked the question about semaphores and mutex locks. I 'd like to summarize their differences and implementations.

First, understand the semantic differences between semaphores and thread mutex locks:

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>>>>>>>>>>>>

Reference the content of the previous post of Cu:
"Semaphores are used for multi-thread and multi-task synchronization. When a thread completes an action, it tells other threads through semaphores that other threads perform some other actions (when everyone is in sem_wait, ). The mutex lock is used for multi-thread multi-task mutex. If a thread occupies a certain resource, other threads cannot access it until the thread is unlocked and other threads can use the resource. For example, you may need to lock the access to global variables. After the operation is complete, you must unlock the global variables. Sometimes the lock and semaphore will be used at the same time"
That is to say, semaphores do not necessarily lock a certain resource, but are the concept of a process. For example, there are two threads A and B, thread B must wait for thread a to complete a task and then perform the following steps. This task does not necessarily lock a certain resource, or perform some computing or data processing. The thread mutex is the concept of "locking a resource". During the lock period, other threads cannot operate on the protected data. In some cases, the two are interchangeable.

Differences between the two:

Scope
Semaphore: process or thread (Linux only)
Mutex lock: between threads

Lock
Semaphores: as long as the semaphores value is greater than 0, other threads can successfully sem_wait, And the semaphores value minus one after success. If the value is not greater than 0, sem_wait is blocked until the value of sem_post is added after the release of sem_post. In one sentence, the semaphore value> = 0.
Mutex lock: as long as it is locked, no other thread can access protected resources. If no lock exists, the resource is obtained successfully. Otherwise, the resource is blocked and waits for the resource to be available. In a word, the vlaue of the thread mutex lock can be negative.

<<

Next, we need to analyze the implementation mechanism of mutex lock between semaphores and threads.

In Linux, semaphores and thread mutex locks are implemented through the futex system.

Futex is a basic tool for locking and building high-level abstract locks such as semaphores and POSIX mutex on Linux. They first appeared in version 2.5.7 developed by the kernel. Their semantics was fixed in version 2.5.40 and then appeared in the kernel of 2.6.x series stable version.

Futex is short for fast userspace mutex, which means a fast user space mutex. The Linux kernel uses them as pre-fabricated components for Fast User space locks and semaphores. Futex is very basic. With its own excellent performance, it builds an abstraction of higher-level locks, such as POSIX mutex. Most programmers do not need to use futex directly. It is generally used to implement a system library like nptl.

Futex consists of a memory space (an aligned integer variable) that can be shared by multiple processes; the value of this integer variable can be increased or decreased by calling the atomic operation commands provided by the CPU through the assembly language, and a process can wait until the value changes to a positive number. Almost all futex operations are completed in the application space. Only when the operation results are inconsistent and need to be arbitrated will the operation be executed in the operating system kernel space. This mechanism allows the use of the futex lock primitive to have a very high execution efficiency: because the vast majority of operations do not need to be arbitrated between multiple processes, most operations can be executed in the application space, instead of using (relatively costly) kernel system calls.

----------------------------------------------------------------
Insert a description of the x86 atomic operation commands:

Cmpxchg compares and exchanges commands with the following semantics:

int CompareAndExchange(int *ptr, int old, int new)
{
int actual = *ptr;
if (actual == old)
*ptr = new;
return actual;
}

The instructions in the Intel white paper are as follows:
(* Accumulator = Al, ax, eax, or Rax depending on whether a byte, word, doubleword, or
Quadword comparison is being saved med *)
If accumulator = dest
Then
ZF limit 1;
DeST parse SRC;
Else
ZF limit 0;
Accumulator implements DEST;
FI;

This atomic operation can be used to implement spin locks. This is described in the previous article:

void lock(lock_t *lock) {
while (CompareAndExchange(&lock->flag, 0, 1) == 1)
; // spin
}
void unlock(lock_t *lock) {
lock->flag = 0;
}
 

Description of atomic operations under SMP:
Atomic operations are inseparable and will not be interrupted by any other tasks or events after execution. In a single processor system (uniprocessor), operations that can be completed in a single command can be considered as "Atomic operations", because interruptions can only occur between commands. This is also why test_and_set, test_and_clear and other commands are introduced in some CPU command systems for the critical resource mutex. The symmetric multi-processor structure is different. Because multiple processors in the system run independently, operations that can be completed in a single command may be affected.
On the X86 platform, the CPU provides a means to lock the bus during command execution. There is a lead on the CPU chip # hlock pin. If the assembly language program adds the prefix "Lock" before a command ", the compiled machine code lowers the potential of the # hlock pin when the CPU executes this command until the end of this command, thereby locking the bus, in this way, other CPUs on the same bus cannot access the memory temporarily through the bus, ensuring the atomicity of this command in a multi-processor environment.
Of course, not all commands can be prefixed with lock. Only add, ADC, And, BTC, BTR, BTS, cmpxchg, Dec, Inc, neg, not, or, you can add the lock command before SBB, sub, XOR, xadd, and xchg commands to perform atomic operations.
----------------------------------------------------------------

The advertisement is back. Let's continue.

Futex is stored in the shared memory of the user space and operated through atomic operations. In most cases, if the resource does not compete for use, the process or thread can immediately obtain the resource success. In fact, there is no need to call the system call and the system is stuck in the kernel. In fact, the function of futex is to reduce the number of system calls and improve the system performance.

Implementation principle of thread mutex pthread_mutex_t:

Pthread_mutex_lock:
Atomic_dec (pthread_mutex_t.value );
If (pthread_mutex_t.value! = 0)
Futex (wait)
Else
Success

Pthread_mutex_unlock:
Atomic_inc (pthread_mutex_t.value );
If (pthread_mutex_t.value! = 1)
Futex (wakeup)
Else
Success

Semaphores sem_t implementation principle (directly from glibc/nptl/DESIGN-sem.txt ):

Sem_wait (sem_t * SEM)
{
For (;;){

If (atomic_decrement_if_positive (SEM-> count ))
Break;

Futex_wait (& SEM-> count, 0)
}
}

Sem_post (sem_t * SEM)
{
N = atomic_increment (SEM-> count );
// Pass the new value of SEM-> count
Futex_wake (& SEM-> count, n + 1 );
}

Compared with the implementation of pthread_mutex_unlock () and sem_post (), we find that, in any case, sem_post () will call futex_wake () for system calling. However, pthread_mutex_unlock () is in line with the original intention of futex, and futex_wake () is called only when arbitration is required (). So What Are arbitration conditions?

As mentioned above, the semantic difference between semaphores and thread mutex locks is that the semaphores value> = 0, while the value of thread mutex locks can be negative.
There is no difference between the two lock operations. A semaphore can obtain resources as long as the value is greater than 0. The thread mutex lock requires value = 1.
But for the unlock operation, there are some differences between the two. The semaphore and thread mutex lock increase the corresponding value. If the value is 1 after 1 is added, for the thread mutex lock, the resource is actually available, and no other thread is waiting for this resource; otherwise, other threads are waiting for this resource, you need to call the futex system call to wake them up. However, for semaphores, the value must be greater than or equal to 0. After adding 1, even if the value is 1, it cannot be determined that there are no other processes or threads waiting for resources. Therefore, the futex system must be called. For example:

# Include <stdio. h>
# Include <semaphore. h>
# Include <pthread. h>

Sem_t sem_a;
Void * task1 ();

Int main (void)
{
Int ret = 0;
Pthread_t thrd1;
Pthread_t thrd2;
Sem_init (& sem_a, 0, 1 );
Ret = pthread_create (& thrd1, null, task1, null); // create a subthread
Ret = pthread_create (& thrd2, null, task1, null); // create a subthread
Pthread_join (thrd1, null); // wait until the sub-thread ends.
Pthread_join (thrd2, null); // wait until the sub-thread ends.
}

Void * task1 ()
{
Int sval = 0;
Sem_wait (& sem_a); // hold the semaphore
Sleep (5); // do_nothing
Sem_getvalue (& sem_a, & sval );
Printf ("SEM value = % d/N", sval );
Sem_post (& sem_a); // release the semaphore
}

The above SEM value is initialized to 1, but there are two threads competing for resources. Then the first thread gets the resource successfully. When it is unlocked, the value of SEM becomes 1. However, there is actually a thread waiting for resources at this time. Therefore, you must call the futex_wake () system to wake up the thread waiting for resources.

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.