操作系统实战(四)(linux+C语言)

目录

实验目的

前提知识

实验题目

题目分析 

 实验程序

头文件

头文件实现 

核心代码文件 (各类进程)

生产者

抽烟者A 

抽烟者B 

抽烟者C 

makefile文件

实验运行 

运行结果分析

总结


实验目的


加深对并发协作进程同步与互斥概念的理解,观察和体验并发进程同步与互斥操作的效果,分析与研究经典进程同步与互斥问题的实际解决方案。了解Linux 系统中IPC进程同步工具的用法,练习并发协作进程的同步与互斥操作的编程与调试技术。

前提知识

在进程并发执行时,需要通过一些信号量对进程进行限制,这种限制是进程能够正确并发执行的必要条件。这个信号量主要有三个:同步信号量、互斥信号量、计数信号量。

同步信号量:是一种用于协调多个进程之间执行顺序或者完成时间的机制。它可以用来确保在某个条件满足时,多个进程能够按照特定的顺序或者时间间隔进行执行,从而实现进程间的同步。

互斥信号量:是一种用于保护共享资源不被多个进程同时访问的机制。它确保了在任何时刻只有一个进程能够进入临界区(Critical Section),从而避免了多个进程同时对共享资源进行写操作或者读写操作,造成数据不一致或者混乱的情况。

计数信号量:是一种允许多个进程同时访问共享资源的信号量机制。与互斥信号量不同,计数信号量允许信号量值大于1,表示有多个资源可供使用。通常用于Buffer限额的情况。

实验题目

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

题目分析 

首先,这是一个进程同步问题,涉及到进程间的同步、互斥。这类问题的解法如下:

第一步:分析所需信号量

1、两个供应者进程提供产品放入共享资源,这里需要一个互斥信号量,保证同一时间只有一个供应者进程在放入产品

2、存在共享资源区:提供给供应者进程放置产品,消费者进程消费产品。所以需要一个计数信号量

3、供应者提供三种材料中的两种,这两种材料对应的消费者进程才会启动去完成工作。所以供应者进程和消费者进程存在先后执行的顺序关系,需要同步信号量。由于有三个消费者进程,所以需要三个同步信号量

第二步:写出核心伪代码 

//生产者进程
while(1){
    wait(empty_int) //计数信号量
    wait(produce_int) //互斥信号量
    生产
    signal(produce_int)
    d=(d+1)%3
    if(d==0) //三个同步信号量
        signal(bc_int)
    else if(d==1)
        signal(ac_int)
    else
        signal(ab_int)
}
//消费者进程(以其中一个为例子)
while(1){
    wait(bc_int)
    消费
    signal(empty_int)
   }

 第三步:分析程序结构

对于复杂的C语言程序,其结构可以分为三个部分:一、头文件;二、头文件的实现;三、核心代码文件。

头文件:函数的声明(头文件中只能有声明)

头文件的实现文件:调用头文件、头文件函数的定义、变量定义(头文件实现中会有各种变量、函数的定义)

核心代码文件:包含头文件、声明但不定义变量(核心代码文件会和头文件实现文件共同完成链接执行工作)

!!切记头文件中只有声明,不能有定义!这相当重要,否则多个文件一起编译运行会出现重定义的错误!!

第四步:分析程序中的变量 

信号量、共享资源、共享变量的创建获取所需要的值都是相同的(标识、初始值、权限)

1、第一步中的5个信号量本身以及每个信号量生成所需要的:key值、初始值、权限分配

2、共享资源区,以及共享资源区创建所需的key值、初始值、权限分配

3、生产者共享变量(指针),记录生产产品放置的位置

4、消费者共享变量(指针),记录取得产品所在的位置

信号量利用set_sem函数创建/获得;后面两者利用set_shm函数创建/获得

 实验程序

头文件

#include <stdio.h>//输入输出
#include <stdlib.h>//基本函数
#include <sys/types.h>//系统数据类型
#include <sys/ipc.h>//进程通信函数及结构体
#include <sys/shm.h>//共享内存函数
#include <sys/sem.h>//信号量
#include <sys/msg.h>//消息队列
#define BUFSZ 256 //缓冲区大小

//建立或获取 ipc 的一组函数的声明(库中内置的函数)
int get_ipc_id(char *proc_file,key_t key); //获得IPC对象的标识符(IPC对象就是实现进程通信对象,例如共享内存、消息队列等)
char *set_shm(key_t shm_key,int shm_num,int shm_flag); //创建共享内存
int set_msq(key_t msq_key,int msq_flag); //创建消息队列
int set_sem(key_t sem_key,int sem_val,int sem_flag); //创建信号量
int down(int sem_id); //wait函数(消耗资源)
int up(int sem_id); //signal函数(释放资源)

/*信号量类型*/
typedef union semuns
{
    int val;
} Sem_uns;
/*信息类型*/
typedef struct msgbuf
{
    long mtype;
    char mtext[1];
} Msg_buf;


头文件实现 

#include "ipc.h"
#include <sys/types.h> 
#include <unistd.h>

/*生产、消费者共享缓冲区的有关变量*/
key_t buff_key; //共享内存的关键字
int buff_num; //共享内存的大小
char *buff_ptr; //共享内存的指针,用于访问共享内存中的数据

/*生产者放产品位置的共享指针*/
key_t pput_key; //创建生产者放置产品位置的共享内存段的关键字
int pput_num; //记录生产者放置产品位置空间的大小
int *pput_ptr; //指向了共享内存段中用于记录生产者放置产品位置

/*消费者取产品位置的共享指针*/
key_t cget_key;
int cget_num;
int *cget_ptr;

//消费者的同步信号量标识与信号量本身
key_t ab_key;
key_t ac_key;
key_t bc_key;
int ab_int;
int ac_int;
int bc_int;
//缓冲区限额信号量标识与信号量本身
key_t full_key;
int full_int;
//生产者的同步信号量标识与信号量本身
key_t produce_key;
int produce_int;
//信号量的控制变量
int sem_val;
int sem_flg;
int shm_flg;

/*
 * get_ipc_id() 从/proc/sysvipc/文件系统中获取 IPC 的 id 号
 * pfile: 对应/proc/sysvipc/目录中的 IPC 文件分别为
 * msg-消息队列,sem-信号量,shm-共享内存
 * key: 对应要获取的 IPC 的 id 号的键值
*/
int get_ipc_id(char *proc_file,key_t key)
{
    FILE *pf;
    int i,j;
    char line[BUFSZ],colum[BUFSZ];
    if((pf = fopen(proc_file,"r")) == NULL)
    {
        perror("Proc file not open");
        exit(EXIT_FAILURE);
    }
    fgets(line, BUFSZ,pf);
    while(!feof(pf))
    {
        i = j = 0;
        fgets(line, BUFSZ,pf);
        while(line[i] == ' ')
            i++;
        while(line[i] !=' ')
            colum[j++] = line[i++];
        colum[j] = '\0';
        if(atoi(colum) != key)
            continue;
        j=0;
        while(line[i] == ' ')
            i++;
        while(line[i] !=' ')
            colum[j++] = line[i++];
        colum[j] = '\0';
        i = atoi(colum);
        fclose(pf);
        return i;
    }
    fclose(pf);
    return -1;
}
/*信号量上的消耗操作*/
int down(int sem_id)
{
    struct sembuf buf; //信号量结构体
    buf.sem_op = -1; //执行信号量-1
    buf.sem_num = 0;
    buf.sem_flg = SEM_UNDO;
    if((semop(sem_id,&buf,1)) <0)
    {
        perror("down error ");
        exit(EXIT_FAILURE);
    }
    return EXIT_SUCCESS;
}
/*信号量上的生产操作*/
int up(int sem_id)
{
    struct sembuf buf;
    buf.sem_op = 1;
    buf.sem_num = 0;
    buf.sem_flg = SEM_UNDO;
    if((semop(sem_id, &buf, 1)) < 0) {
        perror("up error ");
        exit(EXIT_FAILURE);
    }
    return EXIT_SUCCESS;
}

/*信号量的创建函数(创建+给予初值)*/
int set_sem(key_t sem_key,int sem_val,int sem_flg)
{
    int sem_id;
    Sem_uns sem_arg;
//sem_key 是创建或获取信号量集时使用的键值,用来指定要什么类型的信号量。而 sem_id 是系统返回的用于标识信号量集的标识符。
//sem_key 是在程序中指定的,而 sem_id 是系统分配的
    if((sem_id = get_ipc_id("/proc/sysvipc/sem",sem_key)) < 0 )
    {
//semget 新建一个信号灯,其标号返回到 sem_id
        if((sem_id = semget(sem_key,1,sem_flg)) < 0)
        {
            perror("semaphore create error");
            exit(EXIT_FAILURE);
        }
//设置信号灯的初值
        sem_arg.val = sem_val;
        if(semctl(sem_id,0,SETVAL,sem_arg) <0)
        {
            perror("semaphore set error");
            exit(EXIT_FAILURE);
        }
    }
    return sem_id;
}
/*
* set_shm 函数建立一个具有 n 个字节 的共享内存区
* 如果建立成功,返回一个指向该内存区首地址的指针shm_buf;如果shm_key已经有共享内存则将该内存绑定给指针shm_buf
* 输入参数:
* shm_key 共享内存的键值
* shm_val 共享内存字节的长度
* shm_flag 共享内存的存取权限
*/
char* set_shm(key_t shm_key,int shm_num,int shm_flg)
{
    int i,shm_id;
    char* shm_buf;
//测试由 shm_key 标识的共享内存区是否已经建立,存在则返回标识符,否则返回负值
    if((shm_id = get_ipc_id("/proc/sysvipc/shm",shm_key)) < 0 )
    {
//shmget 新建 一个长度为 shm_num 字节的共享内存,其标号返回到 shm_id
        if((shm_id = shmget(shm_key,shm_num,shm_flg)) <0)
        {
            perror("shareMemory set error");
            exit(EXIT_FAILURE);
        }
//shmat 将由 shm_id 标识的共享内存附加给指针 shm_buf
        if((shm_buf = (char *)shmat(shm_id,0,0)) < (char *)0)
        {
            perror("get shareMemory error");
            exit(EXIT_FAILURE);
        }
//清空共享内存
        for(i=0; i<shm_num; i++)
            shm_buf[i] = 0; //初始为 0
    }
//shm_key 标识的共享内存区已经建立,将由 shm_id 标识的共享内存附加给指针 shm_buf
    if((shm_buf = (char *)shmat(shm_id,0,0)) < (char *)0)
    {
        perror("get shareMemory error");
        exit(EXIT_FAILURE);
    }
    return shm_buf;
}
/*
* set_msq 函数建立一个消息队列
* 如果建立成功,返回 一个消息队列的标识符 msq_id
* 输入参数:
* msq_key 消息队列的键值
* msq_flag 消息队列的存取权限
*/
int set_msq(key_t msq_key,int msq_flg)
{
    int msq_id;
//测试由 msq_key 标识的消息队列是否已经建立
    if((msq_id = get_ipc_id("/proc/sysvipc/msg",msq_key)) < 0 )
    {
//msgget 新建一个消息队列,其标号返回到 msq_id
        if((msq_id = msgget(msq_key,msq_flg)) < 0)
        {
            perror("messageQueue set error");
            exit(EXIT_FAILURE);
        }
    }
    return msq_id;
}

核心代码文件 (各类进程)

生产者

/*生产者1*/
#include "ipc.h"
#include <unistd.h>
#include <sys/types.h>

/*生产、消费者共享缓冲区的有关变量*/
extern key_t buff_key; //共享内存的关键字
extern int buff_num; //共享内存的大小
extern char *buff_ptr; //共享内存的指针,用于访问共享内存中的数据

/*生产者放产品位置的共享指针*/
extern key_t pput_key; //创建生产者放置产品位置的共享内存段的关键字
extern int pput_num; //记录生产者放置产品位置空间的大小
extern int *pput_ptr; //指向了共享内存段中用于记录生产者放置产品位置

/*消费者取产品位置的共享指针*/
extern key_t cget_key;
extern int cget_num;
extern int *cget_ptr;

//消费者的同步信号量标识与信号量本身
extern key_t ab_key;
extern key_t ac_key;
extern key_t bc_key;
extern int ab_int;
extern int ac_int;
extern int bc_int;
//缓冲区限额信号量标识与信号量本身
extern key_t full_key;
extern int full_int;
//生产者的同步信号量标识与信号量本身
extern key_t produce_key;
extern int produce_int;
//信号量的控制变量
extern int sem_val;
extern int sem_flg;
extern int shm_flg;

int main(int argc,char *argv[])
{
    int rate;
//可在在命令行第一参数指定一个进程睡眠秒数,以调解进程执行速度
    if(argv[1] != NULL)
        rate = atoi(argv[1]);
    else
        rate = 1; //不指定为 1 秒

/*获取共享内存信息*/
//获取缓冲区使用的共享内存
    buff_key = 101; //缓冲区任给的键值
    buff_num = 8; //缓冲区任给的长度
    shm_flg = IPC_CREAT | 0644; //共享内存读写权限
    buff_ptr = (char *)set_shm(buff_key,buff_num,shm_flg);

/*获取生产者共享的生产信息*/
//获取生产者放产品位置指针 pput_ptr
    pput_key = 102;//生产者放产品指针的键值
    pput_num = 1; //指针数
    shm_flg = IPC_CREAT | 0644;//共享内存读写权限
    pput_ptr = (int *)set_shm(pput_key,pput_num,shm_flg);

/*获取Buffer问题的限额信号量*/
//获取限额信号量
    full_key = 208;//对一个缓冲区的控制键值
    sem_val = buff_num;
    sem_flg = IPC_CREAT | 0644;
    full_int = set_sem(full_key,sem_val,sem_flg);

/*获取produce间的互斥信号量*/
//获取两个produce之间的互斥信号量
    produce_key = 205;//对两个生产者的同步键值
    sem_val = 1;//生产者互斥信号灯初值为 1
    sem_flg = IPC_CREAT | 0644;
    produce_int = set_sem(produce_key,sem_val,sem_flg);

/*获取produce与三个消费者之间的同步信号量*/
    ab_key = 201;//有C的消费者控制键值
    ac_key = 202;//有B的消费者控制键值
    bc_key = 203;//有A的消费者控制键值
    sem_flg = IPC_CREAT | 0644;
    sem_val = 0;//消费者初始无产品可取,同步信号灯初值设为 0
    ab_int = set_sem(ab_key,sem_val,sem_flg);
    ac_int = set_sem(ac_key,sem_val,sem_flg);
    bc_int = set_sem(bc_key,sem_val,sem_flg);
    int d=*pput_ptr;

/*开始运行核心程序*/ 
    while(1){
        down(full_int);
        down(produce_int);
        buff_ptr[*pput_ptr] = 'A'+ *pput_ptr; //共享数据量利用存入共享内存机制实现
        
        sleep(rate);
	if(*pput_ptr==0)
            printf("生产者%d把烟草和纸放入[%d]缓存区\n",getpid(),d);
        else if(*pput_ptr==1)
            printf("生产者%d把胶水和纸放入[%d]缓存区\n",getpid(),d);
        else
            printf("生产者%d把烟草和胶水放入[%d]缓存区\n",getpid(),d);
        *pput_ptr = (*pput_ptr+1) % 3; 
        d = (d+1) % 8; 
        up(produce_int);
        if(*pput_ptr==0)
            up(bc_int);
        else if(*pput_ptr==1)
            up(ac_int);
        else
            up(ab_int);
        sleep(rate);
    }
    return EXIT_SUCCESS;
}


抽烟者A 

/*抽烟者A,需要烟草和纸,对应同步信号量为bc*/

#include "ipc.h"
#include <sys/types.h> 
#include <unistd.h>

/*生产、消费者共享缓冲区的有关变量*/
extern key_t buff_key; //共享内存的关键字
extern int buff_num; //共享内存的大小
extern char *buff_ptr; //共享内存的指针,用于访问共享内存中的数据

/*生产者放产品位置的共享指针*/
extern key_t pput_key; //创建生产者放置产品位置的共享内存段的关键字
extern int pput_num; //记录生产者放置产品位置空间的大小
extern int *pput_ptr; //指向了共享内存段中用于记录生产者放置产品位置

/*消费者取产品位置的共享指针*/
extern key_t cget_key;
extern int cget_num;
extern int *cget_ptr;

//消费者的同步信号量标识与信号量本身
extern key_t ab_key;
extern key_t ac_key;
extern key_t bc_key;
extern int ab_int;
extern int ac_int;
extern int bc_int;
//缓冲区限额信号量标识与信号量本身
extern key_t full_key;
extern int full_int;
//生产者的同步信号量标识与信号量本身
extern key_t produce_key;
extern int produce_int;
//信号量的控制变量
extern int sem_val;
extern int sem_flg;
extern int shm_flg;

int main(int argc,char *argv[])
{
    int rate;
//可在在命令行第一参数指定一个进程睡眠秒数,以调解进程执行速度
    if(argv[1] != NULL)
        rate = atoi(argv[1]);
    else
        rate = 1; //不指定为 1 秒

/*获取共享内存信息*/
//获取缓冲区使用的共享内存
    buff_key = 101; //缓冲区任给的键值
    buff_num = 8; //缓冲区任给的长度
    shm_flg = IPC_CREAT | 0644; //共享内存读写权限
    buff_ptr = (char *)set_shm(buff_key,buff_num,shm_flg);

/*获取消费者共享的消费信息*/
//获取消费者共享取产品指针
    cget_key = 103; //消费者取产品指针的键值
    cget_num = 1; //指针数
    shm_flg = IPC_CREAT | 0644; //共享内存读写权限
    cget_ptr = (int *)set_shm(cget_key,cget_num,shm_flg);

/*获取Buffer问题的限额信号量*/
//获取限额信号量
    full_key = 208;//对一个缓冲区的控制键值
    sem_val = buff_num;
    sem_flg = IPC_CREAT | 0644;
    full_int = set_sem(full_key,sem_val,sem_flg);

/*获取同步信号量*/
//同步控制的bc信号量,同时也由这个信号量实现消费者之间的互斥
    bc_key = 203;//有A的消费者控制键值
    sem_flg = IPC_CREAT | 0644;
    sem_val = 0;
    bc_int = set_sem(bc_key,sem_val,sem_flg);

    while(1)
    {
        down(bc_int);
        sleep(rate);
        printf("%d  1号吸烟者得到了:烟草和纸[%d]%c\n",getpid(),*cget_ptr ,buff_ptr[*cget_ptr]);
        *cget_ptr = (*cget_ptr+1) % buff_num;
        up(full_int);
    }
    return EXIT_SUCCESS;
}


抽烟者B 

/*抽烟者B,需要胶水和纸,对应同步信号量为ac*/

#include "ipc.h"
#include <sys/types.h> 
#include <unistd.h>

/*生产、消费者共享缓冲区的有关变量*/
extern key_t buff_key; //共享内存的关键字
extern int buff_num; //共享内存的大小
extern char *buff_ptr; //共享内存的指针,用于访问共享内存中的数据

/*生产者放产品位置的共享指针*/
extern key_t pput_key; //创建生产者放置产品位置的共享内存段的关键字
extern int pput_num; //记录生产者放置产品位置空间的大小
extern int *pput_ptr; //指向了共享内存段中用于记录生产者放置产品位置

/*消费者取产品位置的共享指针*/
extern key_t cget_key;
extern int cget_num;
extern int *cget_ptr;

//消费者的同步信号量标识与信号量本身
extern key_t ab_key;
extern key_t ac_key;
extern key_t bc_key;
extern int ab_int;
extern int ac_int;
extern int bc_int;
//缓冲区限额信号量标识与信号量本身
extern key_t full_key;
extern int full_int;
//生产者的同步信号量标识与信号量本身
extern key_t produce_key;
extern int produce_int;
//信号量的控制变量
extern int sem_val;
extern int sem_flg;
extern int shm_flg;

int main(int argc,char *argv[])
{
    int rate;
//可在在命令行第一参数指定一个进程睡眠秒数,以调解进程执行速度
    if(argv[1] != NULL)
        rate = atoi(argv[1]);
    else
        rate = 1; //不指定为 1 秒

/*获取共享内存信息*/
//获取缓冲区使用的共享内存
    buff_key = 101; //缓冲区任给的键值
    buff_num = 8; //缓冲区任给的长度
    shm_flg = IPC_CREAT | 0644; //共享内存读写权限
    buff_ptr = (char *)set_shm(buff_key,buff_num,shm_flg);

/*获取消费者共享的消费信息*/
//获取消费者共享取产品指针
    cget_key = 103; //消费者取产品指针的键值
    cget_num = 1; //指针数
    shm_flg = IPC_CREAT | 0644; //共享内存读写权限
    cget_ptr = (int *)set_shm(cget_key,cget_num,shm_flg);

/*获取Buffer问题的限额信号量*/
//获取限额信号量
    full_key = 208;//对一个缓冲区的控制键值
    sem_val = buff_num;
    sem_flg = IPC_CREAT | 0644;
    full_int = set_sem(full_key,sem_val,sem_flg);

/*获取同步信号量*/
//同步控制的bc信号量,同时也由这个信号量实现消费者之间的互斥
    ac_key = 202;//有A的消费者控制键值
    sem_flg = IPC_CREAT | 0644;
    sem_val = 0;
    ac_int = set_sem(ac_key,sem_val,sem_flg);

    while(1)
    {
        down(ac_int);
        sleep(rate);
        printf("%d  2号吸烟者得到了:胶水和纸[%d]%c\n",getpid(),*cget_ptr ,buff_ptr[*cget_ptr]);
        *cget_ptr = (*cget_ptr+1) % buff_num;
        up(full_int);
    }
    return EXIT_SUCCESS;
}


抽烟者C 

/*抽烟者C,需要胶水和烟卷,对应同步信号量为ab*/

#include "ipc.h"
#include <sys/types.h> 
#include <unistd.h>

/*生产、消费者共享缓冲区的有关变量*/
extern key_t buff_key; //共享内存的关键字
extern int buff_num; //共享内存的大小
extern char *buff_ptr; //共享内存的指针,用于访问共享内存中的数据

/*生产者放产品位置的共享指针*/
extern key_t pput_key; //创建生产者放置产品位置的共享内存段的关键字
extern int pput_num; //记录生产者放置产品位置空间的大小
extern int *pput_ptr; //指向了共享内存段中用于记录生产者放置产品位置

/*消费者取产品位置的共享指针*/
extern key_t cget_key;
extern int cget_num;
extern int *cget_ptr;

//消费者的同步信号量标识与信号量本身
extern key_t ab_key;
extern key_t ac_key;
extern key_t bc_key;
extern int ab_int;
extern int ac_int;
extern int bc_int;
//缓冲区限额信号量标识与信号量本身
extern key_t full_key;
extern int full_int;
//生产者的同步信号量标识与信号量本身
extern key_t produce_key;
extern int produce_int;
//信号量的控制变量
extern int sem_val;
extern int sem_flg;
extern int shm_flg;

int main(int argc,char *argv[])
{
    int rate;
//可在在命令行第一参数指定一个进程睡眠秒数,以调解进程执行速度
    if(argv[1] != NULL)
        rate = atoi(argv[1]);
    else
        rate = 1; //不指定为 1 秒

/*获取共享内存信息*/
//获取缓冲区使用的共享内存
    buff_key = 101; //缓冲区任给的键值
    buff_num = 8; //缓冲区任给的长度
    shm_flg = IPC_CREAT | 0644; //共享内存读写权限
    buff_ptr = (char *)set_shm(buff_key,buff_num,shm_flg);

/*获取消费者共享的消费信息*/
//获取消费者共享取产品指针
    cget_key = 103; //消费者取产品指针的键值
    cget_num = 1; //指针数
    shm_flg = IPC_CREAT | 0644; //共享内存读写权限
    cget_ptr = (int *)set_shm(cget_key,cget_num,shm_flg);

/*获取Buffer问题的限额信号量*/
//获取限额信号量
    full_key = 208;//对一个缓冲区的控制键值
    sem_val = buff_num;
    sem_flg = IPC_CREAT | 0644;
    full_int = set_sem(full_key,sem_val,sem_flg);

/*获取同步信号量*/
//同步控制的bc信号量,同时也由这个信号量实现消费者之间的互斥
    ab_key = 201;//有A的消费者控制键值
    sem_flg = IPC_CREAT | 0644;
    sem_val = 0;
    ab_int = set_sem(ab_key,sem_val,sem_flg);

    while(1)
    {
        down(ab_int);
        sleep(rate);
        printf("%d  3号吸烟者得到了:胶水和烟卷[%d]%c\n",getpid(),*cget_ptr ,buff_ptr[*cget_ptr]);
        *cget_ptr = (*cget_ptr+1) % buff_num;
        up(full_int);
    }
    return EXIT_SUCCESS;
}


由于两个生产者的代码是完全相同的,只不过是利用两个进程去分别运行。所以这边只需要编写一个程序,利用两个终端(两个进程)去运行同一段程序即可 

本程序调试过程中出现的两个bug:

一、一开始将变量的定义放在了头文件中,然后同时编译运行ipc.c和producer1.c两个程序。这两个程序都include头文件,所以造成了变量的重定义 

二、full_int变量更名为empty_int变量更加合适,标识缓冲区还有的空余位置。程序一开始我设定的full_key为204,出现的结果就是这个信号量创建失败,返回的full_int值为0。出现这种情况的原因是204的信号量key值,在我的window11系统上是无权访问的。

如果大家出现进程直接阻塞的情况,可以print自己的信号量看看值是否正确。如果没有正确创建信号量,可能是key值权限的问题

makefile文件

hdrs = ipc.h
opts = -g -c -lrt
ipc_src = ipc.c
ipc_obj = ipc.o
p_src = producer1.c
p_obj = producer1.o
a_src = haveA.c
a_obj = haveA.o
b_src = haveB.c
b_obj = haveB.o
c_src = haveC.c
c_obj = haveC.o

all: producer1 haveA haveB haveC

producer1: $(p_obj) $(ipc_obj)
	gcc $(p_obj) $(ipc_obj) -o producer1

$(p_obj): $(p_src) $(hdrs)
	gcc $(opts) $(p_src) -o $(p_obj)

haveA: $(a_obj) $(ipc_obj)
	gcc $(a_obj) $(ipc_obj) -o haveA

$(a_obj): $(a_src) $(hdrs)
	gcc $(opts) $(a_src) -o $(a_obj)

haveB: $(b_obj) $(ipc_obj)
	gcc $(b_obj) $(ipc_obj) -o haveB

$(b_obj): $(b_src) $(hdrs)
	gcc $(opts) $(b_src) -o $(b_obj)

haveC: $(c_obj) $(ipc_obj)
	gcc $(c_obj) $(ipc_obj) -o haveC

$(c_obj): $(c_src) $(hdrs)
	gcc $(opts) $(c_src) -o $(c_obj)

ipc.o: ipc.c $(hdrs)
	gcc $(opts) ipc.c -o ipc.o

clean:
	rm -f *.o producer1 haveA haveB haveC

注意!各个文件的命名需要和makefile中对应 

实验运行 

运行结果分析

1、首先运行第一个producer程序,生产者进程开始向共享资源区放置资源。由于设置的共享资源区大小为8,所以在放了8个资源后,生产者进程阻塞。

2、运行第二个producer程序,由于共享资源区已经满了,所以直接阻塞

3、运行haveA、haveB、haveC程序,轮流被CPU调度。在共享资源区通过共享的消费者指针去获取自己想要的资源。所以5个进程就能够不停的生产、消费着运行 

总结

本文到这里就结束啦~~

本篇文章重点在于利用linux系统的完成操作系统的实验,巩固课堂知识

本篇文章的撰写+实验代码调试运行+知识点细致化学习,共花了本人5h左右的时间

如果仍有不够希望大家多多包涵~~如果觉得对你有帮助,辛苦友友点个赞哦~

知识来源:山东大学操作系统实验四、山东大学《操作系统原理实用实验教程》张鸿烈老师编著

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/648794.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

nginx与nginx-rtmp-module安装

nginx与nginx-rtmp-module安装 画了好几天图&#xff0c;实在有些乏力&#xff0c;找点有意思的事情做做 觉得视频流传输挺有意思&#xff0c;B站找了些视频&#xff0c;但感觉有些大同小异&#xff0c;讲得不是很清楚 FFmpeg/RTMP/webRTC丨90分钟搞定直播逻辑-推流-流媒体服…

面向可复用性和可维护性的设计模式 课程学习总结

什么是设计模式 设计模式&#xff1a;在软件设计中给定上下文中常见问题的通用的、可重用的解决方案。 设计模式分类 1. 创建型模式——Creational patterns 关注对象创建的过程 1.1 工厂方法模式 定义用于创建对象的接口&#xff0c;但让子类决定要实例化哪个类。工厂方…

舞蹈工作室会员服务预约门店管理系统小程序的作用是什么

舞蹈涵盖少儿、街舞、芭蕾、拉丁等多个细分类目&#xff0c;舞蹈工作室除了商演外&#xff0c;内部还有学员培训教育等&#xff0c;提高营收和提升服务效率是商家一直需要思考的问题&#xff0c;线上化程度加深&#xff0c;需要满足客户个性化需求且快速完成流程。 运用【雨科…

乐游巴蜀,V你而来!苏州金龙海格新V系很“巴适”

成都&#xff0c;自古有“天府之国”之美誉&#xff0c;古老的城市人文与现代的摩登活力相交相融&#xff0c;加之令人垂涎的美食文化&#xff0c;共同造就了这里超强的旅游吸引力。2024年5月23日&#xff0c;以“用心前行&#xff0c;V你而来”为题的苏州金龙新V系客车推介会走…

基于若依的旅游推荐管理系统(spring boot+vue+mybatis+Ajax)

目录 一、项目目的 二、项目需求 1、功能模块分析 2、数据库表er图 三、部分界面展示 1、景点信息 2、旅游路线 3、地方美食管理 四、新颖点 1、旅游路线、景点和美食的联系 2、联系实现 3、地级选择器&#xff08;省市二级&#xff09; 五、总结 一、项目目的 随着…

数据恢复的救星!快速恢复手机数据的2个秘籍!

当我们的照片、视频、联系人、短信和应用程序丢失时&#xff0c;许多人可能会感到束手无策&#xff0c;无论是珍贵的照片、重要的工作文件还是个人的联系方式&#xff0c;一旦丢失&#xff0c;都可能带来极大的不便和困扰。但随着数据恢复技术的发展&#xff0c;我们有了更多的…

Spring - Spring Cache 缓存注解这样用,实在是太香了!

作者最近在开发公司项目时使用到 Redis 缓存&#xff0c;并在翻看前人代码时&#xff0c;看到了一种关于 Cacheable 注解的自定义缓存有效期的解决方案&#xff0c;感觉比较实用&#xff0c;因此作者自己拓展完善了一番后分享给各位。 Spring 缓存常规配置 Spring Cache 框架给…

Linux 基本使用和 web 程序部署云端

目录 1.Linux发行版 2.Linux常用命令 ls pwd cd touch mkdir cat rm cp mv man vim grep ps netstat 绝对路径 vs 相对路径 使用 tab 键补全 使用 ctrl c 重新输入 粘贴与复制快捷键 3.Linux环境搭建 环境搭建方式 使用云服务器 4.搭建Java部署环境 …

一步步实现知乎热榜采集:Scala与Sttp库的应用

背景 在大数据时代&#xff0c;网络爬虫技术发挥着不可或缺的作用。它不仅能够帮助我们快速地获取互联网上的信息&#xff0c;还能处理和分析这些数据&#xff0c;为我们提供深刻的洞察。知乎&#xff0c;作为中国领先的问答社区&#xff0c;汇聚了各行各业的专家和广大用户的…

30多万汉字词语押韵查询ACCESS\EXCEL数据库

押韵&#xff0c;也作“压韵”。作诗词曲赋等韵文时在句末或联末用同韵的字相押&#xff0c;称为押韵。诗歌押韵&#xff0c;使作品声韵和谐&#xff0c;便于吟诵和记忆&#xff0c;具有节奏和声调美。旧时押韵&#xff0c;要求韵部相同或相通&#xff0c;也有少数变格。现代新…

洪师傅代驾系统开发 支持公众号H5小程序APP 后端Java源码

代驾流程图 业务流程图 管理端设置 1、首页装修 2、师傅奖励配置 师傅注册后,可享受后台设置的新师傅可得的额外奖励; 例:A注册了师傅,新人奖励可享受3天,第一天的第一笔订单完成后可得正常佣金佣金*奖励比例 完成第二笔/第三笔后依次可得正常佣金佣金*奖励比例 完成的第四…

百川大模型拿下国产第一,AI助手「百小应」上线,比Kimi强不少

最近几天&#xff0c;国内 AI 创业公司正在连续刷新大模型的能力上限。 5 月 22 日&#xff0c;百川智能发布最新一代基座大模型 Baichuan 4&#xff0c;同时推出了首款 AI 助手「百小应」。 相较 1 月份发布的 Baichuan 3&#xff0c;新一代模型在各项能力上均有大幅提升&am…

三轴加速度计M-A352AD实现实时的动态监测

地震监测设备如何快速监测到地震波的发生?如何快速地将地震信号传输到系统或设备上&#xff0c;让人快速做出相应对策?如何在恶劣的环境下&#xff0c;仍能保持稳定可靠的监测?其核心之一就是采用了传感器技术和相关设备&#xff0c;我们可以在地震易发生区域或重点观察的区…

最早做“转化医学”的国货护肤品牌,发力了!

文章来自化妆品行业媒体青眼 作者小朱 放眼全球护肤市场&#xff0c;皮肤科学的力量正在前所未有地凸显&#xff0c;多个国际美妆巨头专门设立了皮肤科学部门&#xff0c;国内皮肤科医生参与护肤品牌创建也成为一股风潮。 据青眼不完全统计&#xff0c;近年来&#xff0c;至少…

UI控件与视图层次:探索界面的无限可能

[OC]UI学习笔记 文章目录 [OC]UI学习笔记视图和视图层次结构CGRectUILabelUIButtonUIView控件UIView的层级关系UIWindow定时器和视图移动UISwitch进度条和滑动条控件步进器和分栏控件警告对话框与等待指示器UITextField 视图和视图层次结构 Objective-C中的UI编程主要围绕视图…

WebGL在历史和考古重建中的应用

WebGL&#xff08;Web Graphics Library&#xff09;是一种基于JavaScript的API&#xff0c;用于在浏览器中呈现2D和3D图形。由于其强大的图形处理能力和广泛的兼容性&#xff0c;WebGL在历史和考古重建中的应用具有重要的意义。以下是WebGL在这一领域的主要应用和详细描述。北…

十大品牌落地台灯有用吗?护眼落地灯十大知名品牌

十大品牌落地台灯有用吗&#xff1f;落地台灯作为这几年家长很关注的家电/学生产品&#xff0c;家里有孩子或者是经常面对电子设备的人士&#xff0c;相信都会对其有所了解并且购买了落地台灯&#xff0c;但是还有些家长对落地台灯的认知不够深&#xff0c;以至于还没有给孩子安…

JUC框架(Semaphore、CountDownLatch、CyclicBarrier)

文章目录 Semaphore(信号量)Semaphore介绍Semaphore基本概念Semaphore使用场景Semaphore示例 CountDownLatch &#xff08;计数器/闭锁&#xff09;CountDownLatch 介绍CountDownLatch 基本概念CountDownLatch 使用场景CountDownLatch 基本方法CountDownLatch 示例 CyclicBarri…

用PhpStudy在本地电脑搭建WordPress网站教程(2024版)

对新手来说&#xff0c;明白了建站3要素后&#xff0c;如果直接购买域名、空间去建站&#xff0c;因为不熟练&#xff0c;反复测试主题、框架、插件等费时费力&#xff0c;等网站建成可能要两三个月&#xff0c;白白损失这段时间的建站费用。那么新手怎么建测试网站来练手呢&am…

Redis使用Set实现点赞功能

文章目录 set 数据类型介绍不排序实现排序实现 set 数据类型介绍 Redis中的set类型是一组无序的字符串值。 set通过其独特的数据结构和丰富的命令提供了在存储和处理集合元素方面的一些非常有用的功能。下面列出了主要的set类型命令&#xff1a; SADD key member1 [member2]&a…