《操作系统导论》第31章课后答案

31.1

问题描述:

第一个问题就是实现和测试 fork/join 问题的解决方案,如本文所述。 即使在文本中描述了此解决方案, 重新自己实现一遍也是值得的。 even Bach would rewrite Vivaldi, allowing one soon-to-be master to learn from an existing one。 有关详细信息,请参见 fork-join.c。 将添加 sleep(1) 到 child 函数内以确保其正常工作。

解答:

题目要求我们补全fork-join.c

然后按顺序打印

parent:begin
child
parent:end

完整的代码如下:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include "common_threads.h"
#include <semaphore.h>

sem_t s;

void *child(void *arg) {
    sleep(1);
    printf("child\n");
    sem_post(&s);
    return NULL;
}

int main(int argc, char *argv[]) {
    pthread_t p;
    printf("parent: begin\n");
    sem_init(&s, 0, 0);
    Pthread_create(&p, NULL, child, NULL);
    sem_wait(&s);
    printf("parent: end\n");
    return 0;
}

输入命令gcc -o fork-join fork-join.c -Wall -pthread查看运行结果
在这里插入图片描述

31.2

问题描述

现在,我们通过研究集合点问题 rendezvous problem 来对此进行概括。 问题如下:您有两个线程,每个线程将要在代码中进入集合点。 任何一方都不应在另一方进入之前退出代码的这一部分。 该任务使用两个信号量,有关详细信息,请参见 rendezvous.c。

解答:

题目要求先打印两个before,再打印两个after

完整的代码如下:

#include <stdio.h>
#include <unistd.h>
#include "common_threads.h"
#include <semaphore.h>

// If done correctly, each child should print their "before" message
// before either prints their "after" message. Test by adding sleep(1)
// calls in various locations.

sem_t s1, s2;

void *child_1(void *arg) {
    printf("child 1: before\n");
    sleep(1);
    sem_post(&s2);
    sem_wait(&s1);
    printf("child 1: after\n");
    return NULL;
}

void *child_2(void *arg) {
    printf("child 2: before\n");
    sleep(1);
    sem_post(&s1);
    sem_wait(&s2);
    printf("child 2: after\n");
    return NULL;
}

int main(int argc, char *argv[]) {
    pthread_t p1, p2;
    printf("parent: begin\n");
    sem_init(&s1, 0, 0);
    sem_init(&s2, 0, 0);
    Pthread_create(&p1, NULL, child_1, NULL);
    Pthread_create(&p2, NULL, child_2, NULL);
    Pthread_join(p1, NULL);
    Pthread_join(p2, NULL);
    printf("parent: end\n");
    return 0;
}

输入命令gcc -o a rendezvous.c -Wall -pthread 查看运行结果
在这里插入图片描述

31.4

问题描述

现在按照文本中所述,解决读者写者问题。 首先,不用考虑进程饥饿。 有关详细信息,请参见 reader-writer.c 中的代码。 将 sleep()调用添加到您的代码中,以证明它可以按预期工作。 你能证明饥饿问题的存在吗?

解答

题目要求我们补充reader-writer.c 中的函数代码

完整代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "common_threads.h"
#include <semaphore.h>
//
// Your code goes in the structure and functions below
//

typedef struct __rwlock_t {
    sem_t lock;
    sem_t write_lock;
    int reader_number;
} rwlock_t;


void rwlock_init(rwlock_t *rw) {
    sem_init(&rw->lock, 0, 1);
    sem_init(&rw->write_lock, 0, 1);
    rw->reader_number = 0;
}

void rwlock_acquire_readlock(rwlock_t *rw) {
    sleep(1);
    sem_wait(&rw->lock);
    rw->reader_number++;
    if (rw->reader_number == 1) {
        sem_wait(&rw->write_lock);
    }
    sem_post(&rw->lock);
}

void rwlock_release_readlock(rwlock_t *rw) {
    sem_wait(&rw->lock);
    rw->reader_number--;
    if (rw->reader_number == 0) {
        sem_post(&rw->write_lock);
    }
    sem_post(&rw->lock);
}

void rwlock_acquire_writelock(rwlock_t *rw) {
    sleep(1);
    sem_wait(&rw->write_lock);
}

void rwlock_release_writelock(rwlock_t *rw) {
    sem_post(&rw->write_lock);
}

//
// Don't change the code below (just use it!)
// 

int loops;
int value = 0;

rwlock_t lock;

void *reader(void *arg) {
    int i;
    for (i = 0; i < loops; i++) {
        rwlock_acquire_readlock(&lock);
        printf("read %d\n", value);
        rwlock_release_readlock(&lock);
    }
    return NULL;
}

void *writer(void *arg) {
    int i;
    for (i = 0; i < loops; i++) {
        rwlock_acquire_writelock(&lock);
        value++;
        printf("write %d\n", value);
        rwlock_release_writelock(&lock);
    }
    return NULL;
}

int main(int argc, char *argv[]) {
    assert(argc == 4);
    int num_readers = atoi(argv[1]);
    int num_writers = atoi(argv[2]);
    loops = atoi(argv[3]);
    pthread_t pr[num_readers], pw[num_writers];
    rwlock_init(&lock);
    printf("begin\n");
    int i;
    for (i = 0; i < num_readers; i++)
        Pthread_create(&pr[i], NULL, reader, NULL);
    for (i = 0; i < num_writers; i++)
        Pthread_create(&pw[i], NULL, writer, NULL);

    for (i = 0; i < num_readers; i++)
        Pthread_join(pr[i], NULL);
    for (i = 0; i < num_writers; i++)
        Pthread_join(pw[i], NULL);

    printf("end: value %d\n", value)
    return 0;
}

程序三个参数分别为读者数,写者数,每个读者、写者进行的读写操作数
当读者数量远大于写者时,写者可能饿死,读者不需要锁就能进入临界区,只要有一个读者获得锁,
其他读者线程就能运行,读者数量可能一直大于0,而写者始终无法获取锁

运行命令

gcc -o reader-writer reader-writer.c -Wall -pthread
./reader-writer 5 5 10

查看结果

在这里插入图片描述

31.5

问题描述

让我们再次看一下读者写者问题,但这一次需要考虑进程饥饿。 您如何确保所有读者和写者运行? 有关详细信息,请参见 reader-writer-nostarve.c。

解答

新增 write_waiting 锁,写者读者都需要竞争这个锁。新增读者要持有这把锁,写者进行写也要持有这把锁,一旦写者抢到了这把锁,就不能增加新读者,直到写者完成写(写者要等当前所有读者离开才能写)

完整代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "common_threads.h"
#include <semaphore.h>
//
// Your code goes in the structure and functions below
//

typedef struct __rwlock_t {
    sem_t lock;
    sem_t write_lock;
    sem_t write_waiting;
    int reader_number;
} rwlock_t;


void rwlock_init(rwlock_t *rw) {
    sem_init(&rw->lock, 0, 1);
    sem_init(&rw->write_lock, 0, 1);
    sem_init(&rw->write_waiting, 0, 1);
    rw->reader_number = 0;
}

void rwlock_acquire_readlock(rwlock_t *rw) {
    sleep(1);
    sem_wait(&rw->write_waiting);
    sem_wait(&rw->lock);
    rw->reader_number++;

    if (rw->reader_number == 1) {
        sem_wait(&rw->write_lock);
    }
    sem_post(&rw->lock);
    sem_post(&rw->write_waiting);

}

void rwlock_release_readlock(rwlock_t *rw) {
    sem_wait(&rw->lock);
    rw->reader_number--;
    if (rw->reader_number == 0) {
        sem_post(&rw->write_lock);
    }

    sem_post(&rw->lock);
}

void rwlock_acquire_writelock(rwlock_t *rw) {
    sleep(1);
    sem_wait(&rw->write_waiting);
    sem_wait(&rw->write_lock);
    sem_post(&rw->write_waiting);
}

void rwlock_release_writelock(rwlock_t *rw) {
    sem_post(&rw->write_lock);
}

//
// Don't change the code below (just use it!)
//

int loops;
int value = 0;

rwlock_t lock;

void *reader(void *arg) {
    int i;
    for (i = 0; i < loops; i++) {
        rwlock_acquire_readlock(&lock);
        printf("read %d\n", value);
        rwlock_release_readlock(&lock);
    }
    return NULL;
}

void *writer(void *arg) {
    int i;
    for (i = 0; i < loops; i++) {
        rwlock_acquire_writelock(&lock);
        value++;
        printf("write %d\n", value);
        rwlock_release_writelock(&lock);
    }
    return NULL;
}

int main(int argc, char *argv[]) {
    assert(argc == 4);
    int num_readers = atoi(argv[1]);
    int num_writers = atoi(argv[2]);
    loops = atoi(argv[3]);

    pthread_t pr[num_readers], pw[num_writers];

    rwlock_init(&lock);

    printf("begin\n");

    int i;
    for (i = 0; i < num_readers; i++)
        Pthread_create(&pr[i], NULL, reader, NULL);
    for (i = 0; i < num_writers; i++)
        Pthread_create(&pw[i], NULL, writer, NULL);

    for (i = 0; i < num_readers; i++)
        Pthread_join(pr[i], NULL);
    for (i = 0; i < num_writers; i++)
        Pthread_join(pw[i], NULL);

    printf("end: value %d\n", value);

    return 0;
}

输入命令

gcc -o a reader-writer-nostarve.c -Wall -pthread
./a 5 5 10

查看运行结果:

在这里插入图片描述

31.6

问题描述

使用信号量构建一个没有饥饿的互斥量,其中任何试图获取该互斥量的线程都将最终获得它。 有关更多信息,请参见 mutex-nostarve.c 中的代码。

解答

这题的主要原理为释放临界区锁后,调用barrier.c内实现的barrier函数,等待所有线程执行完再继续进行

完整代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include "common_threads.h"
#include "semaphore.h"
//
// Here, you have to write (almost) ALL the code. Oh no!
// How can you show that a thread does not starve
// when attempting to acquire this mutex you build?
//

typedef struct __ns_mutex_t {
    sem_t mutex;
    sem_t barrier;
    sem_t lock;
    int num_threads;
} ns_mutex_t;

ns_mutex_t m;

void ns_mutex_init(ns_mutex_t *m, int num_threads) {
    sem_init(&m->barrier, 0, -num_threads + 1);
    sem_init(&m->mutex, 0, 1);//互斥锁
}

void ns_mutex_acquire(ns_mutex_t *m) {
    sem_wait(&m->lock);
}

void ns_mutex_release(ns_mutex_t *m) {
    sem_post(&m->lock);
    sem_post(&m->barrier);
    sem_wait(&m->barrier);
    sem_wait(&m->mutex);
    for (int i = 0; i < m->num_threads; i++) {
        sem_post(&m->barrier);
        //确认所有线程都执行过临界区后再继续执行
    }
    sem_post(&m->mutex);
}


void *worker(void *arg) {
    return NULL;
}

int main(int argc, char *argv[]) {
    printf("parent: begin\n");
    printf("parent: end\n");
    return 0;
}

输入命令运行结果如下:
在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值