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的进程管理,进程交互,文件系统,内存管理等知识,相关知识暂时不更了

posted @ 2025-01-22 13:07  我们的歌谣  阅读(16)  评论(0)    收藏  举报