📜  门| GATE-CS-2006 |问题 19(1)

📅  最后修改于: 2023-12-03 15:28:43.046000             🧑  作者: Mango

门 | GATE-CS-2006 |问题 19

这是 GATE-CS-2006 的第19道问题,让我们一起来看看这个有趣的问题吧!

问题描述

在一个虚拟机器上运行了两个进程 $P1$ 和 $P2$ ,它们需要通过共享内存进行通信。 共享内存的大小为 $1000$ 个字节,可以被看作一个大的字符数组。 进程 $P1$ 写入了一些字符串到该数组中,$P2$ 随后读取了这些字符串。 当 $P1$ 写入字符串时,它首先将字符串长度写入共享内存的第一个字节(称为长度字节),然后写入字符串剩余部分。 进程 $P2$ 读取长度字节,然后根据该长度读取整个字符串。

$P1$ 和 $P2$ 交替运行。 也就是说,当 $P1$ 运行时,它会写入一个字符串到共享内存中,然后释放CPU,进入阻塞状态; 当 $P2$ 运行时,它会从共享内存中读取一个字符串,然后释放CPU,进入阻塞状态。 请考虑以下几点:

  1. 两个进程分享内存,如何防止竞争条件?你可以假设编译器和硬件提供原子操作 Test-and-Set 或者 Swap 。
  2. 如果 $P1$ 占用了CPU时间太久,那么会发生什么情况?同样的,如果 $P2$ 占用了CPU时间太久会怎么样?
解题思路
  1. 对于共享内存的读写,在多进程并发的情况下,我们需要使用同步机制来避免竞争条件。一个简单的做法是使用信号量来进行同步。每次 $P1$ 写入一个字符串时,它会使用信号量将 $P2$ 阻塞,防止 $P2$ 错误地读取未完成的字符串。写入完成后,$P1$ 会释放信号量,并且等待信号量(如果此时 $P2$ 仍未读取共享内存,则 $P1$ 将自己阻塞)。类似地,$P2$ 在读取字符串时会使用信号量将 $P1$ 阻塞,避免交错读取。读取完成后,$P2$ 将释放信号量,并等待信号量(如果此时 $P1$ 仍未写入,则 $P2$ 将自己阻塞)。

  2. 如果 $P1$ 或 $P2$ 在运行时占用了CPU时间太久,那么该进程在等待共享内存时将会浪费大量的时间,从而增加了等待时间和系统负载。一个解决方案是使用调度算法来保证公平性,即每个进程分配相同的CPU时间片(时间片轮转)。另一个解决方案是采用优先级调度算法,即将 $P1$ 和 $P2$ 分别赋予不同的优先级,以确保高优先级进程优先执行。

代码示例
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#define SHARED_MEMORY_SIZE 1000  // 共享内存大小
#define MAX_STRING_SIZE 1000     // 字符串最大长度

typedef struct SharedMemory {
  char memory[SHARED_MEMORY_SIZE];  // 共享内存大小
  int length;                       // 字符串长度 
  int current_pos;                  // 当前位置 
} SharedMemory;

enum ProcessId {
  P1 = 1,
  P2 = 2
};

bool test_and_set(bool* lock) {
  bool old_value = *lock;
  *lock = true;
  return old_value;
}

void swap(bool* a, bool* b) {
  bool tmp = *a;
  *a = *b;
  *b = tmp;
}

void acquire_mutex(bool* lock) {
  while (test_and_set(lock));
}

void release_mutex(bool* lock) {
  *lock = false;
}

void write_string_to_shared_memory(SharedMemory* mem, const char* str) {
  int len = strlen(str);
  acquire_mutex(&mem->memory[0]);
  if (mem->current_pos + len + 1 >= SHARED_MEMORY_SIZE) {
    fprintf(stderr, "Shared memory is full!\n");
    exit(1);
  }
  mem->memory[mem->current_pos] = (char)len;
  strncpy(&mem->memory[mem->current_pos + 1], str, len);
  mem->current_pos += len + 1;
  release_mutex(&mem->memory[0]);
}

void read_string_from_shared_memory(SharedMemory* mem, char* buffer) {
  acquire_mutex(&mem->memory[0]);
  int len = mem->memory[mem->current_pos];
  if (mem->current_pos + len + 1 >= SHARED_MEMORY_SIZE) {
    fprintf(stderr, "Shared memory is empty!\n");
    exit(1);
  }
  strncpy(buffer, &mem->memory[mem->current_pos + 1], len);
  buffer[len] = '\0';
  mem->current_pos += len + 1;
  release_mutex(&mem->memory[0]);
}

int main() {
  SharedMemory* mem = malloc(sizeof(SharedMemory));
  memset(mem, 0, sizeof(SharedMemory));
  mem->current_pos = 0;

  int process_id = 1;
  while (true) {
    if (process_id == P1) {  // P1 写入
      char str[MAX_STRING_SIZE];
      printf("P1: Enter a string to write: ");
      scanf("%s", str);

      write_string_to_shared_memory(mem, str);
    } else {  // P2 读取
      char buffer[MAX_STRING_SIZE];
      read_string_from_shared_memory(mem, buffer);
      printf("P2: Read a string: %s\n", buffer);
    }
    process_id = (process_id == P1 ? P2 : P1);
    usleep(100000);  // 睡眠 100 ms
  }

  free(mem);
  return 0;
}

以上是C语言的代码,实现了共享内存的读写和同步,可以在多进程并发的情况下工作。在代码中还使用了死循环和时间延迟来模拟进程交替运行的过程。

代码说明

代码中的 SharedMemory 结构体是用来表示共享内存的,其中包含了一个字符数组 memory 用来存储字符串和一个整型变量 current_pos 用来记录当前位置。 write_string_to_shared_memory 函数用来向共享内存中写入字符串,它首先会调用 acquire_mutex 获取互斥锁,然后根据字符串长度将字符串和长度写入共享内存。写入完成后,它会调用 release_mutex 释放互斥锁,并等待互斥锁(如果此时另外一个进程正在写入,则当前进程将自己阻塞,等待其它进程释放互斥锁)。读取共享内存的过程与之类似,在读取前也需要获取互斥锁。

以上是C语言的代码实现,你也可以使用其它语言来实现,只要保证实现上述的同步机制即可。