BUAA OS 作业:进程同步与PV操作
BUAA OS 作业:进程同步与PV操作
注:本文中的伪代码可能有的地方不符合题目要求,如发现有误或有更好的实现方式,麻烦大家指出或在评论区留言,谢谢!
第一题
读者写者问题(写者优先): 1)共享读; 2)互斥写、读写互斥; 3)写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)。
int read_count = 0, write_count = 0;
semaphore read_mutex = 1, write_mutex = 1;
semaphore data_mutex = 1; // 互斥访问共享数据
semaphore read = 1;
void writer() {
while (true) {
P(write_count);
if (write_count++ == 0) P(read); //只要有写者,就占着read
V(write_count);
P(data_mutex);
// writing
V(data_mutex);
P(write_count);
if (--write_count == 0) V(read); //没有写者才释放read
V(write_count);
}
}
void reader() {
while (true) {
P(read); // 没有写者才能进入临界区
P(read_mutex);
if (read_mutex++ == 0) P(data_mutex);
V(read_mutex);
V(read);
// reading
P(read_mutex);
if (--read_mutex == 0) V(data_mutex);
V(read_mutex);
}
}
第二题
寿司店问题。假设一个寿司店有 5 个座位,如果你到达的时候有一个空座位,你可以立刻就坐。但是如果你到达的时候 5 个座位都是满的有人已经就坐,这就意味着这些人都是一起来吃饭的,那么你需要等待所有的人一起离开才能就坐。编写同步原语,实现这个场景的约束。
int wait_num = 0, eat_num = 0;
bool no_chairs = false;
semaphore mutex = 1; //互斥访问上面三个变量
semaphore block = 0;
void eat() {
while (true) {
P(mutex);
if (no_chairs) { // 5个位置都满了,需要等所有人吃完
wait_num++; // mutex保证互斥
P(block);
} else {
eat_num++;
no_chairs = (eat_num == 5);
V(mutex);
}
// eat
P(mutex);
eat_num--;
if (eat_num == 0) {
int n = min(wait_num, 5);
wait_num -= n;
eat_num += n;
no_chairs = (n == 5);
while (n--) V(block);
}
V(mutex);
}
}
第三题
三个进程 P1、P2、P3 互斥使用一个包含 N(N>0)个单元的缓冲区。P1 每次用 produce()生成一个正整数并用 put()送入缓冲区某一个空单元中;P2 每次用getodd()从该缓冲区中取出一个奇数并用 countodd()统计奇数个数;P3 每次用 geteven()从该缓冲区中取出一个偶数并用 counteven()统计偶数个数。请用信号量机制实现这三个进程的同步与互斥活动,并说明所定义的信号量的含义。要求用伪代码描述。
var mutex: semaphore := 1; // 互斥信号量,互斥访问这N个单元
var even: semaphore := 0; // 奇数数据的同步信号量,表示已占用缓冲区中偶数个数;
var odd: semaphore := 0; // 偶数数据的同步信号量,表示已占用缓冲区中奇数个数;
var empty: semaphore := N; // 缓冲区同步信号量,表示空闲缓冲区个数
// p1
begin
repeat
P(empty);
var number: integer := produce();
P(mutex);
put(number);
V(mutex);
if (number % 2 = 0) then
V(even);
else
V(odd);
until false;
end
// p2
begin
repeat
P(odd); // 不能先P(mutex)
P(mutex);
var ret: integer := getOdd();
V(mutex);
V(empty);
countodd();
until false;
end
//p3
begin
repeat
P(even);
P(mutex);
var ret: integer := getEven();
V(mutex);
V(empty);
counteven();
until false;
end.
第四题
搜索-插入-删除问题。三个线程对一个单链表进行并发的访问,分别进行搜索、插入和删除。搜索线程仅仅读取链表,因此多个搜索线程可以并发。插入线程把数据项插入到链表最后的位置;多个插入线程必须互斥防止同时执行插入操作。但是,一个插入线程可以和多个搜索线程并发执行。最后,删除线程可以从链表中任何一个位置删除数据。一次只能有一个删除线程执行;删除线程之间,删除线程和搜索线程,删除线程和插入线程都不能同时执行。请编写三类线程的同步互斥代码,描述这种三路的分类互斥问题。
/* 优先级:搜索 > 插入 > 删除 */
int search_num = 0;
semaphore has_searchers = 1;
semaphore has_inserters = 1;
semaphore search_num_mutex = 1; // 互斥访问search_num
void del() {
while (true) {
P(has_searchers); //不能有其他搜索进程
P(has_inserters); //不能有其他插入进程
// 上面两个信号量已经保证了del访问临界资源互斥,不需要另外新加信号量
/* delete */
V(has_inserters);
V(has_searchers);
}
}
void search() {
while (true) {
P(search_num_mutex);
if (search_num++ == 0) {
V(search_num_mutex);
P(has_searchers);
} else V(search_num_mutex);
/* search */
P(search_num_mutex);
if (--search_num == 0) {
V(has_searchers);
}
V(search_num_mutex);
}
}
void insert() {
while (true) {
P(has_inserters); // 保证insert之间互斥
// 此时一定只有一个insert进程执行,不需对search进程数目限制,同时此时没有delete进程
/* insert */
V(has_inserters);
}
}