lab6 multithread
调度
实验结果

实验
switching between threads
代码
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
/* Possible states of a thread: */
#define FREE 0x0
#define RUNNING 0x1
#define RUNNABLE 0x2
#define STACK_SIZE 8192
#define MAX_THREAD 4
struct ThreadFrame {
uint64 ra;
uint64 sp;
uint64 s0;
uint64 s1;
uint64 s2;
uint64 s3;
uint64 s4;
uint64 s5;
uint64 s6;
uint64 s7;
uint64 s8;
uint64 s9;
uint64 s10;
uint64 s11;
};
struct thread {
struct ThreadFrame ThreadFrameS;
char stack[STACK_SIZE]; /* the thread's stack */
int state; /* FREE, RUNNING, RUNNABLE */
};
struct thread all_thread[MAX_THREAD];
struct thread *current_thread;
extern void thread_switch(uint64, uint64);//save restore
void
thread_init(void)
{
// main() is thread 0, which will make the first invocation to
// thread_schedule(). it needs a stack so that the first thread_switch() can
// save thread 0's state. thread_schedule() won't run the main thread ever
// again, because its state is set to RUNNING, and thread_schedule() selects
// a RUNNABLE thread.
current_thread = &all_thread[0];
current_thread->state = RUNNING;
}
void
thread_schedule(void)
{
struct thread *t, *next_thread;
/* Find another runnable thread. */
next_thread = 0;
t = current_thread + 1;
for(int i = 0; i < MAX_THREAD; i++){
if(t >= all_thread + MAX_THREAD)
t = all_thread;
if(t->state == RUNNABLE) {
next_thread = t;
break;
}
t = t + 1;
}
if (next_thread == 0) {
printf("thread_schedule: no runnable threads\n");
exit(-1);
}
if (current_thread != next_thread) { /* switch threads? */
next_thread->state = RUNNING;
t = current_thread;
current_thread = next_thread;
/* YOUR CODE HERE
* Invoke thread_switch to switch from t to next_thread:
* thread_switch(??, ??);
*/
uint64 Save;
uint64 Restore;
Save = (uint64)&(t->ThreadFrameS);
Restore = (uint64)&(next_thread->ThreadFrameS);
//printf("1:%d 2:%d\n",Save,Restore);
thread_switch(Save,Restore);
}
else
{
next_thread = 0;
}
}
void
thread_create(void (*func)())
{
struct thread *t;
for (t = all_thread; t < all_thread + MAX_THREAD; t++) {
if (t->state == FREE) break;
}
t->state = RUNNABLE;
// YOUR CODE HERE
t->ThreadFrameS.ra = (uint64)func;
t->ThreadFrameS.sp = (uint64)(&(t->stack[STACK_SIZE - 1]));
//t->ThreadFrameS.s0 = (uint64)(&(t->stack[STACK_SIZE - 1]));
}
void
thread_yield(void)
{
current_thread->state = RUNNABLE;
thread_schedule();
}
volatile int a_started, b_started, c_started;
volatile int a_n, b_n, c_n;
void
thread_a(void)
{
int i;
printf("thread_a started\n");
a_started = 1;
while(b_started == 0 || c_started == 0)
thread_yield();
for (i = 0; i < 100; i++) {
printf("thread_a %d\n", i);
a_n += 1;
thread_yield();
}
printf("thread_a: exit after %d\n", a_n);
current_thread->state = FREE;
thread_schedule();
}
void
thread_b(void)
{
int i;
printf("thread_b started\n");
b_started = 1;
while(a_started == 0 || c_started == 0)
thread_yield();
for (i = 0; i < 100; i++) {
printf("thread_b %d\n", i);
b_n += 1;
thread_yield();
}
printf("thread_b: exit after %d\n", b_n);
current_thread->state = FREE;
thread_schedule();
}
void
thread_c(void)
{
int i;
printf("thread_c started\n");
c_started = 1;
while(a_started == 0 || b_started == 0)
thread_yield();
for (i = 0; i < 100; i++) {
printf("thread_c %d\n", i);
c_n += 1;
thread_yield();
}
printf("thread_c: exit after %d\n", c_n);
current_thread->state = FREE;
thread_schedule();
}
int
main(int argc, char *argv[])
{
a_started = b_started = c_started = 0;
a_n = b_n = c_n = 0;
thread_init();
thread_create(thread_a);
thread_create(thread_b);
thread_create(thread_c);
thread_schedule();
exit(0);
}
出现的问题
struct thread {
struct ThreadFrame ThreadFrameS;
char stack[STACK_SIZE]; /* the thread's stack */
int state; /* FREE, RUNNING, RUNNABLE */
};
struct thread all_thread[MAX_THREAD];
struct thread *current_thread;
extern void thread_switch(uint64, uint64);//save restore
void
thread_create(void (*func)())
{
struct thread *t;
for (t = all_thread; t < all_thread + MAX_THREAD; t++) {
if (t->state == FREE) break;
}
t->state = RUNNABLE;
// YOUR CODE HERE
t->ThreadFrameS.ra = (uint64)func;
//t->ThreadFrameS.sp = (uint64)(&(t->stack[STACK_SIZE - 1]));
t->ThreadFrameS.sp = (uint64)(&(t->stack[0]));
}
在thread_create的创建中,用线程的数组下标0的地址来作为栈sp的地址,甚至fp赋值为(uint64)(&(t->stack[STACK_SIZE - 1]))。
然后导致如下图的结果

出现上图的原因是struct thread all_thread[MAX_THREAD],线程结构体数组导致每个线程结构体的地址前后相接;栈帧时向下生长的,帧生长的地方不在stack的范围,在stack前面;thread_frame的大小不够大,导致栈用的地址覆盖到前一个线程结构体,另一个线程结构体最后一个时state,导致其被修改,导致前一个线程的状态就不是RUNABLE。
正确的做法应该是
void
thread_create(void (*func)())
{
struct thread *t;
for (t = all_thread; t < all_thread + MAX_THREAD; t++) {
if (t->state == FREE) break;
}
t->state = RUNNABLE;
// YOUR CODE HERE
t->ThreadFrameS.ra = (uint64)func;
t->ThreadFrameS.sp = (uint64)(&(t->stack[STACK_SIZE - 1]));
//t->ThreadFrameS.sp = (uint64)(&(t->stack[0]));
}
因为RISCV的栈帧是向下的,每个的栈帧的头部是ra,下一个fp,最下是fp。不是一个栈的最下时sp,最上时ra,随时会变的。
using thread
要回答的问题:Why are there missing keys with 2 threads, but not with 1 thread?
因为无论有多少个线程,它们都会对struct entry *table[NBUCKET],这个变量进行修改。一个线程时,某个bucket修改后,因为只有这个线程,所以就看到了修改后的bucket。而多线程时,比如两个线程,一个线程刚好执玩insert,把一个(key value)放在第二个bucket后面,另一个线程也是第二个bucket,另一个线程的key第二个bucket也没有且执行到if(e)那边,它会执行insert把这个key放在另一个线程key的位置。
答案
仔细观察代码,该程序本质是在5个单项链表中不断添加满足一定条件的元素。创造了pthread_mutex_t lock_arr[NBUCKET],即NBUCKET互斥锁。再put()函数里,根据key % NBUCKET得到余数,获取相应的互斥锁,put最后返回时,解锁。用pthread_join将每个线程都回收后,才会执行get_thread()函数,所以get()这边是不需要加互斥锁的。记得main()调用pthread_create()前初始化所有锁。
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <assert.h>
#include <pthread.h>
#include <sys/time.h>
#define NBUCKET 5
#define NKEYS 100000
struct entry {
int key;
int value;
struct entry *next;
};
struct entry *table[NBUCKET];
int keys[NKEYS];
int nthread = 1;
pthread_mutex_t lock_arr[NBUCKET] = {0};
double
now()
{
struct timeval tv;
gettimeofday(&tv, 0);
return tv.tv_sec + tv.tv_usec / 1000000.0;
}
static void
insert(int key, int value, struct entry **p, struct entry *n)
{
struct entry *e = malloc(sizeof(struct entry));
e->key = key;
e->value = value;
e->next = n;
*p = e;
}
static
void put(int key, int value)
{
int i = key % NBUCKET;
pthread_mutex_lock(&lock_arr[i]);
// is the key already present?
struct entry *e = 0;
for (e = table[i]; e != 0; e = e->next) {
if (e->key == key)
break;
}
if(e){
// update the existing key.
e->value = value;
} else {
// the new is new.
insert(key, value, &table[i], table[i]);
}
pthread_mutex_unlock(&lock_arr[i]);
}
static struct entry*
get(int key)
{
int i = key % NBUCKET;
//pthread_mutex_lock(&lock_arr[i]);
struct entry *e = 0;
for (e = table[i]; e != 0; e = e->next) {
if (e->key == key) break;
}
return e;
}
static void *
put_thread(void *xa)
{
int n = (int) (long) xa; // thread number
int b = NKEYS/nthread;
for (int i = 0; i < b; i++) {
put(keys[b*n + i], n);
}
return NULL;
}
static void *
get_thread(void *xa)
{
int n = (int) (long) xa; // thread number
int missing = 0;
for (int i = 0; i < NKEYS; i++) {
struct entry *e = get(keys[i]);
if (e == 0) missing++;
}
printf("%d: %d keys missing\n", n, missing);
return NULL;
}
int
main(int argc, char *argv[])
{
pthread_t *tha;
void *value;
double t1, t0;
for(int i = 0; i < NBUCKET; i++)
{
pthread_mutex_init(&lock_arr[i], NULL);
}
if (argc < 2) {
fprintf(stderr, "Usage: %s nthreads\n", argv[0]);
exit(-1);
}
nthread = atoi(argv[1]);
tha = malloc(sizeof(pthread_t) * nthread);
srandom(0);
assert(NKEYS % nthread == 0);
for (int i = 0; i < NKEYS; i++) {
keys[i] = random();
}
//
// first the puts
//
t0 = now();
for(int i = 0; i < nthread; i++) {
assert(pthread_create(&tha[i], NULL, put_thread, (void *) (long) i) == 0);
}
for(int i = 0; i < nthread; i++) {
assert(pthread_join(tha[i], &value) == 0);
}
t1 = now();
printf("%d puts, %.3f seconds, %.0f puts/second\n",
NKEYS, t1 - t0, NKEYS / (t1 - t0));
//
// now the gets
//
t0 = now();
for(int i = 0; i < nthread; i++) {
assert(pthread_create(&tha[i], NULL, get_thread, (void *) (long) i) == 0);
}
for(int i = 0; i < nthread; i++) {
assert(pthread_join(tha[i], &value) == 0);
}
t1 = now();
printf("%d gets, %.3f seconds, %.0f gets/second\n",
NKEYS*nthread, t1 - t0, (NKEYS*nthread) / (t1 - t0));
}
Barrier
static void
barrier()
{
// YOUR CODE HERE
//
// Block until all threads have called barrier() and
// then increment bstate.round.
//
pthread_mutex_lock(&bstate.barrier_mutex);
bstate.nthread = bstate.nthread + 1;
if (bstate.nthread < nthread)
{
pthread_cond_wait(&bstate.barrier_cond, &bstate.barrier_mutex);
}
else
{
bstate.nthread = 0;
round = round + 1;
bstate.round = round;
pthread_cond_broadcast(&bstate.barrier_cond);
}
pthread_mutex_unlock(&bstate.barrier_mutex);
}
想法
忽然感觉有点来不及,也有点悲伤。走到今天这个地步,我是没有想到。。。。
10个实验做完后,会出几篇文章介绍xv6的进程管理,进程交互,文件系统,内存管理等知识,相关知识暂时不更了。

浙公网安备 33010602011771号