Linux system programming mutex semaphores, multitask mutex and synchronization

Multitask Mutual Exclusion and Synchronization

Overview of Mutual Exclusion and Synchronization

In a multitask operating system, multiple tasks running at the same time may need to access/use the same resource. Many tasks depend on each other. One task depends on another task. Synchronization and mutually exclusion are the solutions to both problems.
mutex
A common resource can only be used by one process or thread at a time. Multiple processes or threads cannot use the common resource at the same time. The synchronization and mutually exclusive methods of processes and threads in POSIX standard include semaphore and mutually exclusive lock.
synchronization
Two or more processes or threads coordinate their steps during operation. Running synchronization in a predetermined order is based on mutual exclusion.

mutex

The concept of mutex:
Mutex is a simple locking method to control access to shared resources. Mutex has only two states, lock and unlock. Mutex should be applied for first before accessing the resource. If mutex is in unlock state, mutex will be applied for and immediately locked.
If mutex is locked, the applicant is blocked by default
The unlock operation should be performed by the locker

Operation of Mutex

Create Mutex

Mutex with pthread_ Mutex_ The t data type indicates that the mutex must be initialized before it can be used
Statically Assigned Mutex
pthread_mutex_t mutex= PTHREAD_MUTEX_INITALIZER;
Dynamic allocation of mutex
pthread_mutex_t mutex;
pthread_mutex_init(&mutex,NULL);
When all threads that have used this mutex no longer need to use it, it should be called
pthread_mutex_destory destroy mutex

Initialize Mutex

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, 
const pthread_mutexattr_t * attr)

Functions:
Initialize a mutex
Parameters:
Mutex: mutex address
attr: Mutex property, NULL is the default property
Return value
Successful return 0, failure return non-0

Mutex Lock Lock (1) Most commonly used

#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);

function

Lock the mutex, and if it is already locked, the caller will block until the mutex is unlocked

parameter

Mutex: Mutex address, which is the specified mutex

Return value

Successful return 0, failure return non-0

Mutex Lock Lock (2) Less commonly used

#include <pthread.h>
int pthread_mutex_trylock(pthread_mutex_t *mutex);

function

Lock on mutex, if already locked, lock fails, function returns immediately

parameter

Mutex: mutex address

Return value

Successful return 0, failure return non-0

Mutex Lock Unlock

#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t * mutex);

function

Unlock the specified mutex

parameter

Mutex: mutex address

Return value

Successful return 0, failure return non-0

Destroy Mutex

#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);

function

Destroy a specified mutex

parameter

Mutex: mutex address

Return value

Successful return 0, failure return non-0

Examples of using mutexes

Consequences of not using mutexes

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
int money = 10000;
void *pthread_fun1(void* arg)
{
        int get, yu, shiji;
        get = 10000;
        printf("Zhang San is inquiring about the balance... \n");
        sleep(1);
        yu = money;

        printf("Zhang San is drawing money... \n");
        sleep(1);
        if(get > yu)
        {
                shiji = 0;
        }
        else
        {
                shiji = get;
                yu = yu - get;
                money = yu;
        }
        printf("Zhang San wants to go %d The money has actually gone %d money ,The balance is %d money\n", get ,shiji, yu);
        pthread_exit(NULL);
}
void * pthread_fun2(void* arg)
{
        int get, yu, shiji;
        get = 10000;

        printf("Li Si is inquiring about the balance\n");
        sleep(1);
        yu = money;

        printf("Li Si is drawing money\n");
        sleep(1);
        if(get > yu)
        {
                shiji = 0;
        }
        else
        {
                shiji = get;
                yu = yu - get;
                money = yu;
        }

        printf(" Li Si wants to take %d Money actually taken %d Money balance is%d money\n", get, shiji, yu);
        pthread_exit(NULL);

}
int main()
{
        pthread_t thread1, thread2;
        if(pthread_create(&thread1, NULL, pthread_fun1, NULL)  != 0)
        {
                perror("fail to pthread_create");
                exit(1);
        }

        if(pthread_create(&thread2, NULL, pthread_fun2, NULL)  != 0)
        {
                perror("fail to pthread_create");
                exit(1);
        }

        pthread_join(thread1,NULL);
        pthread_join(thread2,NULL);
        return 0;
}


A total of 10,000 yuan, the main card, and the secondary card, two people together, the result is two people take 10,000 yuan, so reasonable, certainly unreasonable, the bank will not let you do so

After using mutex

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
int money = 10000;

---------Step One---------Mutex Use the first step to create a mutex---
pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
void *pthread_fun1(void* arg)
{
        int get, yu, shiji;
        get = 10000;
---------Step 3---------Lock operations on shared resources---
	    pthread_mutex_lock(&mymutex);
        printf("Zhang San is inquiring about the balance... \n");
        sleep(1);
        yu = money;

        printf("Zhang San is drawing money... \n");
        sleep(1);
        if(get > yu)
        {
                shiji = 0;
        }
        else
        {
                shiji = get;
                yu = yu - get;
                money = yu;
        }
        printf("Zhang San wants to go %d The money has actually gone %d money ,The balance is %d money\n", get ,shiji, yu);
---------Step 5---------Unlock mutually exclusive locks after a shared resource operation is completed---
        pthread_mutex_unlock(&mymutex);

        pthread_exit(NULL);
}
void * pthread_fun2(void* arg)
{
        int get, yu, shiji;
        get = 10000;
---------Step 4---------Lock operations on shared resources---
	    pthread_mutex_lock(&mymutex);
        printf("Li Si is inquiring about the balance\n");
        sleep(1);
        yu = money;

        printf("Li Si is drawing money\n");
        sleep(1);
        if(get > yu)
        {
                shiji = 0;
        }
        else
        {
                shiji = get;
                yu = yu - get;
                money = yu;
        }

        printf(" Li Si wants to take %d Money actually taken %d Money balance is%d money\n", get, shiji, yu);
---------Step 6---------Unlock mutually exclusive locks after a shared resource operation is completed---
        pthread_mutex_unlock(&mymutex);
	  pthread_exit(NULL);
}
int main()
{
---------Step 2---------Initialize Mutex-------
        pthread_mutex_init(&mymutex,NULL);
        pthread_t thread1, thread2;
        if(pthread_create(&thread1, NULL, pthread_fun1, NULL)  != 0)
        {
                perror("fail to pthread_create");
                exit(1);
        }

        if(pthread_create(&thread2, NULL, pthread_fun2, NULL)  != 0)
        {
                perror("fail to pthread_create");
                exit(1);
        }

        pthread_join(thread1,NULL);
        pthread_join(thread2,NULL);
---------Step 7--------Destroy mutex after use of mutex-------
        pthread_mutex_destroy(&mymutex);

        return 0;
}

Semaphore

Semaphores are widely used for synchronization and mutually exclusion between processes or threads. Semaphores are essentially non-negative integer counters that are used to control access to public resources
Programming can use the result of manipulating the semaphore value to determine whether or not you have access to public resources. When the semaphore value is greater than 0, it can be accessed, otherwise it will be blocked
The P V primitive is to operate a semaphore with a P operation to reduce the SEM by 1, and a V operation to add 1 to the sem. For a P operation, if the SEM value of the semaphore is less than or equal to 0, the P operation will be blocked. If the SEM value of the semaphore is greater than 0, the P operation can be performed to reduce 1.

Semaphores are typically used for synchronization and mutual exclusion between processes or threads

1. For mutually exclusive, several processes (or threads) tend to set only one semaphore
2. When used for synchronous operations, multiple semaphores are often set and different initial values are arranged to achieve the order of execution between them.

Semaphores for Mutual Exclusion


Semaphores are used for mutual exclusion. First, the semaphore is initialized, p-operated, thread 1 executes, then v-operated, second, p-operated, thread 2 executes. To execute v-operated also means that since the semaphore value is 1, it means who executes p-operated first between the two threads. Once a thread executes p-operated first, the semaphore becomes 0. So another thread will block when it sees that the semaphore value is zero, so when a thread finishes executing the V operation, it will + 1 the original semaphore value, then after changing from 0 to 1, the other thread will be able to execute normally, which is the basic nature of semaphores used to mutually exclusive it

Semaphore for synchronization


Two semaphores are set here. (If there are only two threads, one is fine.) First, set the value of semaphore 1 to 1, the value of semaphore 2 to 0, Thread 1 to perform the p operation of SEM 1, Thread 2 to perform the p operation of sem2, since the initial Sem2 value is 0, the position of thread 2 will be blocked, so thread 1 to perform the p operation first, after execution, Thread 1 is running normally. When thread 2's v operation changes SEM 2's value from + 1 to 1 after thread 2's operation, then my thread 2 can execute p operation normally. This is a synchronization problem through semaphores

>Operation of semaphores

Initialization of semaphores

#include<semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);

function
Create a semaphore and initialize its value
parameter
sem: Address of semaphore
pshared: equal to 0, semaphores are shared between threads; Not equal to 0, semaphores are shared between processes
Value: Initial value of semaphore
Return value
Successfully returned 0, failed returned -1

p-operation of semaphore

#include<semaphore.h>
int sem_wait(sem_t* sem);

function
Reduce the semaphore value by 1. If the semaphore value is less than or equal to 0, this function will cause the caller to block
parameter
sem; Semaphore address
Return value
Successfully returned 0, failed returned -1

This one below is not commonly used, not blocked
#include<semaphore.h>
int sem_trywait(sem_t *sem);

function
Reduce the value of the semaphore by 1. If the value of the semaphore is less than or equal to 0, the operation on the semaphore fails and the function returns immediately
parameter
sem: semaphore address
Return value
Successfully returned 0, failed returned -1

v operation of semaphore

#include<semaphore.h>
int sem_post(sem_t *sem);

function
Increase the semaphore value by 1 and signal wake-up waiting threads
parameter
sem: semaphore address
Return value
Successfully returned 0, failed returned -1

Get the count value of the semaphore

#include <semaphore.h>
int sem_getvalue(sem_t *sem, int *sval);

function
Gets the sem-identified semaphore value, which is saved in sval
parameter
sem: semaphore address
sval: Address to store semaphore values
Return value
Successfully returned 0, failed returned -1

Destruction of semaphores

#include <semaphore.h>
int sem_destory(sem_t *sem);

function
Delete sem-denoted semaphores
parameter
sem: semaphore address
Return value
Successfully returned 0, failed returned -1

Use of semaphores

Semaphore for Mutual Exclusion

No semaphores were added first

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

void printer(char* str)
{
        while(*str)
        {
                putchar(*str); //Output a character
                fflush(stdout);//Refresh Buffer
                str++;
                sleep(1);      //Output a character every second
        }
}

void* thread_fun1(void* arg)
{
        char* str1 = "hello";
        printer(str1);
}
void* thread_fun2(void* arg)
{
        char* str2 = "world";
        printer(str2);
}
int main()
{
        pthread_t tid1, tid2;

        pthread_create(&tid1, NULL, thread_fun1, NULL);
        pthread_create(&tid2, NULL, thread_fun2, NULL);

        pthread_join(tid1,NULL);
        pthread_join(tid2,NULL);
        printf("\n");
        return 0;
}

You can see that it's not the hello world or world hello we want

#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

//Step 1 Create a semaphore
sem_t sem;

void printer(char* str)
{
        //Step 3 Perform the p operation
        //Since semaphores are used for mutual exclusion and the initial semaphore value is set to 1, two threads perform a p-operation
        //Threads that execute the p operation continue to execute, then blocked waiting for the p operation to execute
        sem_wait(&sem);

        while(*str)
        {
                putchar(*str); //Output a character
                fflush(stdout);//Refresh Buffer
                str++;
                sleep(1);      //Output a character every second
        }

        //Step 4 Perform the v operation
        sem_post(&sem);
}
void* thread_fun1(void* arg)
{
        char* str1 = "hello";
        printer(str1);
}
void* thread_fun2(void* arg)
{
        char* str2 = "world";
        printer(str2);
}
int main()
{
        //The second step initializes the semaphore
        sem_init(&sem, 0, 1);

        pthread_t tid1, tid2;

        pthread_create(&tid1, NULL, thread_fun1, NULL);
        pthread_create(&tid2, NULL, thread_fun2, NULL);

        pthread_join(tid1,NULL);
        pthread_join(tid2,NULL);
        printf("\n");

        sem_destroy(&sem);
        return 0;
}


Equivalent to printing two files at the same time, cannot first line first file content, second line second file content

The semaphore implements synchronization.

Synchronization is sequential printing on a mutually exclusive basis

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <pthread.h>
char ch = 'a';

void* pthread_g(void* arg)
{
        while(1)
        {
                ch++;
                sleep(1);
        }
}

void* pthread_p(void* arg)
{
        while(1)
        {
                printf("%c", ch);
                fflush(stdout);
        }
}
int main()
{
        pthread_t tid1, tid2;

        pthread_create(&tid1, NULL, pthread_g, NULL);
        pthread_create(&tid2, NULL, pthread_p, NULL);

        pthread_join(tid1, NULL);
        pthread_join(tid2, NULL);
        printf("\n");
        return 0;
}

What we want is to print a every second and do this, but the results are obviously different

So add semaphores to solve synchronization problems

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <pthread.h>
char ch = 'a';
//Synchronization using semaphores requires two semaphores if two threads synchronize
//The first step is to create two semaphores
sem_t sem_g, sem_p;
void* pthread_g(void* arg)
{
        while(1)
        {
        //In the thread that executes after step 4, perform the p operation on a semaphore whose initial value is set to 0
                sem_wait(&sem_g);
                ch++;
                sleep(1);
                sem_post(&sem_p);
        //Step 6: A semaphore with an initial semaphore value of 1 performs a v-operation after the thread that executes the post-execution finishes executing
        }
}
void* pthread_p(void* arg)
{
        while(1)
        {
        	//In the thread that executes first, the semaphore whose initial semaphore value is set to 1 performs the p operation
                sem_wait(&sem_p);
                printf("%c", ch);
                fflush(stdout);

                sem_post(&sem_g);
             //Step 5: When the thread that executes first finishes executing, the semaphore with initial semaphore value of 0 needs to be executed v
        }
}
int main()
{
        // The second step initializes the semaphore
        sem_init(&sem_g, 0 ,0);
        sem_init(&sem_p, 0 ,1);
        pthread_t tid1, tid2;

        pthread_create(&tid1, NULL, pthread_g, NULL);
        pthread_create(&tid2, NULL, pthread_p, NULL);

        pthread_join(tid1, NULL);
        pthread_join(tid2, NULL);
        printf("\n");
//Step 7 Destroy semaphores after use
        sem_destroy(&sem_g);
        sem_destroy(&sem_p);
        return 0;
}

The results are as follows:

We used semaphores to synchronize threads by letting it print, then change, and then print

Keywords: Linux

Added by vurentjie on Fri, 10 Dec 2021 19:10:07 +0200