【问题标题】:How do I write to a different thread's stack in C/C++?如何在 C/C++ 中写入不同线程的堆栈?
【发布时间】:2010-12-11 10:52:51
【问题描述】:

我知道这是个坏主意!当然,为了安全编程,给定线程的堆栈应该被认为是该线程私有的。但是 POSIX 至少保证了一个线程的所有内存都可以被其他线程共享和写入,这意味着一个线程的堆栈(理论上)可以被另一个线程写入。所以我很好奇如何做到这一点。

我能想到的唯一方法是将线程 B 中的局部变量的地址传递给线程 A,并让 A 开始写入该一般区域。但这并不能完全模拟 C 中的函数调用。特别是我很好奇是否可以说,让线程 A 在其程序计数器设置为某个接受参数的函数的第一行时休眠,然后在线程 B 上实际上将这些参数压入堆栈,然后恢复线程 A 并让它执行,就好像该函数最初是在线程 A 中使用这些参数调用的一样。我认为这要求线程 B 至少能够在 x86 调用约定下访问线程 A 的寄存器,我不确定这是否可能。

【问题讨论】:

  • 您的问题是关于写入不同线程的堆栈(简单)还是如何控制不同线程的执行流程(困难)?
  • 我并没有试图改变线程 A 中的执行流程,除非它认为它最初使用 B 提供的参数调用了函数。例如,我没有考虑,如何让 B 改变线程 A 循环或分支的方式。
  • 也许更好的方法是让 A 调用函数而不是 B? B 仍然可以使用任何你喜欢的线程间通信机制向 A 提供参数。

标签: c multithreading assembly stack shared-memory


【解决方案1】:

好的,访问线程堆栈的一种方法是自己分配堆栈内存并在创建线程时分配它。这可以通过pthread_attr_setstack() 调用来实现。

以下代码将线程堆栈设置为手动分配的内存(此处为 malloc),而不是系统分配它。稍后您可以在创建线程后访问 mystack 指针。此类代码的一个用例是,您可以转储线程堆栈以获取其快照,然后您可以恢复该线程。

代码:

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

   void *thread(void *arg) {
      char *ret;
      printf("thread() entered with argument '%s'\n", (char *)arg);
      if ((ret = (char*) malloc(20)) == NULL) {
        perror("malloc() error");
        exit(2);
      }
      strcpy(ret, "This is a test");
      pthread_exit(ret);
    }

    int main(void)
    {
       pthread_attr_t attr;
       int              rc;

      pthread_t thid;
      void *ret;

       void  *mystack;
       size_t mystacksize = 2 * PTHREAD_STACK_MIN;

       if (pthread_attr_init(&attr) == -1) {
          exit(1);
       }

       /* Get a big enough stack and align it on 4K boundary. */
       mystack = malloc(PTHREAD_STACK_MIN * 3);
       if (mystack != NULL) {
          printf("Using PTHREAD_STACK_MIN to align stackaddr %x.\n", mystack);
          mystack = (void *)((((long)mystack + (PTHREAD_STACK_MIN - 1)) /
                              PTHREAD_STACK_MIN) * PTHREAD_STACK_MIN);
       } else {
          exit(2);
       }

       printf("Setting stackaddr to %x\n", mystack);
       printf("Setting stacksize to %x\n", mystacksize);
       rc = pthread_attr_setstack(&attr, mystack, mystacksize);
       if (rc != 0) {
          printf("pthread_attr_setstack returned: %d\n", rc);
          exit(3);
       } else {
          printf("Set stackaddr to %x\n", mystack);
          printf("Set stacksize to %x\n", mystacksize);
       }


      if (pthread_create(&thid, &attr, thread, "thread 1") != 0) {
        exit(1);
      }

      if (pthread_join(thid, &ret) != 0) {
        exit(3);
      }

      printf("thread exited with '%s'\n", ret);
       rc = pthread_attr_destroy(&attr);
       if (rc != 0) {
          exit(5);
       }

       exit(0);
    }

如果这是您想要的,请告诉我们。抱歉,缩进和编码风格不好。

【讨论】:

  • Doh,我忘记了在创建另一个线程时,您通常会确定堆栈使用的空间。这使事情变得更简单。我仍然很好奇,但是否可以操纵用于函数调用的堆栈,AFAIK 需要从另一个线程更改一个线程的寄存器值。
【解决方案2】:

将指向线程A中函数foo()中的volatile局部变量的指针传递给线程B中的函数bar(),然后让线程B修改其内容可能会起作用。

但是面对优化和线程内存缓存,没有办法保证这会奏效。

所以,是的,这可能是个坏主意。你为什么要这样做? 信号量互斥体会是更好的解决方案吗?

【讨论】:

  • 什么是“线程内存缓存”,为什么优化会破坏 volatile? volatile 的重点是不要被优化破坏。
  • 我了解到,在多个 CPU(或多核 CPU)上运行的多个线程具有不同的 L1 内存缓存,这可能会干扰 volatile 语义。
【解决方案3】:

看起来您正在尝试实现自己的(快速)用户空间互斥锁,那么为什么不简单地使用futex 进行探索呢?他们can be tricky,但至少你(作为最后的手段)有内核来仲裁比赛。

Loadmaster 建议的内容可以想象,有两个线程。超过两个,它会变得非常非常恶心。此外,我赞同他所说的 volatile 并不总是 volatile。

【讨论】:

    猜你喜欢
    • 2010-10-21
    • 1970-01-01
    • 1970-01-01
    • 2015-09-08
    • 2021-03-17
    • 1970-01-01
    • 2012-03-30
    • 2010-10-19
    • 2016-05-09
    相关资源
    最近更新 更多