【问题标题】:TSL instruction referenceTSL 指令参考
【发布时间】:2014-04-20 21:04:34
【问题描述】:

我想在汇编中使用“TSL”指令,但它没有参考理解。在某些文章中,该指令是针对互斥问题引入的,但没有参考或完整的示例来完全理解。

【问题讨论】:

  • 它适用于什么平台?这种功能通常有高级包装器。在 Windows 中,有一堆 InterlockedXXXX 操作。在 GCC 中,有内置函数。除非必要,否则不要重新发明同步原语。

标签: assembly masm


【解决方案1】:

TSL(Test and Set Lock)是在处理一般互斥问题时经常出现的操作,但这并不意味着这样的指令实际上存在于您使用的任何架构上;或者,即使它确实存在,它也被称为 TSL。

例如,在 x86 上,您可以使用 XCHG 指令来执行 TSL。

【讨论】:

  • 我如何使用 XCHG 来做到这一点?我想要这项工作的任何说明
  • 你知道TSL是做什么的吗?它只是简单地设置锁(通常为 1)并返回之前的值。这是原子交换。所以你需要做的就是:MOV EAX, 1; XCHG EAX, [lock]
  • 你能给我一个使用这些指令解决互斥问题的示例代码吗?我想要一个使用它的汇编或 C 代码
【解决方案2】:

XCNG 不是有条件的,它只是用一个内存位置交换一个寄存器。您想要的是带有 LOCK 前缀的 CMPXCHG。后者将使其在多核机器上具有原子性。

此外,您可以使用 LOCK XADD 实现原子比较和设置,但这需要一个循环。

参考:http://en.wikipedia.org/wiki/Compare-and-swap

【讨论】:

  • 你能给我一个使用这些指令解决互斥问题的示例代码吗?我想要一个使用它的汇编或 C 代码
  • 或者我没有说错,因为我没有说它是有条件的。您自动将锁设置为 1,然后检查先前的值是否为 0,在这种情况下您获得了锁,或者如果它已经为 1,在这种情况下它没有被更改并且您没有锁。另外,我说for example,所以还有其他方法可以做到这一点,但除非你能证明,否则我的方法并没有错。另请参阅wikipedia
  • @Jester:如果 testset 不是同一个命令,你就会面临竞争条件。想象一下:你交换,你看到之前的值是 1。现在你把它设置回零......并杀死同时由另一个线程引发的合法锁。上下文切换可能出现在汇编命令之间的任何边界上。包括设置和测试之间的一个。这就是 CMPXGHG 的意义所在。
  • 嗯?如果之前的值为 1,则您没有获得锁,因此您什么也不做(重复直到获得锁)。如果你得到了锁,它现在是 1,没有其他人会得到它,当你完成关键部分时,你可以将它设置为 0。没有竞争条件。
  • 点了。确实没有种族。不过,为什么要解耦测试和设置以及两者都存在的 CPU 级原语?
【解决方案3】:

我认为@Jester 和@Seva Alekseyev 已经在回答这个问题方面做得很好,但这里是一个简单的 C 语言实现,通过在 x86 Ubuntu 机器上使用 pthreads 实现。

在以下示例中,有两个长时间运行的线程。两个线程都有一个临界区和一个非临界区,分别是critical_region() 和noncritical_region。他们都必须调用 enter_region() 才能获得锁。一旦线程获得锁,它就可以开始运行它的临界区。另一个线程被阻塞,直到有锁的线程调用leave_region()

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


void enter_region() {
    asm(
        ".data\n\t"
        "lock:\n\t"
        ".byte 0\n\t"
        ".text\n\t"

        "_enter_region:\n\t"
        "movb $1, %al\n\t" /* move 1 to AL */
        "xchgb (lock),%al\n\t" 
        "cmp $0, %al\n\t"
        "jne _enter_region\n\t"

    );
}

void leave_region() {
    asm("movb $0, (lock)");
}


void critical_region() {

}

void noncritical_region() {

}

static void* f1(void* p) {
    while(1) {
        puts("wait for f2");
        enter_region();
        printf("f1 can start its critical section\n");
        critical_region();
        leave_region();
        noncritical_region();
    }
    return NULL;
}

static void* f2(void* p) {
    while(1) {
        puts("wait for f1");
        enter_region();
        printf("f2 can start its critical section\n");
        critical_region();
        leave_region();

        /* if you call sleep, you can see that the non-critical section of this thread won't
        *  block the other thread from running its critical section as many times as it wants
        */
        // sleep(1);
        noncritical_region();
    }
    return NULL;
}

int main() {
    int rc;

    pthread_t t1, t2;

    rc = pthread_create(&t1, NULL, f1, NULL);
    if(rc != 0) {
        fprintf(stderr, "pthread f1 failed\n");
        return EXIT_FAILURE;
    }

    rc = pthread_create(&t2, NULL, f2, NULL);
    if(rc != 0) {
        fprintf(stderr, "pthread f2 failed\n");
        return EXIT_FAILURE;
    }

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    puts("All threads finished.");
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-09
    • 2012-04-26
    • 2016-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-03
    相关资源
    最近更新 更多