Lab:multithreading

写在前面

进程调度:进程由于需要进行磁盘读写或者时钟中断等原因需要让出CPU,此时操作系统会选择可运行的其他的进程上CPU运行。进程调度的过程首先是进程通过系统调用等从用户态进入到内核态,这个过程会将用户态的现场保存到trapframe中,然后通过swtch()进行上下文切换,将进程的上下文保存至KSTACK中,并恢复调度器线程的上下文,之后由调度器线程获取可以运行的其他的线程,再进行一次上下文切换,再恢复该线程的用户态现场,即完成了一次进程调度。

锁与临界区:对于一个具有多核的机器,可以同时运行多个线程。如果多个线程需要对某个共享变量进行读写操作时,由于并发性的存在,可能会导致读写的丢失和出错。为了避免这种问题,需要用锁让某些代码在某个时刻只允许一个线程执行,用锁包围起来的代码称为临界区,其余的线程必须等待临界区的代码执行完后才能运行临界区的代码,从而实现了线程间的互斥。

信号量与同步:信号量由一个计数器和两个操作(P和V)组成,用于生产者和消费者问题,V操作会让计数器值加1,而P操作则会让计数器值减一。为了实现P、V操作,引入了sleep和wakeup。sleep是一个条件锁,对于未满足条件的线程会被阻塞,同时释放该线程互斥锁,而wakeup则是唤醒在某个条件下被阻塞的线程,当线程被唤醒后需要重新获取互斥锁。这种机制确保了线程间同步问题。

实验部分

一 Uthread: switching between threads

该实验主要是为了实现用户线程间的切换,实现用户线程的上下文切换。

1 在user/uthread_switch.S中对旧的上下文进行保存,恢复新的上下文。

	.text

	/*
         * save the old thread's registers,
         * restore the new thread's registers.
         */

	.globl thread_switch
thread_switch:
	/* YOUR CODE HERE */
	sd ra,    0(a0)
	sd sp,    8(a0)
	sd s0,   16(a0)
	sd s1,   24(a0)
	sd s2,   32(a0)
	sd s3,   40(a0)
	sd s4,   48(a0)
	sd s5,   56(a0)
	sd s6,   64(a0)
	sd s7,   72(a0)
	sd s8,   80(a0)
	sd s9,   88(a0)
	sd s10,  96(a0)
	sd s11, 104(a0)

	ld ra,    0(a1)
	ld sp,    8(a1)
	ld s0,   16(a1)
	ld s1,   24(a1)
	ld s2,   32(a1)
	ld s3,   40(a1)
	ld s4,   48(a1)
	ld s5,   56(a1)
	ld s6,   64(a1)
	ld s7,   72(a1)
	ld s8,   80(a1)
	ld s9,   88(a1)
	ld s10,  96(a1)
	ld s11, 104(a1)

	ret    /* return to ra */

2 在user/uthread.c中,在struct thread中添加context的数组,用于存储上下文。并在初始化线程时,将ra初始化为func的函数地址,将sp初始化为线程对应的栈顶指针。

//添加上下文变量
struct thread {
  char       stack[STACK_SIZE]; /* the thread's stack */
  int        state;             /* FREE, RUNNING, RUNNABLE */
  uint64     context[14];
};

//上下文切换
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(??, ??);
     */
    thread_switch((uint64)t->context,(uint64)next_thread->context);
  } 

//线程的初始化
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->context[0]=(uint64)func;
  t->context[1]=(uint64)t->stack+STACK_SIZE;
}

二 Using threads

该实验是为了实现多个线程向哈希表中添加关键字,若对应关键字存在则需要对值进行修改,若关键字不存在则需要添加关键字和值到哈希表中。为了避免两个线程同时对一个关键字值进行修改,需要对每一个entry添加一个锁,从而保证了只有一个线程能够修改entry的值;为了避免两个线程同时添加entry到哈希表中,从而导致其中一个entry丢失,insert操作需要原子化。具体代码实现如下:

//声明一个hash锁,为每个entry添加锁
pthread_mutex_t hashlock;

struct entry {
  int key;
  int value;
  struct entry *next;
  pthread_mutex_t lock;
};

//初始化entry锁
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;
  pthread_mutex_init(&e->lock,NULL);
  e->next = n;
  *p = e;
}

//main中初始化hash锁
  pthread_mutex_init(&hashlock,NULL);
  nthread = atoi(argv[1]);

static 
void put(int key, int value)
{
  int i = key % NBUCKET;

  // 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.
    pthread_mutex_lock(&e->lock);
    e->value = value;
    pthread_mutex_unlock(&e->lock);
  } else {
    // the new is new.
    pthread_mutex_lock(&hashlock);
    insert(key, value, &table[i], table[i]);
    pthread_mutex_unlock(&hashlock);
  }
}

三 Barrier

该实验是为了实现一个barrier,使线程阻塞在barrier前,直到所有线程都抵达barrier后,才能执行下一轮。为了确保每一个线程阻塞在barrier前,需要使用条件锁,每个线程抵达barrier后需要对bstate.nthread++,如果bstate.nthread不等于nthread,那么说明最后一个线程还未抵达barrier,需要将线程阻塞,当最后一个线程抵达时,再唤醒所有被阻塞的线程,出barrier要将bstate.nthread--。另外当某个线程完成了一轮循环,进入到下一轮循环时,由于其他线程还没有出barrier,此时bstate.nthread还属于上一轮,因此线程需要进行阻塞。

//定义一个条件锁
pthread_cond_t round_lock;

static void 
barrier()
{
  // YOUR CODE HERE
  //
  // Block until all threads have called barrier() and
  // then increment bstate.round.
  //
  pthread_mutex_lock(&bstate.barrier_mutex);
  if(bstate.round!=round) pthread_cond_wait(&round_lock,&bstate.barrier_mutex);
  bstate.nthread++;
  if(bstate.nthread!=nthread)
    pthread_cond_wait(&bstate.barrier_cond,&bstate.barrier_mutex);
  else{
    pthread_cond_broadcast(&bstate.barrier_cond);
    bstate.round++;
  }
  bstate.nthread--;
  if(bstate.nthread==0){
    round++;
    pthread_cond_broadcast(&round_lock);
  }
  pthread_mutex_unlock(&bstate.barrier_mutex);

}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值