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

news2024/11/15 9:08:59

目录

实验目的

前提知识

实验题目

题目分析 

 实验程序

头文件

头文件实现 

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

生产者

抽烟者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左右的时间

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学浪视频怎么下载保存到本地

你是否曾经因为想要保存一份珍贵的学浪视频却苦于无法下载而感到烦恼&#xff1f;现在&#xff0c;我将向你揭示一个简单易行的方法&#xff0c;让你轻松地将学浪视频保存到本地&#xff0c;随时随地享受学习的乐趣。你是否曾经因为想要保存一份珍贵的学浪视频却苦于无法下载而…

大模型的发展方向:让大模型感知人类所处的物理世界,文字、听、看、闻、触摸、动手操作等信息接收和输出能力,向物理世界学习 大模型开发者方向

大模型的发展方向非常广泛&#xff0c;除了让大模型感知人类所处的物理世界&#xff0c;通过文字、听觉、视觉、嗅觉、触觉和动手操作等信息接收能力&#xff0c;还包括以下几个重要的方向&#xff1a; 多模态学习与融合&#xff1a; 多模态感知&#xff1a;整合来自不同感知渠…

【Linux】TCP的三次握手和四次挥手

三次握手 在TCP/IP协议中&#xff0c;TCP协议提供可靠的连接服务&#xff0c;采用三次握手建立一个连接。注意&#xff01;三次握手只是用来建立连接用的&#xff0c;和TCP可靠稳定没有关系&#xff0c;TCP的可靠是通过重传和检错等机制实现的。 默认创建一个socket后&#xff…

瑜伽馆约课会员管理系统小程序的作用是什么

瑜伽馆有着众多学员&#xff0c;如瘦身、改变气质、减脂塑形等往往属于长期多次跟随教练学习&#xff0c;或是自己在家里学习等&#xff0c;对商家来说&#xff0c;品牌宣传、吸引客户到店以及长期经营、提高自身服务效率是重中之重。 客户多次进店享受服务的同时还需要悦己&a…

深入理解SVM和浅层机器学习算法的训练机制

深入理解SVM和浅层机器学习算法的训练机制支持向量机&#xff08;SVM&#xff09;的训练过程SVM的基本概念SVM的损失函数训练方法 浅层机器学习算法的训练机制决策树K-最近邻&#xff08;K-NN&#xff09;朴素贝叶斯 结论 深入理解SVM和浅层机器学习算法的训练机制 在探讨浅层…

悲痛都会过去,唯有当下值得珍惜

在生活的长河中&#xff0c;我们都会经历各种各样的悲痛与挫折&#xff0c;无论是来自原生家庭的困扰&#xff0c;婚姻中的曲折&#xff0c;还是小时候的创伤、男女关系中的纠葛、校园时期的霸凌。然而&#xff0c;当我们回首过去&#xff0c;曾经以为无法逾越的痛苦&#xff0…

javaEE—图书管理系统(基础代码版)

前言&#xff1a; 本篇博客是集合了javaEE所学的知识构建的一个基础框架&#xff0c;讲述着面向对象的过程是如何做到多对象交互协作完成框架的构建的。利用了数组&#xff0c;接口&#xff0c;类和对象&#xff0c;抽象类&#xff0c;Object类等知识来完成。 后续会加入数据…

记一次绕过宝塔防火墙的BC站渗透

0x00 信息收集 由于主站存在云waf 一测就封 且初步测试不存在能用得上的洞 所以转战分站 希望能通过分站获得有价值的信息 这是一个查询代理帐号的站 url输入admin 自动跳转至后台 看这个参数 猜测可能是thinkCMF 0x01 getshell thinkcmf正好有一个RCE 可以尝试一下 ?afetc…

[HDCTF 2023]爬过小山去看云(HILL密码,云影密码)

题目&#xff1a; 我们看到给出了矩阵[3 4 19 11] 1:利用在线工具进行解码Practical Cryptography 2&#xff1a;解码完成后所得结果翻译之后是数字&#xff0c;提取后842084210884024084010124&#xff0c;看到只含有01248便猜测时云影密码&#xff0c;利用脚本进行解密。 …

惠海 H6901B升压恒流3.7V 7.4V 12V 24V 30V 36V 48V 60V 80V 100V调光无频闪细腻顺滑

H6901B是一款升压型LED恒流驱动芯片&#xff0c;具有良好稳定性的特点。H6901B的主要特点包括宽输入电压范围&#xff08;2.7V-100V&#xff09;、高工作频率&#xff08;1MHz&#xff09;以及多种保护功能&#xff08;如芯片供电欠压保护、过温保护、软启动等&#xff09;。此…

如何判断自己的情商高低?

什么是情商&#xff1f; 情商&#xff08;简称为EQ&#xff09;&#xff0c;也叫情绪智力&#xff0c;和我们通常提到的智商智力有所不同&#xff08;侧重于理性思维&#xff09;&#xff0c;情商更贴近实际生活&#xff0c;如&#xff1a;情绪识别和自我管理&#xff0c;自我…

内网穿透--Ngrok-入门-上线

免责声明:本文仅做技术交流与学习... 目录 Ngrok: 技术实现: 前提: 命令: 详细流程及图解: 平台Ngrok: Sunny-Ngrok内网转发内网穿透 - 国内内网映射服务器 支持的协议&#xff1a;tcp、http、https 支持的类型&#xff1a;正向代理、反向代理 --隧道开通免费的 --协议…

ROS2入门21讲__第07讲__节点:机器人的工作细胞

目录 前言 通信模型 案例一&#xff1a;Hello World节点&#xff08;面向过程&#xff09; 运行效果 代码解析 创建节点流程 案例二&#xff1a;Hello World节点&#xff08;面向对象&#xff09; 运行效果 代码解析 创建节点流程 案例三&#xff1a;物体识别节点 …

xml篇---提取VOC格式的坐标,并按照cameraID进行排序(二)

xml篇—提取VOC格式的坐标&#xff0c;并按照cameraID进行排序&#xff08;二&#xff09; import os import xml.etree.ElementTree as ETdef parse_xml(xml_file):tree ET.parse(xml_file)root tree.getroot()objects {}for obj in root.findall(object):name obj.find(…

电商API接口:供应商价格与主流电商平台价格做比价

品牌在进行采购工作时&#xff0c;将供应商提供的价格与主流电商平台上的公开价格进行比价是一种非常常见的做法&#xff0c;这样做的目的主要是为了保证自身供应商提供的价格具有竞争力和合理性&#xff0c;从而更好地优化采购工作。 以下是过程中的具体步骤及一些注意事项&a…

是他将计算机从“一屋子”变成“一柜子”——量子前哨缅怀小型机之父 戈登·贝尔

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨浪味仙 排版丨沛贤 深度好文&#xff1a;6000字丨15分钟阅读 5 月 21 日&#xff0c; 美国贝尔实验室资深人士 John Mashey 发布消息称&#xff0c;计算机先驱戈登贝尔&#xff08;Gordon…

左极限与右极限

左极限与右极限 1. 前言 极限描述了函数在一个定点附近的行为&#xff0c;具体说就是当函数的自变量&#xff08;例如 x x x&#xff09;趋近于某一个值时&#xff0c;函数的因变量&#xff08;例如 y y y&#xff09;会产生什么样的特性&#xff08;或结果&#xff09;。 …

优于其他超导量子比特数千倍!猫态量子比特实现超过十秒的受控比特翻转时间

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨娴睿/慕一 排版丨沛贤 深度好文&#xff1a;2000字丨8分钟阅读 摘要&#xff1a;量子计算公司Alice & Bob和QUANTIC团队&#xff08;国立巴黎高等矿业学院PSL分校、巴黎高等师范学院和…

如何解决IT运维不给力

运维不给力&#xff0c;是很多企业IT部门面临的头疼问题&#xff0c;其背后的原因错综复杂&#xff0c;可能涉及到资金投入不足、团队积极性不高、或是缺乏科学的运维管理体系。要解决这些问题&#xff0c;引入IT运维管理和利用先进的ITILDESK平台&#xff0c;可以作为破局的关…

三、Servlet基础

注&#xff1a;因为我并不完全是为了从0开始Java开发&#xff0c;因此&#xff0c;我这里先暂时跳过第二章服务器环境相关的内容&#xff0c;直接开始第三章的内容。 3.1、Servlet 的基本结构&#xff1a; ​ 下面的代码给出了一个基本的 Servlet &#xff0c;它处理 GET 请求…