作业地址
下载两个文件到 xv6 文件夹并在 Makefile 在 _forktest
后面加入下面的代码
1
2
3
|
_uthread: uthread.o uthread_switch.o
$(LD) $(LDFLAGS) -N -e main -Ttext 0 -o _uthread uthread.o uthread_switch.o $(ULIB)
$(OBJDUMP) -S _uthread > uthread.asm
|
并在 UPROGS
后面加入 _uthread
在重新编译后会遇到 panic
1
2
3
|
$ uthread
lapicid 1: panic: remap
80106967 80105911 801056fc 0 0 0 0 0 0 0
|
整个作业是要实现 uthread_switch.S
即线程切换的过程:
The job of uthread_switch is to save the current thread state into the structure pointed to by current_thread, restore next_thread’s state, and make current_thread point to where next_thread was pointing to, so that when uthread_switch returns next_thread is running and is the current_thread.
线程结构如下:
1
2
3
4
5
|
struct thread {
int sp; /* saved stack pointer */
char stack[STACK_SIZE]; /* the thread's stack */
int state; /* FREE, RUNNING, RUNNABLE */
};
|
关于具体的压栈可以参考 thread_create()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
void
thread_create(void (*func)())
{
thread_p t;
for (t = all_thread; t < all_thread + MAX_THREAD; t++) {
if (t->state == FREE) break;
}
t->sp = (int) (t->stack + STACK_SIZE); // set sp to the top of the stack
t->sp -= 4; // space for return address
* (int *) (t->sp) = (int)func; // push return address on stack
t->sp -= 32; // space for registers that thread_switch expects
t->state = RUNNABLE;
}
|
上面的 32 byte
用于 pushal
和 popal
,再上面就是线程对应的函数
struct thread
的内存分布
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
--------------------
| 4 bytes for state|
--------------------
| stack size bytes |
| for stack |
--------------------
| 4 bytes for sp |
-------------------- <--- current_thread
......
......
--------------------
| 4 bytes for state|
--------------------
| stack size bytes |
| for stack |
--------------------
| 4 bytes for sp |
-------------------- <--- next_thread
|
整个 uthread_switch()
实现如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
thread_switch:
/* YOUR CODE HERE */
/* save the current thread state into the structure pointed to by current_thread */
pushal
movl current_thread, %eax
movl %esp, (%eax)
/* restore next_thread's state */
movl next_thread, %eax
/* make current_thread point to where next_thread was pointing to */
movl %eax, current_thread
movl (%eax), %esp
/* clear next_thread */
movl $0x0, next_thread
/* pop return address from stack */
popal
ret
|
最后补充一个线程切换的过程 thread_schedule()
,整个过程就是找状态为 RUNNABLE
的线程并对齐进行切换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
static void
thread_schedule(void)
{
thread_p t;
/* Find another runnable thread. */
next_thread = 0;
for (t = all_thread; t < all_thread + MAX_THREAD; t++) {
if (t->state == RUNNABLE && t != current_thread) {
next_thread = t;
break;
}
}
if (t >= all_thread + MAX_THREAD && current_thread->state == RUNNABLE) {
/* The current thread is the only runnable thread; run it. */
next_thread = current_thread;
}
if (next_thread == 0) {
printf(2, "thread_schedule: no runnable threads\n");
exit();
}
if (current_thread != next_thread) { /* switch threads? */
next_thread->state = RUNNING;
thread_switch();
} else
next_thread = 0;
}
|