In concurrent programming development, lock mechanisms are core technologies to ensure data consistency and thread safety. Whether in resource-constrained microcontroller environments or feature-rich Linux systems, lock mechanisms play a crucial role.
Lock Mechanisms
What is a Lock Mechanism?
A lock mechanism is a synchronization primitive used to control access to shared resources by multiple execution units. It ensures that only one execution unit can access the protected resource at any given time, thus avoiding data races and inconsistent states.
Attempt to acquire lock
Is YsY
Is
Thread A
Is the lock available?
Lock acquired successfully
Wait or block
Access shared resource
Release lock
Retry or give up
Other threads can acquire the lock
Classification of Locks
Locks can be classified by granularity, behavior, and scope:
Coarse-grained locks
Fine-grained locks
Blocking locks
Non-blocking locks
Intra-process locks
Inter-process locks
Global locks
Table-level locks
Row-level locks
Field-level locks
Mutex locks
Read-write locks
Spin locks
CAS operations
Thread locks
Memory locks
File locks
Semaphores
Lock Mechanisms in Microcontrollers
Characteristics and Constraints
The microcontroller environment has the following characteristics:
- • Resource Constraints: Limited memory and CPU resources
- • Real-time Requirements: Need to ensure response time
- • Single-core Architecture: Usually only one CPU core
- • Interrupt-driven: Needs to handle hardware interrupts
Common Lock Mechanisms
1. Interrupt Lock
The most basic lock mechanism, ensuring atomicity by disabling interrupts:
// Interrupt lock example
typedef struct {
uint32_t saved_irq_state;
} irq_lock_t;
void irq_lock_acquire(irq_lock_t *lock) {
lock->saved_irq_state = __get_PRIMASK();
__disable_irq();
}
void irq_lock_release(irq_lock_t *lock) {
if (!lock->saved_irq_state) {
__enable_irq();
}
}
2. Atomic Operation Lock
Utilizes CPU atomic instructions to implement lightweight locks:
// Atomic operation lock example
typedef volatile uint32_t atomic_lock_t;
#define ATOMIC_LOCK_INIT 0
#define ATOMIC_LOCK_LOCKED 1
static inline int atomic_lock_try_acquire(atomic_lock_t *lock) {
return __sync_bool_compare_and_swap(lock, ATOMIC_LOCK_INIT, ATOMIC_LOCK_LOCKED);
}
static inline void atomic_lock_release(atomic_lock_t *lock) {
__sync_lock_test_and_set(lock, ATOMIC_LOCK_INIT);
}
3. FreeRTOS Mutex Lock
Standard mutex lock in RTOS environments:
#include "FreeRTOS.h"
#include "semphr.h"
// FreeRTOS mutex lock example
SemaphoreHandle_t xMutex;
void init_mutex(void) {
xMutex = xSemaphoreCreateMutex();
}
void critical_section(void) {
if (xSemaphoreTake(xMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
// Critical section code
xSemaphoreGive(xMutex);
}
}
Flowchart of Lock Mechanism in Microcontrollers
Is Is
No No
Interrupt occurs
Is there an interrupt lock?
Delay interrupt handling
Handle interrupt immediately
Wait for lock release
Process delayed interrupt
Interrupt handling completed
Resume execution
Lock Mechanisms in Linux
Characteristics and Advantages
The Linux environment has the following characteristics:
- • Multi-core Architecture: Supports true parallel execution
- • Rich System Calls: Provides various synchronization primitives
- • Process Isolation: Requires inter-process communication mechanisms
- • Memory Management: Virtual memory and page management
Common Lock Mechanisms
1. POSIX Thread Lock
Mutex based on the pthread library:
#include <pthread.h>
// POSIX mutex lock example
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *thread_function(void *arg) {
pthread_mutex_lock(&mutex);
// Critical section code
pthread_mutex_unlock(&mutex);
return NULL;
}
2. Spin Lock
Suitable for locks held for a short time:
#include <pthread.h>
// Spin lock implementation
typedef struct {
volatile int locked;
} spinlock_t;
void spin_lock(spinlock_t *lock) {
while (__sync_lock_test_and_set(&lock->locked, 1)) {
// Spin wait
__asm__ volatile("pause");
}
}
void spin_unlock(spinlock_t *lock) {
__sync_lock_release(&lock->locked);
}
3. File Lock
Used for inter-process synchronization:
#include <sys/file.h>
#include <fcntl.h>
// File lock example
int acquire_file_lock(const char *filename) {
int fd = open(filename, O_RDWR | O_CREAT, 0644);
if (fd == -1) return -1;
if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
close(fd);
return -1;
}
return fd;
}
void release_file_lock(int fd) {
flock(fd, LOCK_UN);
close(fd);
}
4. Semaphore
Used for resource counting and synchronization:
#include <semaphore.h>
// Semaphore example
sem_t semaphore;
void init_semaphore(void) {
sem_init(&semaphore, 0, 1); // Binary semaphore initialized to 1
}
void producer(void) {
// Produce resource
sem_post(&semaphore); // Increase semaphore
}
void consumer(void) {
sem_wait(&semaphore); // Wait for resource
// Consume resource
}
Architecture Diagram of Linux Lock Mechanisms
Hardware Layer
Kernel Space
User Space
Application
pthread Library
System Call Interface
File System
Process Scheduler
Memory Management
Device Driver
CPU Core
Memory
Interrupt Controller
Commonalities and Differences Analysis
Commonalities
Common goals of lock mechanisms:
Core concepts:
Usage patterns:
Protect shared resources
Avoid data races
Ensure data consistency
Critical section
Atomic operations
Synchronization primitives
Acquire lock
Access resource
Release lock
Differences Comparison
| Feature | Microcontroller Environment | Linux Environment |
| Resource Constraints | Strictly limited | Relatively relaxed |
| Concurrency Model | Interrupt-driven | Multithreaded/Multiprocess |
| Lock Granularity | Coarse-grained | Fine-grained |
| Implementation Complexity | Simple and direct | Complex and diverse |
| Performance Overhead | Minimized | Acceptable |
| Debugging Difficulty | Difficult | Relatively easy |
Practical Case Analysis
Shared Counter Protection
Problem Description
Multiple threads/tasks need to safely access and modify a shared counter.
Microcontroller Implementation
// Counter protection in microcontroller environment
typedef struct {
volatile uint32_t counter;
irq_lock_t lock;
} safe_counter_t;
void counter_increment(safe_counter_t *counter) {
irq_lock_acquire(&counter->lock);
counter->counter++;
irq_lock_release(&counter->lock);
}
uint32_t counter_get_value(safe_counter_t *counter) {
uint32_t value;
irq_lock_acquire(&counter->lock);
value = counter->counter;
irq_lock_release(&counter->lock);
return value;
}
Linux Implementation
// Counter protection in Linux environment
typedef struct {
volatile uint32_t counter;
pthread_mutex_t mutex;
} safe_counter_t;
void counter_init(safe_counter_t *counter) {
counter->counter = 0;
pthread_mutex_init(&counter->mutex, NULL);
}
void counter_increment(safe_counter_t *counter) {
pthread_mutex_lock(&counter->mutex);
counter->counter++;
pthread_mutex_unlock(&counter->mutex);
}
uint32_t counter_get_value(safe_counter_t *counter) {
uint32_t value;
pthread_mutex_lock(&counter->mutex);
value = counter->counter;
pthread_mutex_unlock(&counter->mutex);
return value;
}
Producer-Consumer Pattern
Microcontroller Implementation (FreeRTOS)
#include "FreeRTOS.h"
#include "queue.h"
#define QUEUE_SIZE 10
QueueHandle_t xQueue;
void producer_task(void *pvParameters) {
int data = 0;
while (1) {
if (xQueueSend(xQueue, &data, pdMS_TO_TICKS(100)) == pdTRUE) {
data++;
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void consumer_task(void *pvParameters) {
int received_data;
while (1) {
if (xQueueReceive(xQueue, &received_data, pdMS_TO_TICKS(1000)) == pdTRUE) {
// Process data
printf("Received: %d\n", received_data);
}
}
}
Linux Implementation
#include <pthread.h>
#include <semaphore.h>
#define BUFFER_SIZE 10
typedef struct {
int buffer[BUFFER_SIZE];
int in, out;
sem_t empty, full;
pthread_mutex_t mutex;
} circular_buffer_t;
void producer(circular_buffer_t *cb, int item) {
sem_wait(&cb->empty);
pthread_mutex_lock(&cb->mutex);
cb->buffer[cb->in] = item;
cb->in = (cb->in + 1) % BUFFER_SIZE;
pthread_mutex_unlock(&cb->mutex);
sem_post(&cb->full);
}
int consumer(circular_buffer_t *cb) {
int item;
sem_wait(&cb->full);
pthread_mutex_lock(&cb->mutex);
item = cb->buffer[cb->out];
cb->out = (cb->out + 1) % BUFFER_SIZE;
pthread_mutex_unlock(&cb->mutex);
sem_post(&cb->empty);
return item;
}
Common Pitfalls Handling
Priority Inversion
Shared resource
Low priority task
Medium priority task
High priority task
Shared resource
Low priority task
Medium priority task
High priority task
Medium priority task runs
High priority task is blocked by medium priority task
Low priority task cannot release lock
Attempt to acquire lock (blocked)
Preempt CPU
Solution: Priority Inheritance
// Mutex with priority inheritance
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
pthread_mutex_init(&mutex, &attr);
ABA Problem
// ABA problem example
typedef struct node {
int value;
struct node *next;
} node_t;
// Incorrect CAS operation
node_t *head = NULL;
void push(int value) {
node_t *new_node = malloc(sizeof(node_t));
new_node->value = value;
do {
new_node->next = head;
} while (!__sync_bool_compare_and_swap(&head, new_node->next, new_node));
}
// Solution: Use version number or flag
typedef struct node {
int value;
struct node *next;
uint32_t version; // Version number
} node_t;
Lock Contention
Wait for lock
Wait for lock
Wait for lock
Wait for lock
Thread A
Lock
Thread B
Thread C
Thread D
Lock holder
Release lock
Wake up waiting threads
Thread scheduling
Solution: Lock Separation
// Lock separation example
typedef struct {
pthread_mutex_t read_mutex;
pthread_mutex_t write_mutex;
int reader_count;
pthread_mutex_t count_mutex;
} rw_lock_t;
void read_lock(rw_lock_t *rw) {
pthread_mutex_lock(&rw->count_mutex);
rw->reader_count++;
if (rw->reader_count == 1) {
pthread_mutex_lock(&rw->write_mutex);
}
pthread_mutex_unlock(&rw->count_mutex);
}
void write_lock(rw_lock_t *rw) {
pthread_mutex_lock(&rw->write_mutex);
}

How to reduce bugs in coding, testing, and debugging phases?

What are the differences between domestic and foreign programmers?

The impact of stack growth direction on embedded development