In a multiprogramming environment , Processes are executed concurrently , There are different mutual constraints between different processes . It's called process synchronization ( Thread synchronization is the same ), It mainly solves the problem of mutual exclusive access to critical resources . For example, multiple processes access the same piece of shared memory , This shared memory must be mutually exclusive .

One . Process synchronization

stay Linux Next , There are four main solutions to process synchronization :
1. Semaphore
2. File lock
3. unlocked CAS
4. Verification method (CRC32 check )

1. Semaphore

Can use Linux The semaphore of PV Operation to achieve mutually exclusive access to critical resources .
For details, please see the blog :
http://blog.csdn.net/okiwilldoit/article/details/78401536

2. File lock

linux You can use flock() Function to lock and unlock the file . A brief introduction flock() function :
1. Defined function int flock(int fd,int operation);
2. Function description flock() It depends on the parameters operation The way specified is for the parameter fd All kinds of actions of locking or unlocking are performed on the file . This function can only lock the entire file , Unable to lock an area of the file .
3. Parameters operation There are four situations :
LOCK_SH Set up a shared lock . Multiple processes can share and lock the same file at the same time .
LOCK_EX Establish a mutex lock . A file has only one mutex lock at the same time .
LOCK_UN Unlock file .
LOCK_NB When a lock cannot be established , This operation can not be blocked , Return to the process now . Usually with LOCK_SH or LOCK_EX do OR(|) Combine .
4. Return value return 0 It means success , If there is an error, return -1, The error code is stored in errno.

Be careful : A single file cannot establish a shared lock and a mutex lock at the same time , And when you use dup() or fork() The file descriptor does not inherit this lock .

flock The release of the lock is very distinctive , You can call LOCK_UN Parameter to release the file lock , It can also be done by closing fd To release the file lock (flock The first parameter of fd), signify flock Will be automatically released as the process closes .

In short :
A process plus LOCK_SH, Other processes can also add LOCK_SH, But we can't add LOCK_EX lock .
A process plus LOCK_EX, No other process can lock this file .
This mechanism is similar to a read-write lock ,LOCK_SH It's read lock ,LOCK_EX It's a writing lock .

Here is an example :
process 1:

#include <stdio.h>
#include <sys/file.h>
#include <unistd.h>
#include <errno.h>
int main(void)  
{  
    FILE *fp = NULL;   
    if ((fp = fopen("./file_lock", "wb+")) == NULL) // Open file   
    {
        printf("file open error,errno=%d!\n",errno);
        return -1;
    }       
    if (flock(fp->_fileno, LOCK_EX) != 0) // Add a mutex to the file
        printf("file lock by others\n");// Locking failed , Blocking
    while(1)
    {     
        printf("process1-ex\n");
        sleep(1);  
    }    
    flock(fp->_fileno, LOCK_UN); // File unlock  
    fclose(fp); // Close file        
    return 0;  
}

process 2:

#include <stdio.h>
#include <sys/file.h>
#include <unistd.h>
#include <errno.h>
int main(void)  
{  
    FILE *fp = NULL;  
    int i = 0;  
    if ((fp = fopen("./file_lock", "wb+")) == NULL) // Open file
    {
        printf("file open error,errno=%d!\n",errno);
        return -1;
    }   
    if(flock(fp->_fileno, LOCK_SH) != 0)// File sharing lock
    {
        printf("file lock by others\n");// Locking failed , Blocking
    } 
    while(1) // Into the loop   
    {     
        printf("process2-sh\n");
        sleep(1);  
    }     
    flock(fp->_fileno, LOCK_UN); // Release the file lock   
    fclose(fp); // Close file
    return 0;  
}

process 1 Runtime , Other processes cannot get any locks to run ;
process 2 Runtime , Other processes can get shared locks to run ;

3. unlocked CAS visit

The lock method above , Although it can guarantee the consistency of data , But locking can cause performance degradation , Multiple processes compete for the same lock , Force context switch after preemption failure . Another way is to use atomic instructions . There is an important method called CAS(compare and swap).
CAS It's a set of primitive instructions , It's used to achieve multiple access / Variable synchronization under thread .
stay x86 The order CMPXCHG Realized CAS, In front of LOCK Atomic operation can be achieved .
CAS The primitive has three parameters , Memory address , Expectations , The new value . If the value of the memory address == Expectations , Indicates that the value has not been modified , This can be changed to a new value . Otherwise, the modification failed , return false, It's up to the user to decide what to do next .

bool CAS(T* addr, T expected, T newValue) 
{ 
      if( *addr == expected ) 
     { 
          *addr =  newValue; 
           return true; 
     } 
     return false; 
 }

How to use CAS Realizing no lock ?

For example, there are multiple processes accessing a variable in shared memory , The value of the variable read from memory is expected, You want to update the value of this variable to newValue, You can call CAS function , Update values in shared memory . Because it's a set of atomic operations , So in the process of updating , There will be no other process / Thread access to this variable .

GCC Of CAS,GCC4.1+ Version supports CAS Atomic operation of ( Complete atomic operations can be found in GCC Atomic Builtins)

bool __sync_bool_compare_and_swap (type *ptr, type oldval, type newval, ...)
type __sync_val_compare_and_swap (type *ptr, type oldval, type newval, ...)

  • 1
  • 2

4. Verification method

It can be used crc32 The way of verification , Put variables a Of crc32 It's worth recording , Put in another variable b.
Writing variables a When , Update variables a after , Then work out a Of crc32 value , Update variables b;
Reading variables a When , Put the value read out a Of crc32 Values and other variables b Compare , If it's not the same , That means variables a It's being updated , So as to realize the lock free mutex access to the variable .

It's not as efficient as CAS, But relative CAS Come on , It's simple and controllable .

Two . Thread synchronization

Different threads in the same process can share global variables , So threads are mutually exclusive to access global variables , It also needs synchronization . There are mainly five ways :
1. The mutex (pthread_mutex_t )
2. Condition variables, ( Need to be used in combination with mutex )
3. Semaphore
4. unlocked CAS
5. Verification method (CRC32 check )

Similar to process synchronization , Semaphores and no locks CAS Thread synchronization is also supported , Other ways are mutex and conditional semaphores .