Chapter 9 Concurrency and Competition Learning in "Detailed Explanation and Practice of Atomic Embedded Linux Driver Development"
[Copy link]
This post was last edited by Changjianze1 on 2024-4-13 14:58
Chapter 8 Chapter 9 Learning Experience of LINUX Concurrency and Competition
An atomic operation
The original meaning of atom is "the smallest particle that cannot be further divided", and atomic operation means "one or a series of operations that cannot be interrupted", which can ensure that instructions run in an atomic manner, that is, the execution process is not interrupted. If it is interrupted, the execution result may not be consistent with the expectation.
atomic_read(atomic_t * v);
This function performs an atomic read operation on an atomic type variable, and it returns the value of the atomic type variable v.
atomic_set(atomic_t * v, int i);
This function sets the value of the atomic type variable v to i.
void atomic_add(int i, atomic_t *v);
This function adds value i to the atomic type variable v.
atomic_sub(int i, atomic_t *v);
This function subtracts i from the atomic type variable v.
int atomic_sub_and_test(int i, atomic_t *v);
This function subtracts i from the atomic type variable v, and determines whether the result is 0. If it is 0, it returns true, otherwise it returns false.
void atomic_inc(atomic_t *v);
This function atomically increases the value of the atomic type variable v by 1.
void atomic_dec(atomic_t *v);
This function atomically subtracts 1 from the atomic type variable v.
int atomic_dec_and_test(atomic_t *v);
This function atomically subtracts 1 from the atomic type variable v and determines whether the result is 0. If it is 0, it returns true, otherwise it returns false.
int atomic_inc_and_test(atomic_t *v);
This function atomically increases the atomic type variable v by 1 and determines whether the result is 0. If it is 0, it returns true, otherwise it returns false.
int atomic_add_negative(int i, atomic_t *v);
This function atomically increases the atomic type variable v by I and determines whether the result is a negative number. If it is, it returns true, otherwise it returns false.
int atomic_add_return(int i, atomic_t *v);
This function atomically increases the atomic type variable v by i and returns a pointer to v.
int atomic_sub_return(int i, atomic_t *v);
This function subtracts i from the atomic type variable v and returns a pointer to v.
int atomic_inc_return(atomic_t * v);
This function atomically increases the atomic type variable v by 1 and returns a pointer to v.
int atomic_dec_return(atomic_t * v);
This function atomically decreases the atomic type variable v by 1 and returns a pointer to v.
Two spin locks
Atomic operations can only protect integer variables or bits, but in actual usage environments it is impossible to have only integer variables or bits with simple critical sections.
The spin of a spin lock means spinning in place. The purpose of spinning in place is to wait for the spin lock to be available and to access shared resources.
shortcoming:
The thread waiting for the spin lock will always be in the spinning state, which will waste processor time and reduce system performance, so the spin lock cannot be held for too long. Therefore, the spin lock is suitable for short-term lightweight locking.
Spin lock is a mutually exclusive device, which has only two states: locked and unlocked.
Spinlock example:
Test routine:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
//#include<vector>
pthread_spinlock_t spin_lock;
char num = 0;
void xprintf(char *str)
{
int i = 0;
while(str[i] != '\0')
{
printf("%c", str[i++]);
fflush(stdout);
sleep(1);
}
return NULL;
}
void *producer(void *arg)
{
int times = 1000000;
while(times--)
{
pthread_spin_lock(&spin_lock);
num+=1;
printf("%d",num);
pthread_spin_unlock(&spin_lock);
}
}
void *comsumer(void *arg)
{
int times = 1000000;
while(times--)
{
pthread_spin_lock(&spin_lock);
num-=1;
printf("%d",num);
pthread_spin_unlock(&spin_lock);
}
}
int main()
{
pthread_spin_init(&spin_lock,0);
pthread_t thread1,thread2;
pthread_create(&thread1,NULL,producer,NULL);
pthread_create(&thread2,NULL,comsumer,NULL);
pthread_join(thread1,NULL);
pthread_join(thread2,NULL);
printf("%d",num);
return 0;
}
Three semaphores
The concept of semaphore has actually been learned in micro-operating systems such as UCOS. It is somewhat similar, but there are still differences.
Characteristics of semaphores:
1Semaphore can make the waiting resource enter the dormant state immediately, so it is suitable for those occasions where resources are occupied for a long time.
2Semaphores cannot be used in terminals because they cause sleep, and interrupts cannot sleep.
3. If the shared resource is held for a short time, it is not suitable to use semaphores, because the overhead caused by frequent sleep and switching is far greater than the advantage brought by semaphores.
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
sem_t sem;
void xprintf(char *str)
{
int i = 0;
while(str[i] != '\0')
{
printf("%c", str[i++]);
fflush(stdout);
sleep(1);
}
return NULL;
}
void *task_fun1(void *arg)
{
sem_wait(&sem);
xprintf((char *)arg);
sem_post(&sem);
return NULL;
}
void *task_fun2(void *arg)
{
sem_wait(&sem);
xprintf((char *)arg);
sem_post(&sem);
return NULL;
}
int main(int argc, char const *argv[])
{
sem_init(&sem, 0, 1);
pthread_t pthid1, pthid2;
pthread_create(&pthid1, NULL, task_fun1, "TASK1");
pthread_create(&pthid2, NULL, task_fun2, "TASK2");
pthread_join(pthid1, NULL);
pthread_join(pthid2, NULL);
sem_destroy(&sem);
return 0;
}
Multithreading is used here, so add -pthread
gcc sem_thread.c -lpthread
Practical testing
|