【发布时间】:2019-05-19 12:24:45
【问题描述】:
我试图通过创建一个简单的上下文切换函数和一个 FCFS 调度程序来在 C 中实现自定义线程。
我要执行的第一步是将整个函数堆栈帧复制到堆到已保存的帧中,并将其替换为队列中的第一个。
我遇到的问题是,在完成第一个任务后,第二个的堆栈被破坏了。我不知道为什么。
我的代码如下:
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define ITERATIONS 10
#define SSIZE 15
int * last;
void kwrite(const char*);
void kyield(int *);
void f1() {
int i = ITERATIONS;
while (i--) kwrite("A\n");
}
void f2() {
int i = ITERATIONS*2;
while (i--) {
printf("[%d]", i);
kwrite("B\n");
getchar();
}
}
void kwrite(const char* str) {
int a[10] = {5, 5, 5, 5, 5, 5, 5, 5, 5, 5};
write(1, str, strlen(str));
int *frame = malloc(sizeof(int)*SSIZE);
memcpy(frame, a, SSIZE*sizeof(int));
kyield(frame);
printf("ERROR\n");
}
void kyield(int * from) {
if (from == NULL) {
f1();
from = malloc(sizeof(int)*SSIZE);
memcpy(from, last, SSIZE*sizeof(int));
}
if (last == NULL) {
last = malloc(sizeof(int)*SSIZE);
memcpy(last, from, SSIZE*sizeof(int));
free(from);
f2();
exit(0);
}
int a[10] = {3, 3, 3, 3, 3, 3, 3, 3, 3, 3};
memcpy(a, last, SSIZE*sizeof(int));
memcpy(last, from, SSIZE*sizeof(int));
free(from);
}
int main(int argc, char** argv) {
kyield(NULL);
free(last);
}
它应该调用 10 次 f1 和 20 次 f2 然后退出。但是当 f2 的 var i 为 8 时,它会在下一次迭代中被破坏。因此进入了一个无限循环。
任何帮助或建议将不胜感激!祝你有美好的一天。
[编辑] 我想代码可能有点难以理解,所以在这里稍微澄清一下:
main 调用带有 null 参数的 kyield。
kyield 检测到它并调用 f1
f1 一直执行到 kwrite 被调用
kwrite 调用 kyield 传递其当前堆栈帧
kyield 检测到最后一个堆栈帧为空,因此它复制 kwrite 给出的堆栈帧(从现在开始为 sf),然后调用 f2 f2 和 f1 一样
当接下来执行 kyield 时,from 和 last 都不会为 NULL,因此它将用 last 覆盖其当前 sf,将其与 from 交换,最后它将返回,因为堆栈已被更改,它将跳转到最后一个 kwrite 的返回地址,而不是实际的,因此。从 f1 线程跳转到 f2。
你的 memcpy(frame, a, SSIZE*sizeof(int)) 看起来不对。您的 SSIZE 定义为 15,但 a 的大小仅为 10。
这是故意的,因为通过复制 4 个字节的 15 个元素,我们复制了 rax 的最后一个值、最后一个 ebp 和函数的返回地址。
https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/
【问题讨论】:
-
当
memcpy(from, last, SSIZE*sizeof(int));被执行时,last == NULL。你希望这个memcpy()会发生什么? -
@chux 这似乎很令人困惑,但我认为
f1调用kwrite调用kyield和from != NULL,因此在第一个kyield调用到达这一行之前,@987654332 @ 将通过另一个kyield调用分配给memcpyed。仍然没有说明意图。 -
@user10605163 同意。
-
@chux 你必须明白这里有两个并发线程。尽管代码看起来很简单,但它确实很棘手。回答您的问题,最后执行提到的 memcpy 时不会为空,因为另一个 kyield 调用先前已分配它。
-
Tretorn,这里只有一个thread。 @user10605163 已经解释了代码。
标签: c multithreading stack stack-frame