山东大学操作系统实验--3

问题描述:

抽烟者问题。假设一个系统中有三个抽烟者进程,每个抽烟者不断地卷烟并抽
烟。抽烟者卷起并抽掉一颗烟需要有三种材料:烟草、纸和胶水。一个抽烟者有烟
草,一个有纸,另一个有胶水。系统中还有两个供应者进程,它们无限地供应所有
三种材料,但每次仅轮流提供三种材料中的两种。得到缺失的两种材料的抽烟者在
卷起并抽掉一颗烟后会发信号通知供应者,让它继续提供另外的两种材料。这一过
程重复进行。 请用以上介绍的 IPC 同步机制编程,实现该问题要求的功能。

问题分析:

抽烟者问题的本质就是一个生产者对应多个消费者的问题。首先分析各个对象之间的关系
生产者---消费者(同步关系、互斥访问缓冲区)
消费者---消费者(互斥访问缓冲区)
因此我们需要一个buff初始值为1来表示生产者---消费者、消费者---消费者的互斥关系,需要tobacco、paper、matches来表示生产者与各个消费者之间的同步关系。

使用systemV和共享内存的方式来解决该问题

systemV:System V 信号量是 Unix 系统提供的一种进程间同步机制,用于协调多个进程对共享资源的访问。它们是 System V IPC (Inter-Process Communication) 的一部分,允许进程通过一组信号量来控制对资源的访问。

systemV的常用函数

#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg|(权限位));
//创建一个键值为key,大小为size,标识符为shmflg的共享内存
//key可以自由定义
//size可以自由定义单位是字节
/*shmflg的取值一般是IPC_CREAT|0666标识创建如果不存在指定键值的共享内存就创建一个,同时0666代表允许所有用户读写,权限位通过按位或运算设置。IPC_EXCL与 IPC_CREAT 结合使用,用于确保创建一个新的共享内存段。如果已存在具有相同键值的共享内存段,则返回错误。*/
//返回值是shmid

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);
//创建一个键值为ket且拥有nsems个信号量操作标识和权限位为semflg的信号量集
//同时semget也可以通过键值直接获取semid
semid = semget(key, 0, 0666);
//semget用于获取信号量集的ID时。这里的第二个参数设为0,表示不创建新的信号量集,而是获取现有的信号量集。


#include <sys/sem.h>

int semop(int semid, struct sembuf *sops, size_t nsops);
//对键值为semid的信号量集合进行sops操作,nsops是sops的操作数
//semid可以通过键值和semget来获取
//sops是一个指向struct sembuf的指针
struct sembuf {
    unsigned short sem_num;  
    short sem_op;            
    short sem_flg;           
};
sem_num;  // 该信号量集合当中的某一个信号量
sem_op;            // 操作类型(负数为降低到某一程度,整数为+1,若对应信号量已经等于0了就会被阻塞,直到信号量大于0后被唤醒)
sem_flg;           // 操作标志,通常为0
nsops //根据操作决定,执行pv操作时为1

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semctl(int semid, int semnum, int cmd, ...);
//用于控制某一信号量的状态
semid //信号量所在的信号量集
semnum //信号量在信号量集当中的编号
cmd //命令 IPC_RMID:删除信号量集。
//IPC_SET:设置信号量集的属性。
//IPC_STAT:获取信号量集的属性。
//GETVAL:获取指定信号量的值。
//SETVAL:设置指定信号量的值。
//GETALL:获取信号量集中的所有信号量的值。
//SETALL:设置信号量集中的所有信号量的值。
后面的...是cmd所需要的参数
semctl常用于信号量的初始化,删除

代码:

#include <iostream>
#include <thread>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>

#define SHM_KEY 12345
#define SEM_KEY 54321

#define AGENT_SEM_INDEX 0
#define SMOKER1_SEM_INDEX 1
#define SMOKER2_SEM_INDEX 2
#define SMOKER3_SEM_INDEX 3
#define BUFF_SEM_INDEX 4

struct Table {
    bool tobacco;
    bool paper;
    bool matches;
    bool buff;
};

int main() {

    int shm_id = shmget(SHM_KEY, sizeof(Table), IPC_CREAT | 0666);
    if (shm_id == -1) {
        std::cerr << "Failed to create shared memory segment." << std::endl;
        return 1;
    }

    Table* table = (Table*)shmat(shm_id, nullptr, 0);
    if (table == (Table*)(-1)) {
        std::cerr << "Failed to attach to shared memory segment." << std::endl;
        return 1;
    }

    int sem_id = semget(SEM_KEY, 5, IPC_CREAT | 0666);
    if (sem_id == -1) {
        std::cerr << "Failed to create semaphore set." << std::endl;
        return 1;
    }

    semctl(sem_id, AGENT_SEM_INDEX, SETVAL, 1);
    semctl(sem_id, SMOKER1_SEM_INDEX, SETVAL, 0); 
    semctl(sem_id, SMOKER2_SEM_INDEX, SETVAL, 0); 
    semctl(sem_id, SMOKER3_SEM_INDEX, SETVAL, 0); 
    semctl(sem_id, BUFF_SEM_INDEX, SETVAL,1);					  // buffer

 auto agent = [&]() {
	 sembuf buf;
	 buf.sem_num = BUFF_SEM_INDEX;
	 buf.sem_op = -1;
	 buf.sem_flg = 0;
	 semop(sem_id,&buf,1);
         int cnt=0;
	 while (true) {
            sembuf op;
            op.sem_num = AGENT_SEM_INDEX;
            op.sem_op = -1;
            op.sem_flg = 0;
            semop(sem_id, &op, 1);
	    if(cnt==0){
            table->tobacco = false;
            table->paper = true;
            table->matches = true;
            std::cout << "Agent puts matches and paper on the table." << std::endl;
	    op.sem_num = SMOKER1_SEM_INDEX;
            op.sem_op = 1;
            semop(sem_id, &op, 1);
	    }
	    if(cnt==1){
		    table->tobacco=true;
		    table->paper=true;
		    table->matches=false;
		    op.sem_num = SMOKER3_SEM_INDEX;
           	    op.sem_op = 1;
            	    semop(sem_id,&op,1);
		    std::cout<<"Agent puts tobacco and paper on the table"<<std::endl;
	    }
	    if(cnt==2){
		    table->tobacco=true;
		    table->paper=false;
		    table->matches=true;
		    op.sem_num = SMOKER2_SEM_INDEX;
           	    op.sem_op = 1;
           	    semop(sem_id, &op, 1);
		    std::cout<<"Agent puts tobacco and matches on the table"<<std::endl;
	    }
	    cnt++;
	    cnt%=3;
	    sleep(3);
            buf.sem_num = BUFF_SEM_INDEX;
            buf.sem_op = 1;
            op.sem_flg = 0;
            semop(sem_id,&buf,1);

	}
    };


    auto smoker = [&](int index, bool hasTobacco, bool hasPaper, bool hasMatches) {
    while (true) {
        sembuf buf;
        buf.sem_num = BUFF_SEM_INDEX;
        buf.sem_op = -1;
        buf.sem_flg = 0;
        semop(sem_id,&buf,1);
	sembuf op;
        op.sem_num = index;
        op.sem_op = -1;
        op.sem_flg = 0;
        semop(sem_id, &op, 1);

        if (hasTobacco && !table->tobacco && table->paper && table->matches) {
            std::cout << "Smoker with tobacco is smoking." << std::endl;
            table->paper = false;
            table->matches = false;
            op.sem_num = AGENT_SEM_INDEX;
            op.sem_op = 1;
            semop(sem_id, &op, 1);
        } else if (hasPaper && !table->paper && table->tobacco && table->matches) {
            std::cout << "Smoker with paper is smoking." << std::endl;
            table->tobacco = false;
            table->matches = false;
            op.sem_num = AGENT_SEM_INDEX;
            op.sem_op = 1;
            semop(sem_id, &op, 1);
        } else if (hasMatches && !table->matches && table->tobacco && table->paper) {
            std::cout << "Smoker with matches is smoking." << std::endl;
            table->tobacco = false;
            table->paper = false;
            op.sem_num = AGENT_SEM_INDEX;
            op.sem_op = 1;
            semop(sem_id, &op, 1);
        }
	sleep(3);
        buf.sem_num = BUFF_SEM_INDEX;
        buf.sem_op = 1;
        buf.sem_flg = 0;
        semop(sem_id,&buf,1);
    }
};
    std::thread agent_thread(agent);
    std::thread smoker1_thread(smoker, SMOKER1_SEM_INDEX, true, false, false);
    std::thread smoker2_thread(smoker, SMOKER2_SEM_INDEX, false, true, false);
    std::thread smoker3_thread(smoker, SMOKER3_SEM_INDEX, false, false, true);

    agent_thread.join();
    smoker1_thread.join();
    smoker2_thread.join();
    smoker3_thread.join();

    shmdt(table);
    shmctl(shm_id, IPC_RMID, nullptr);
    semctl(sem_id, 0, IPC_RMID);

    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值