[英]Atomic test-and-set for LPC1788 microcontroller
我正在使用NXP LPC1788微控制器,并且正在用C开发一个多线程应用程序。在我的应用程序的一部分中,我定义了一个自定义的链表数据结构。 由于并发访问特定列表,我以前在程序上遇到问题,我似乎通过为线程可以在访问列表本身之前调用的列表实现“锁定获取”方法和“锁定释放”方法而解决了这个问题。
我通过在列表结构中添加“ sema”数据成员来做到这一点:
typedef struct linked_list
{
list_node_t *head;
list_node_t *tail;
uint32_t len;
NODE_ITEM_TYPE_T itemType;
uint32_t itemSize;
uint8_t sema;
} linked_list_t;
我的“锁定获取”方法如下:
void LIST_AcquireLock(linked_list_t *list)
{
while(list->sema);
list->sema = 1;
}
我的“锁定释放”方法如下:
void LIST_ReleaseLock(linked_list_t *list)
{
list->sema = 0;
}
通常,这似乎行得通,因为我的应用程序每秒都要向这样的列表中添加和删除项目几千次,而且此后我还没有发现与并发访问相关的任何错误。
但是,为了更确信此方法的有效性,我想知道是否有任何方法可以实现测试设置方法。 LPC1788依赖于Cortex-M3微控制器特有的Thumb指令集版本,可以在此处或在918+页的用户手册中找到。
纵观它,我找不到像测试设置指令之类的东西。 我可能只是忽略了它。
理想情况下,我想要这样的东西:
void LIST_AcquireLock(linked_list_t *list)
{
do{
while(list->sema);
} while(TestAndSet(list->sema));
}
编辑
根据Nemo的回答,我尝试了以下操作:
void LIST_AcquireLock(linked_list_t *list)
{
// Wait until lock seems free.
while(list->sema);
// Make sure lock is actually free.
do {
// If the semaphore is locked, we continue.
// OTHERWISE we try to lock it ourselves.
if(__LDREXB(&(list->sema))) continue;
// If __STREXB returns 1, then another thread might have accessed that
// memory location and we can't be sure the lock operation is atomic,
// so try the locking procedure again.
} while(__STREXB(1, &(list->sema)));
}
如果有帮助,这是相应的汇编代码:
LIST_AcquireLock:
??LIST_AcquireLock_0:
0x56de: 0x7d01 LDRB R1, [R0, #0x14]
0x56e0: 0x2900 CMP R1, #0
0x56e2: 0xd1fc BNE.N ??LIST_AcquireLock_0 ; 0x56de
??LIST_AcquireLock_1:
0x56e4: 0xf110 0x0114 ADDS.W R1, R0, #20 ; 0x14
0x56e8: 0xe8d1 0x1f4f LDREXB R1, [R1]
0x56ec: 0xb2c9 UXTB R1, R1
0x56ee: 0x2900 CMP R1, #0
??LIST_AcquireLock_2:
0x56f0: 0xf110 0x0114 ADDS.W R1, R0, #20 ; 0x14
0x56f4: 0x2201 MOVS R2, #1
0x56f6: 0xe8c1 0x2f43 STREXB R3, R2, [R1]
0x56fa: 0x2b00 CMP R3, #0
0x56fc: 0xd1f2 BNE.N ??LIST_AcquireLock_1 ; 0x56e4
0x56fe: 0x4770 BX LR
我在重现并发访问问题时遇到了麻烦(假设这是我遇到的并发问题),所以我不确定这是否可行。
ARM对原子操作使用“加载链接/存储专有”范例。 有关详细信息, 请参见此 问题和您链接的用户手册的39.2.4.8节 。
[更新]
根据提供的链接 @HansPassant中的代码 ,我建议对您的例程进行一些细微更改:
void LIST_AcquireLock(linked_list_t *list)
{
// Wait until lock seems free.
//while(list->sema); // unnecessary
// Make sure lock is actually free.
do {
// If the semaphore is locked, we continue.
// OTHERWISE we try to lock it ourselves.
if(__LDREXB(&(list->sema))) continue;
// If __STREXB returns 1, then another thread might have accessed that
// memory location and we can't be sure the lock operation is atomic,
// so try the locking procedure again.
} while(__STREXB(1, &(list->sema)));
// Ensure CPU does not reorder any memory accesses across lock acquisition.
__DMB();
}
__DMB()
在非常简单的ARM内核上可能无关紧要,但是在更复杂的内核上肯定是必需的。 现代CPU具有复杂的内存模型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.