操作系统实战(四)(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)%3if(d==0) //三个同步信号量signal(bc_int)else if(d==1)signal(ac_int)elsesignal(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; //执行信号量-1buf.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_idif((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_idif((shm_id = shmget(shm_key,shm_num,shm_flg)) <0){perror("shareMemory set error");exit(EXIT_FAILURE);}
//shmat 将由 shm_id 标识的共享内存附加给指针 shm_bufif((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_bufif((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_idif((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]);elserate = 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_ptrpput_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;//生产者互斥信号灯初值为 1sem_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;//消费者初始无产品可取,同步信号灯初值设为 0ab_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);elseprintf("生产者%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);elseup(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]);elserate = 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]);elserate = 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]);elserate = 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.oall: producer1 haveA haveB haveCproducer1: $(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.oclean:rm -f *.o producer1 haveA haveB haveC

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

实验运行 

运行结果分析

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

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

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

总结

本文到这里就结束啦~~

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

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

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

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

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

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

相关文章

Jenkins配置(插件/角色/凭证)

目录 传送门前言一、Jenkins插件管理1、更换为国内下载源2、中文汉化插件下载&#xff08;不推荐&#xff09;3、低版本Jenkins爆红插件安装4、低版本Jenkins插件持续报错解决办法 二、Jenkins用户角色三、Jenkins凭证管理&#xff08;svn/git&#xff09;1、Username with pas…

NIO流(多路复用技术)

目录 什么是NIO使用场景 NIO(new IO)相关包路径NIO的实现基础NIO的核心组件Buffer缓冲区详解数据如何从磁盘读到用户进程 ChannelChannel的使用 其他组件字符集和Charset文件锁NIO工具类使用Files的FileVisitor遍历文件和目录使用WatchService监控文件变化访问文件属性 什么是N…

什么样的无线麦克风好?一文看懂哪种麦克风降噪效果好

作为视频创作者&#xff0c;拍摄视频除了要注意拍摄的画质外&#xff0c;声音的录制也很重要。声音录制的清晰度也会直接影响整个作品的整体水平&#xff0c;要想录的声音清晰&#xff0c;有专业级录制效果&#xff0c;必须选好麦克风&#xff0c;而无线领夹麦克风&#xff0c;…

【工具分享】Annabelle勒索病毒解密工具

前言 Annabelle勒索病毒灵感来自恐怖电影系列 Annabelle。除了文件加密功能外&#xff0c;Annabelle 勒索软件还会试图禁用防火墙&#xff0c;强制停止一系列正在运行程序&#xff0c;通过连接的 USB 驱动器进行传播。 特征 勒索内容&#xff1a; Annabelle 使用 AES256 CBC 加…

【Linux】线程同步和生产者-消费者模型

目录 一. 线程同步1. 条件变量2. 条件变量接口条件变量的创建及初始化条件变量的销毁条件变量等待条件变量唤醒 3. 条件变量同步解决抢占问题 二. 生产者-消费者模型1. 什么是生产者-消费者模型2. 为什么要使用生产者-消费者模型3. 生产者-消费者模型特点4. 基于阻塞队列实现生…

技术前沿:三品PLM系统引领工程变更管理新趋势

引言 在当今快速变化的制造行业&#xff0c;产品生命周期管理&#xff08;PLM&#xff09;系统已成为企业不可或缺的工具之一。PLM系统不仅帮助企业优化产品开发流程&#xff0c;还对工程变更管理&#xff08;ECM&#xff09;起着至关重要的作用。本文将探讨PLM系统在工程变更…

解决ssh报错,.ssh/id_rsa: No such file or directory Permission denied (publickey)

拉取依赖或者代码时说没有权限 首先我们可以看到的是这个报错但是我们的远程确实配置ssh密钥 首先我们可以看到的是这个报错 但是我们的远程确实配置ssh密钥 我们可以在我们项目路径下添加一下我们的私钥如&#xff1a; 首先确定我们ssh是正常启动的eval $(ssh-agent)我们可以…

AC/DC电源模块:提供高质量的电力转换解决方案

BOSHIDA AC/DC电源模块&#xff1a;提供高质量的电力转换解决方案 AC/DC电源模块是一种电力转换器件&#xff0c;可以将交流电转换为直流电。它通常用于各种电子设备和系统中&#xff0c;提供高质量的电力转换解决方案。 AC/DC电源模块具有许多优点。首先&#xff0c;它能够提…

让大模型变得更聪明:人工智能的未来发展之路

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

以JVM新特性看Java的进化之路:从Loom到Amber的技术篇章

引言&#xff1a; JVM的最新特性通过在效率、功能和易用性方面的创新&#xff0c;对Java的未来发展产生了深远的影响。以下是几个关键特性如何塑造了Java的未来&#xff1a; 正文&#xff1a; 轻量级并发 - 项目Loom&#xff1a; 项目Loom通过引入虚拟线程&#xff08;也被称为…

江苏职称申报大揭秘:你所不知道的那些细节

大家好&#xff01;今天我将带大家深入探索江苏职称申报的一些你可能从未关注过的细节。对于在江苏工作的工程类小伙伴们来说&#xff0c;这些信息或许能助你一臂之力&#xff0c;让你在职称申报的道路上更加顺畅。 我们要明确的是&#xff0c;江苏省的工程类职称申报主要有三种…

每日一题——只需一行Python秒杀:PAT乙级1009 说反话!但不能故步自封!(举一反三+思想解读+逐步优化)

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 各部分功能分析&#xff1a; 综合时间复杂度 综合空间复杂度 总结 思路…

读人工智能时代与人类未来笔记15_改变人类经验

1. 认识世界的方式 1.1. 信仰 1.2. 理性 1.2.1. 理性不仅革新了科学&#xff0c;也改变了我们的社会生活、艺术和信仰 1.2.2. 在其浸染之下&#xff0c;封建等级制度瓦解了&#xff0c;而民主&#xff0c;即理性的人应该自治的理念崛起了 1.3. 人工智能 1.3.1. 这种转变将…

大数据开发面试题【Kafka篇】

83、介绍下Kafka&#xff0c;Kafka的作用?Kafka的组件?适用场景? kafka是一个高吞吐量、可扩展的分布式消息传递系统&#xff0c;在处理实时流式数据&#xff0c;并能够保证持久性和容错性 可用于数据管道、流分析和数据继承和关键任务应用&#xff08;发布/订阅模式&#…

Vue3+Ant design 实现Select下拉框一键全选/清空

最近在做后台管理系统项目的时候&#xff0c;产品增加了一个让人非常苦恼的需求&#xff0c;让在Select选择器中添加一键全选和清空的功能&#xff0c;刚开始听到的时候真是很懵&#xff0c;他又不让在外部增加按钮&#xff0c;其实如果说在外部增加按钮实现全选或者清空的话&a…

3、python安装-linux系统下

安装前置依赖软件&#xff0c;安装完成后&#xff0c;打开官网&#xff0c;下载linux系统下的python安装包&#xff1a; 选择最新的版本 点击最新版本&#xff0c;进入版本对应的界面&#xff0c; 选择第一个进行源码的编译&#xff0c;右键选择复制连接地址&#xff0c; 回到终…

HTML+CSS+JS(web前端大作业)~致敬鸟山明简略版

HTMLCSSJS【动漫网站】网页设计期末课程大作业 web前端开发技术 web课程设计 文章目录 一、网站题目 二、网站描述 三、网站介绍 四、网站效果 五、 网站代码 文章目录 一、 网站题目 动漫网站-鸟山明-龙珠超 二、 网站描述 页面分为页头、菜单导航栏&#xff08;最好可下拉&…

CDC 数据实时同步入湖的技术、架构和方案(截至2024年5月的现状调研)

近期&#xff0c;对 “实时摄取 CDC 数据同步到数据湖” 这一技术主题作了一系列深入的研究和验证&#xff0c;目前这部分工作已经告一段落&#xff0c;本文把截止目前&#xff08;2024年5月&#xff09;的研究结果和重要结论做一下梳理和汇总。为了能给出针对性的技术方案&…

ESP32-C6接入巴法云,Arduino方式

ESP32-C6接入巴法云&#xff0c;Arduino方式 第一、ESP32-C6开发环境搭建第一步&#xff1a;安装arduino IDE 软件第二步&#xff1a;安装esp32库第三&#xff1a;arduino 软件设置 第二&#xff1a;简单AP配网程序第一步&#xff1a;程序下载第二步&#xff1a;程序使用第三步…

电脑微信群发 500 1000人以上怎么群发,微信营销群发助手软件,本人亲测,增加十倍业绩!!!

今天给大家推荐一款我们目前在使用的电脑群发工具掘金小蜜&#xff0c;不仅可以无限多开&#xff0c;方便你同时管理多个账号&#xff0c;群发功能更是十分强大&#xff0c;轻松释放你的双手。 掘金小蜜&#xff08;只支持Win7及以上操作系统&#xff0c;没有推Mac版和手机客户…