您当前的位置: 首页 >  操作系统

蔗理苦

暂无认证

  • 5浏览

    0关注

    88博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

2021-11-02 操作系统实验3——生产者消费者实验

蔗理苦 发布时间:2021-11-02 14:55:53 ,浏览量:5

文章目录
    • 一、实验目的
    • 二、实验任务
    • 三、实验要求
    • 四、实验过程
    • 五、实验测试

一、实验目的

了解和熟悉linux系统下的信号量集和共享内存。

二、实验任务

使用linux系统提供的信号量集和共享内存实现生产者和消费者问题。

三、实验要求
  1. 写两个程序,一个模拟生产者过程,一个模拟消费者过程;
  2. 创建一个共享内存模拟生产者-消费者问题中缓冲队列,该缓冲队列有N(例如N=10)个缓冲区,每个缓冲区的大小为1024B,每个生产者和消费者对缓冲区必须互斥访问;
  3. 由第一个生产者创建信号量集和共享内存,其他生产者和消费者可以使用该信号量集和共享内存;
  4. 生产者程序:生产者生产产品(即是从键盘输入长度小于1024B的字符)放入空的缓冲区;
  5. 消费者程序:消费者消费产品(即从满的缓冲区中取出内容在屏幕上打印出来),然后满的缓冲区变为空的缓冲区;
  6. 多次运行生产者程序和消费者进程,可以产生多个生产者进程和多个消费者进程,这些进程可以共享这些信号量和共享内存,实现生产者和消费者问题;
  7. 在生产者程序程序中,可以选择: (1)生产产品; (2)退出。退出进程,但信号量和共享内存仍然存在,其他生产者进程和消费者进程还可以继续使用; (3)删除信号量和共享内存。显性删除信号量和共享内存,其他生产者进程和消费者进程都不能使用这些信号量和共享内存。
  8. 在消费者程序中,可以选择: (1)消费产品; (2)退出。退出进程,但信号量和共享内存仍然存在,其他生产者进程和消费者进程还可以继续使用; (3)删除信号量和共享内存。显性删除信号量和共享内存,其他生产者进程和消费者进程都不能使用这些信号量和共享内存。
四、实验过程

1、实验结构:

实验一共包括2个文件,一个是producer.c,负责生产产品;一个是consumer.c,负责消费产品。编译后生成producer和consumer,负责展示生产者和消费者的过程。

2、producer.c文件

(1)宏定义

#define N 5                       // 缓冲队列个数
#define BUFFERSIZE 1024         // 缓冲存储区大小为 1024B
#define SEMNUM 3                 // 信号量集中信号量个数为 3,分别为 mutex、full 和 empty

宏定义使得程序的修改方便。

(2)结构定义

struct sharedMemory {            // 共享内存结构
   char buffer[N][BUFFERSIZE];  // 消息存储区
   int put, take;                  // 两个指针
};

union semun {
   int val;                       // value for setval
   struct semid_ds *buf;       // buffer for IPC_STAT & IPC_SET
   unsigned short *array;      // array for GETALL & SETALL
   struct seminfo *_buf;       // buffer for IPC_INFO
   void *_pad;
};

sharedMemory是我们自己定义的共享内存结构,其中包括N个大小为BUFFERSIZE的消息队列和存放指针put、take。 semun是用来操作信号量的共用体,这里由我们自己定义,程序中主要使用它的val成员。

(3)封装函数操作

  1. P、V操作:
    // 封装 P 操作
    int P(int semid, unsigned short semnum) {
       struct sembuf semaphore;
       semaphore.sem_num = semnum;
       semaphore.sem_op = -1;
       semaphore.sem_flg = SEM_UNDO;
       return semop(semid, &semaphore, 1);
    }
    
    // 封装 V 操作
    int V(int semid, unsigned short semnum) {
       struct sembuf semaphore;
       semaphore.sem_num = semnum;
       semaphore.sem_op = 1;
       semaphore.sem_flg = SEM_UNDO;
       return semop(semid, &semaphore, 1);
    }
    
    这里为了mian函数代码的简洁和直观性,封装了P、V操作。其中第一个参数是信号量集标识semid,第二个参数标识信号量集中第semnum个信号量。这里P、V操作对信号量的资源改变数量都为1.
  2. 信号量的读写操作:
    // 封装对信号量的赋值操作
    int setSemVal(int semid, int semnum, int val) {
       union semun smun;
       smun.val = val;
       return semctl(semid, semnum, SETVAL, smun);
    }
    
    // 封装读取信号量的操作
    int getSemVal(int semid, int semnum) {
       return semctl(semid, semnum, GETVAL, 0);
    }
    
    出于和上面P、V操作一样的目的,这里前两个参数和上面一样,setSemVal操作中第三个参数val表示对信号量的赋值。

(4)main函数

  1. main函数中主要变量

    int main() {
       int key, i;                   // key 为共享内存的键值
       int shmexist = 0;             // 表示共享内存是否已存在
       void *shm = NULL;             // 连接共享内存的标记
       struct sharedMemory *share;   // 连接到共享内存的指针
       int shmid;                    // 共享内存标识符
       int semid;                    // 信号量集标识符
       int choice;                   // 选择标识
       int ifbreak = 0;              // 跳出循环的标识
       char message[BUFFERSIZE];     // 临时消息存储
    

    其中需要说明的是:

    shmexist表示该producer是否为第一个producer,以便后面对信号量、共享内存的初始化操作。 ifbreak表示循环是否需要退出,在该实验中,当用户输入2和3时需要退出循环,结束程序。 message用来缓存用户输入的消息。我们不能直接将用户输入的消息存入共享内存中,因为producer可能会因P操作而被挂起。

  2. 运行的前期操作,包括: ① 创建/获取共享内存

    // 获取共享内存的键值
    key = ftok("./consumer.c", 0);
    // 判断是否获取成功
    if (key == -1) {
       printf("ftok() failed\n");
       exit(EXIT_FAILURE);
    }
    
    // 请求创建新共享内存
    shmid = shmget(key, sizeof(struct sharedMemory), 0666 | IPC_CREAT | IPC_EXCL);
    // 如果创建新的共享内存失败,则试图获取已有的共享内存
    if (shmid == -1) {
       shmexist = 1;      // 共享内存已存在
       shmid = shmget(key, sizeof(struct sharedMemory), 0666 | IPC_CREAT);
       if (shmid == -1) {
          printf("shmget() failed\n");
          exit(EXIT_FAILURE);
       }
    }
    

    ② 进程与共享内存的连接

    // 进程请求连接共享内存,若失败则退出
    if ((shm = shmat(shmid, NULL, 0)) == (void *) (-1)) {
       printf("shmat() failed\n");
       exit(EXIT_FAILURE);
    }
    
    // share 访问共享内存
    share = (struct sharedMemory *) shm;
    

    ③ 创建/获取信号量集

    // 创建含有 3 个信号量的信号量集
    // 0 : mutex
    // 1 : full
    // 2 : empty
    semid = semget(key, SEMNUM, IPC_CREAT | 0666);
    

    ④ producer特有的初始化

    // 如果共享内存是第一次创建,则初始化
    if (shmexist == 0) {
       for (i = 0; i buffer[i], "");
       share->put = share->take = 0;
       setSemVal(semid, 0, 1);    // 互斥信号量 mutex
       setSemVal(semid, 1, 0);    // 信号量 full
       setSemVal(semid, 2, N);        // 信号量 empty
    }
    
  3. while函数(主运行程序) ① 输出程序状态,包括: producer的进程id; full、empty信号量的值; put、take指针位置; 共享内存中的数据; 可供用户选择的操作。 int full, empty;

    // 获得信号量的值
    full = getSemVal(semid, 1);
    empty = getSemVal(semid, 2);
    
    // 一系列输出
    printf("This is %d pid producer,\n\n", getpid());
    printf("full = %d, empty = %d, put = %d, take = %d.\n\n", full, empty, share->put, share->take);
    for (i = 0; i put)
          printf("%d --- %s \t             
关注
打赏
1657823434
查看更多评论
0.1430s