C上下文切换堆栈变量损坏
C Context switch stack variable corruption
我正在尝试通过创建一个简单的上下文切换函数和一个 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 使用空参数调用 kyield。
kyield 检测到它并调用 f1
f1 执行直到 kwrite 被调用
kwrite 调用 kyield 并传递其当前堆栈帧
kyield 检测到最后一个堆栈帧为空,因此它复制 kwrite 给出的堆栈帧(从现在开始为 sf),然后调用 f2
f2 和 f1
一样
当 kyield 下次执行时,from 和 last 都不会为 NULL,因此它将用 last 中的那个覆盖它当前的 s f,将它与 from 中的那个交换,最后它将 return,作为堆栈已被更改,它将跳转到最后一个 kwrite 的 return 地址,而不是实际地址,因此。从f1线程跳转到f2.
Your memcpy(frame, a, SSIZE*sizeof(int)) looks wrong. Your SSIZE is defined to 15, but a has only a size of 10.
这是故意的,因为通过复制 4 个字节的 15 个元素,我们复制了 rax 最后一个值、最后一个 ebp 和函数的 return 地址。
https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/
您的 memcpy(frame, a, SSIZE*sizeof(int))
看起来不对。您的 SSIZE
定义为 15,但 a
的大小仅为 10。
我发现设计存在一些问题。调度不同线程的正常方式是让它们获得完整的堆栈,而不是共享同一个堆栈。
在这种情况下,这意味着堆栈取决于所使用的地址。
+---------+--------+---------+--------+---------+
| main | kyield | f1 | kwrite | kyield |
| | | |a[10] | |
+---------+--------+---------+--------+---------+
|-------------| << copied by the slice of the stack.
您正在获取的堆栈切片独立于它前面的函数中使用的数量,因此如果调用 kwrite 的函数具有不同的堆栈要求(需要不同数量的状态),则会被破坏。
也是因为栈上抓取的信息量不完整,所以也坏了。执行状态基于堆栈和非易失性寄存器中的当前值。这些值会污染备用线程。
最后堆栈也包含地址。只有当所有执行的线程具有相同的堆栈要求时,该方案才有效,就好像 yield 函数将需要地址的值粘贴回去,那么它们总是必须对齐。
我正在尝试通过创建一个简单的上下文切换函数和一个 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 使用空参数调用 kyield。
kyield 检测到它并调用 f1
f1 执行直到 kwrite 被调用
kwrite 调用 kyield 并传递其当前堆栈帧
kyield 检测到最后一个堆栈帧为空,因此它复制 kwrite 给出的堆栈帧(从现在开始为 sf),然后调用 f2 f2 和 f1
一样当 kyield 下次执行时,from 和 last 都不会为 NULL,因此它将用 last 中的那个覆盖它当前的 s f,将它与 from 中的那个交换,最后它将 return,作为堆栈已被更改,它将跳转到最后一个 kwrite 的 return 地址,而不是实际地址,因此。从f1线程跳转到f2.
Your memcpy(frame, a, SSIZE*sizeof(int)) looks wrong. Your SSIZE is defined to 15, but a has only a size of 10.
这是故意的,因为通过复制 4 个字节的 15 个元素,我们复制了 rax 最后一个值、最后一个 ebp 和函数的 return 地址。
https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/
您的 memcpy(frame, a, SSIZE*sizeof(int))
看起来不对。您的 SSIZE
定义为 15,但 a
的大小仅为 10。
我发现设计存在一些问题。调度不同线程的正常方式是让它们获得完整的堆栈,而不是共享同一个堆栈。
在这种情况下,这意味着堆栈取决于所使用的地址。
+---------+--------+---------+--------+---------+
| main | kyield | f1 | kwrite | kyield |
| | | |a[10] | |
+---------+--------+---------+--------+---------+
|-------------| << copied by the slice of the stack.
您正在获取的堆栈切片独立于它前面的函数中使用的数量,因此如果调用 kwrite 的函数具有不同的堆栈要求(需要不同数量的状态),则会被破坏。
也是因为栈上抓取的信息量不完整,所以也坏了。执行状态基于堆栈和非易失性寄存器中的当前值。这些值会污染备用线程。
最后堆栈也包含地址。只有当所有执行的线程具有相同的堆栈要求时,该方案才有效,就好像 yield 函数将需要地址的值粘贴回去,那么它们总是必须对齐。