1. 基于C语言联合体的内存映射模型设计
#include <stdio.h>
#include <stdint.h>
// 定义联合体来表示不同用途的内存映射区域
union MemoryMap {
// 用于存储设备状态
struct {
uint8_t statusByte;
uint32_t statusWord;
} deviceStatus;
// 用于数据传输
struct {
uint16_t dataHalfWord;
double dataDouble;
} dataTransfer;
};
// 初始化内存映射区域
void initMemoryMap(union MemoryMap *mmap, uint8_t statusByte, uint32_t statusWord, uint16_t dataHalfWord, double dataDouble) {
mmap->deviceStatus.statusByte = statusByte;
mmap->deviceStatus.statusWord = statusWord;
mmap->dataTransfer.dataHalfWord = dataHalfWord;
mmap->dataTransfer.dataDouble = dataDouble;
}
// 获取设备状态字节
uint8_t getDeviceStatusByte(const union MemoryMap *mmap) {
return mmap->deviceStatus.statusByte;
}
// 获取设备状态32位整数
uint32_t getDeviceStatusWord(const union MemoryMap *mmap) {
return mmap->deviceStatus.statusWord;
}
// 获取数据传输16位整数
uint16_t getDataHalfWord(const union MemoryMap *mmap) {
return mmap->dataTransfer.dataHalfWord;
}
// 获取数据传输64位浮点数
double getDataDouble(const union MemoryMap *mmap) {
return mmap->dataTransfer.dataDouble;
}
2. 多线程环境下读写操作面临的问题
- 数据竞争:多个线程同时读写联合体的不同成员,可能导致数据不一致。例如,一个线程正在更新
deviceStatus
,而另一个线程同时读取dataTransfer
,可能会读到部分更新的数据。
- 缓存一致性:现代CPU通常有高速缓存,不同线程可能在各自的缓存中保留联合体的副本。如果一个线程更新了联合体,其他线程可能不会立即看到更新后的值。
3. 解决方案
- 互斥锁(Mutex):使用互斥锁来保护对联合体的读写操作。在读写联合体之前,线程必须先获取互斥锁,操作完成后释放互斥锁。
#include <pthread.h>
pthread_mutex_t memoryMapMutex = PTHREAD_MUTEX_INITIALIZER;
// 线程安全的获取设备状态字节
uint8_t getDeviceStatusByteThreadSafe(const union MemoryMap *mmap) {
pthread_mutex_lock(&memoryMapMutex);
uint8_t result = mmap->deviceStatus.statusByte;
pthread_mutex_unlock(&memoryMapMutex);
return result;
}
- 读写锁(Read - Write Lock):如果读操作频繁,写操作较少,可以使用读写锁。多个线程可以同时进行读操作,但写操作时需要独占锁。
#include <pthread.h>
pthread_rwlock_t memoryMapRwlock = PTHREAD_RWLOCK_INITIALIZER;
// 线程安全的获取设备状态字节(读操作)
uint8_t getDeviceStatusByteThreadSafeRead(const union MemoryMap *mmap) {
pthread_rwlock_rdlock(&memoryMapRwlock);
uint8_t result = mmap->deviceStatus.statusByte;
pthread_rwlock_unlock(&memoryMapRwlock);
return result;
}
// 线程安全的设置设备状态字节(写操作)
void setDeviceStatusByteThreadSafe(union MemoryMap *mmap, uint8_t statusByte) {
pthread_rwlock_wrlock(&memoryMapRwlock);
mmap->deviceStatus.statusByte = statusByte;
pthread_rwlock_unlock(&memoryMapRwlock);
}
- 原子操作:对于简单的成员(如整数类型),可以使用原子操作。C11标准引入了
<stdatomic.h>
头文件,提供了原子类型和操作函数,可避免数据竞争问题。例如:
#include <stdatomic.h>
atomic_uint8_t atomicDeviceStatusByte;
// 线程安全的获取原子设备状态字节
uint8_t getAtomicDeviceStatusByte() {
return atomic_load(&atomicDeviceStatusByte);
}
// 线程安全的设置原子设备状态字节
void setAtomicDeviceStatusByte(uint8_t statusByte) {
atomic_store(&atomicDeviceStatusByte, statusByte);
}