实验三 进程的互斥与同步

news2025/1/19 17:19:01

文章目录

  • 一、 实验目的
  • 二、 实验原理
  • 三、实验内容
  • 四、我的代码内容和现象
    • 1、philosopher1
    • 2、philosopher2
    • 这个程序不会发生死锁,因为
  • 五、课后习题:
    • 1.什么是死锁?产生死锁的原因和必要条件是什么?
    • 2.实验中给出的伪代码流程,实现多位哲学家就餐问题,是否会产生死锁?若会,请说明产生死锁的原因。
    • 3.针对本实验的哲学家就餐问题,如何设置信号量以避免产生死锁?
  • 六、心得体会
  • 每天进步一点点 笔记仅供自学,用来回看复习,不一定适合你,如有错误请指出。

一、 实验目的

1、了解进程的互斥与同步的概念,理解经典进程同步问题的本质
2、熟悉 Linux 的进程同步机制,掌握相关 API 的使用方法
3、能利用信号量机制,采用多种同步算法实现不会发生死锁的哲学家进餐程序

二、 实验原理

有点长,所以分开了,点击这里跳转进入!!!!!!!!!!!!!!!

三、实验内容

1、以哲学家进餐模型为依据,在 Linux 控制台环境下创立 5 个进程,用 semget 函数创立一个
信号量集〔5 个信号量,初值为 1〕,模拟哲学家的思考和进餐行为:每一位哲学家饥饿时,先拿
起左手筷子,再拿起右手筷子;筷子是临界资源,为每一支筷子定义 1 个互斥信号量;想拿到筷
子需要先对信号量做 P 操作,使用完释放筷子对信号量做 V 操作。
伪代码描述:

semaphore chopstick[5]={1,1,1,1,1};
第 i 位哲学家的活动可描述为: 
do{
printf("Philopher %d is thinking\n",i);
printf("Philopher %d is hungry\n",i);
 P(chopstick[i]); //拿左筷子
 P(chopstick[(i+1) % 5); //拿右筷子
printf("Philopher %d is eating\n",i);
 V(chopstick[i]); //放左筷子
 V(chopstick[(i+1) % 5); //放右筷子}while[true]; 

请根据伪代码编写程序 philosopher1.c,创建该组进程并运行,观察进程是否能一直运行下去。假设停滞那么发生了什么现象?请分析原因,并给出解决方法,编写程序 philosopher2.c 进行验证。

2、五位哲学家围坐在一张圆形桌子上,桌子上有一盘饺子。每一位哲学家要么思考,要么等待,要么吃饺子。为了吃饺子,哲学家必须拿起两只筷子,但是每个哲学家旁边只有一只筷子,也就是筷子数量和哲学家数量相等,所以每只筷子必须由两个哲学家共享。设计一个算法以允许哲学家吃饭。算法必须保证互斥(没有两位哲学家同时使用同一只筷子),同时还要避免死锁(每人拿着一只筷子不放,导致谁也吃不了)。
请根据哲学家就餐问题流程和上文提供的伪代码编写程序,并分析程序存在的问题及改进办法。
在这里插入图片描述
程序运行如下:
在这里插入图片描述

四、我的代码内容和现象

1、philosopher1

这个代码会发生死锁的原因是,哲学家 0 和哲学家 4 都拿起了左手边的筷子,然后都在等待右手边的筷子,而右手边的筷子分别被哲学家 1 和哲学家 3 拿起了,所以哲学家 0 和哲学家 4 无法获取右手边的筷子,从而导致死锁。

在这里插入图片描述
在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <time.h>
#include <sys/wait.h>

#define NUM_PHILOSOPHERS 5
#define NUM_FORKS 5

int main() 
{
// 创建信号量集,包含 NUM_FORKS 个信号量,初始值都为 1
int semid = semget(IPC_PRIVATE, NUM_FORKS, IPC_CREAT | 0666);
if (semid < 0) 
{
    perror("semget failed");
    exit(1);
}

// 创建 NUM_PHILOSOPHERS 个进程,每个进程模拟一位哲学家的思考和进餐行为
for (int i = 0; i < NUM_PHILOSOPHERS; i++) 
{
    if (fork() == 0) 
{
    // 当前进程是哲学家 i,先拿起左手筷子,再拿起右手筷子
    struct sembuf sb[2];
    sb[0].sem_num = i;
    sb[0].sem_op = -1;
    sb[0].sem_flg = 0;
    semop(semid, sb, 2);
    printf("Philosopher %d is putting up left chopstick.\n", i);
    sb[1].sem_num = (i + NUM_PHILOSOPHERS - 1) % NUM_FORKS; // 修改这行代码,使得哲学家拿起筷子的顺序反过来
    sb[1].sem_op = -1;
    sb[1].sem_flg = 0;
    semop(semid, sb, 2);
    printf("Philosopher %d is putting up right chopstick.\n", i);

  // 模拟进餐行为
  printf("Philosopher %d is eating\n", i);
  sleep(1);

  // 释放筷子
  sb[0].sem_op = 1;
  semop(semid, sb, 2);
  printf("Philosopher %d is putting down left chopstick.\n", i);
  sb[1].sem_op = 1;
  printf("Philosopher %d is putting down right chopstick.\n", i);
  semop(semid, sb, 2);

 

  // 模拟思考行为
    printf("Philosopher %d is thinking\n", i);
    sleep(1);
    }
}

// 删除信号量集
semctl(semid, 0, IPC_RMID);

return 0;
}

2、philosopher2

在这里插入图片描述
在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <time.h>
#include <sys/wait.h>

#define NUM_PHILOSOPHERS 5 //信号量数组,分别用于维护每一位哲学家左手和右手的筷子,避免发生死锁
#define NUM_chopstick 5   //筷子数量
/*哲学家当前状态的定义*/
#define THINKING 0//思考中
#define HUNGRY 1 //饥饿中
#define EATING 2 //吃饭中

int state[NUM_PHILOSOPHERS];//数组,存储哲学家当前的状态
int sem_id;  //用于存储信号量集的标识符。


//打印当前在思考中的哲学家
void think(int philosopher) 
{
  printf("Philosopher %d is thinking.\n", philosopher);
  sleep(rand() % 5); //模拟思考的过程,思考需要时间
}

//打印当前在拿筷子的哲学家
void take_chopstick(int philosopher) 
{
  state[philosopher] = HUNGRY;  
  printf("Philosopher %d is hungry.\n", philosopher);

  // wait for left chopstick
  struct sembuf sops;
  sops.sem_num = philosopher;
  sops.sem_op = -1; //操作数option为 -1 ,即为P操作
  sops.sem_flg = 0;
  semop(sem_id, &sops, 1);
  printf("Philosopher %d is putting up left chopstick.\n", philosopher);

  // wait for right chopstick
  sops.sem_num = (philosopher + 1) % NUM_chopstick;
  semop(sem_id, &sops, 1);
  printf("Philosopher %d is putting up right chopstick.\n", philosopher);
}

void put_chopstick(int philosopher) 
{
  state[philosopher] = THINKING;

  // 放下左筷子
  struct sembuf sops;
  sops.sem_num = philosopher;
  sops.sem_op = 1; //操作数option为 1 ,即为V操作
  sops.sem_flg = 0;
  semop(sem_id, &sops, 1);
  printf("Philosopher %d is putting down left chopstick.\n", philosopher);

  // 放下右筷子
  sops.sem_num = (philosopher + 1) % NUM_chopstick;
  semop(sem_id, &sops, 1);
  printf("Philosopher %d is putting down left chopstick.\n", philosopher);
}

//打印正在吃饭的哲学家
void eat(int philosopher) 
{
  state[philosopher] = EATING;
  printf("Philosopher %d is eating.\n", philosopher);
  sleep(rand() % 5); //模拟哲学家吃饭,吃饭需要花费一段时间
}

int main()
{
// 为随机数生成器提供种子
  srand(time(NULL));

  // 用初始值创建信号量集
  sem_id = semget(IPC_PRIVATE, NUM_chopstick, 0600);
  if (sem_id < 0) 
  {
      perror("semget failed");
      exit(1);//返回非0,表示运行有错,异常终止
  }
  semctl(sem_id, 0, SETALL, 1);

  // 创造哲学家的进程
  for (int i = 0; i < NUM_PHILOSOPHERS; i++) 
  {
    if (fork() == 0) 
    {
      // 哲学家的进程
      while (1) 
      {
        think(i);
        take_chopstick(i);
        eat(i);
        put_chopstick(i);
      }
    }
  }

  // 删除信号量集
  semctl(sem_id, 0, IPC_RMID);
  return 0;

}

这个代码实现了哲学家进餐问题的模拟。它使用了信号量来维护每一支筷子的互斥访问,并使用了 P 操作和 V 操作来控制对信号量的访问。
1、在主函数中,首先使用 semget 函数创建了一个信号量集,并初始化了NUM_chopstick个信号量,每个信号量的初始值都为 1。然后使用 fork 函数创建了 NUM_PHILOSOPHERS 个进程,每个进程模拟一位哲学家的思考和进餐行为。
2、每个哲学家进程都会执行一个无限循环,在循环中,它会先执行 think 函数模拟思考的过程,然后执行take_chopstick 函数模拟拿起筷子的过程。这个函数会使用 semop 函数执行 P 操作来获取左手边的筷子和右手边的筷子。
– 如果获取成功,就会执行 eat 函数模拟进餐的过程,然后执行 put_chopstick 函数模拟释放筷子的过程。这个函数会使用 semop 函数执行 V 操作来释放左手边的筷子和右手边的筷子。
3、最后,主函数会使用 wait 函数等待所有的哲学家进程结束,然后使用 semctl 函数删除信号量集。

这个程序不会发生死锁,因为

1、在程序中,哲学家的进餐行为是通过调用 take_chopstick() 函数实现的。这个函数首先会将当前哲学家的状态设置为饥饿状态,然后会调用 semop() 函数对左手筷子执行 P 操作,再对右手筷子执行 P 操作。
2、当哲学家吃饱之后,会调用 put_chopstick() 函数将左右手的筷子放回桌子上。这个函数会将当前哲学家的状态设置为思考状态,然后对左手筷子和右手筷子分别执行 V 操作,释放这两支筷子。
3、由于这个程序中没有设置资源不足的情况,也没有设置资源分配不当的情况,并且哲学家在吃饭完成后会立即释放筷子,因此该程序不会产生死锁。

五、课后习题:

1.什么是死锁?产生死锁的原因和必要条件是什么?

答:死锁是指两个或多个进程因争夺资源而相互等待,导致系统无法继续运行的状态。
产生死锁的原因有以下几种:
(1)资源不够:系统中资源数量有限,如果进程需要的资源数量超过了系统能够提供的数量,那么就会发生死锁。
(2)资源分配不当:如果资源分配不当,比如一个进程已经占用了资源 A 和资源 B,另一个进程已经占用了资源 B 和资源 C,那么这两个进程就会相互等待,从而导致死锁。
(3)资源占用不当:如果进程在使用资源的过程中不释放资源,就会导致其他进程无法获取所需资源,从而导致死锁。
产生死锁的必要条件包括四个要素:
(1)互斥条件:所谓互斥条件是指系统中的资源至多只能被一个进程占用。如果一个进程占用了资源,则其他进程就不能占用这些资源。
(2)请求与保持条件:所谓请求与保持条件是指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源正被其他进程占有,此时请求进程阻塞,但又对自己已获得的资源保持不放。

(3)不剥夺条件:所谓不剥夺条件是指进程已获得的资源在未使用完之前,不能强行剥夺。
(4)循环等待条件:所谓循环等待条件是指存在一个进程的资源请求序列{P0, P1, …, P0},使得 Pi 必须等到 Pj 释放资源后才能得到资源,其中 i≠j。
当满足上述四个条件时,就会产生死锁。

2.实验中给出的伪代码流程,实现多位哲学家就餐问题,是否会产生死锁?若会,请说明产生死锁的原因。

答:在这个伪代码中,会产生死锁的原因是:
(1)每个哲学家都在循环中进行思考、饥饿和吃饭的行为,并没有退出循环的条件。
(2)在拿筷子之前,每个哲学家都会先拿起左手的筷子,然后再拿起右手的筷子。
(3)当两个哲学家同时拿起了自己的左手筷子时,如果他们都想要拿起右手的筷子,就会产生死锁。
具体情况是这样的:哲学家 1 和哲学家 2 同时拿起了自己的左手筷子,然后同时等待右手的筷子。但是右手的筷子都被对方拿起了,所以他们就陷入了死锁。
为了避免这种情况的发生,可以使用一些机制来解决死锁问题,比如说:
(1)对于每个哲学家,使用随机数来决定先拿哪只手的筷子。
(2)在拿筷子的时候,设置一个超时时间,如果超过了这个时间还没有拿到筷子,就退出循环。
(3)使用顺序等待,让每个哲学家按照固定的顺序拿筷子,这样就不会出现同时拿起左手筷子的情况。
总之,死锁的产生是由于多个进程因为竞争临界资源。

3.针对本实验的哲学家就餐问题,如何设置信号量以避免产生死锁?

答:为了避免在本实验的哲学家就餐问题中产生死锁,可以设置一个顺序信号量,使得哲学家在拿筷子时按照固定顺序进行。比如,哲学家 0 只能先拿左边的筷子再拿右边的筷子,哲学家 1 只能先拿右边的筷子再拿左边的筷子,以此类推。这样,只要所有的哲学家都按照这个顺序拿筷子,就不会产生死锁的情况。

六、心得体会

完成本试验后,我认识到了信号量的作用和使用方法,了解了互斥信号量和条件信号量的区别,并了解了死锁的产生原因和如何避免死锁的方法。在实验中,我还学会了使用 semget 函数和 semop 函数来创建信号量集和进行信号量操作。
总的来说,本实验使我加深了对进程同步和死锁问题的理解,并学会了使用信号量来解决实际问题。


每天进步一点点 笔记仅供自学,用来回看复习,不一定适合你,如有错误请指出。

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

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

相关文章

Problem Set 3

1Lagrange Duality Formulate the Lagrange dual problem of the following linear programming prob-lem min cT rs.t.Ax 二b where a ∈R is variable,c ∈ R"&#xff0c;A ∈Rkn, b ∈ Rk. 解&#xff1a;设拉格朗日函数为L(x,λ)cTxλT(Ax−b)\mathcal{L}(x,\lambda)…

第十七章 webpack5项目搭建Vue-Cli(开发模式)

step1–创建项目目录 创建一个目录用来搭建vue-cli的项目 mkdir vue-cli cd vue-clistep2–初始化项目 初始化项目&#xff0c;生成一个package.json文件 npm init -ystep3–编写vue-cli的开发模式配置 新建目录 / |-config | |--webpack.dev.js | |--webpack.prod.js我…

USB TO SPI(上海同旺电子)调试器调试25LC020A

所需设备&#xff1a; 1、USB TO SPI(上海同旺电子)&#xff1b; 2、25LC020A 2Kb 2.5V SPI Serial EEPROM; Microchip 25LC020A 是一款 2 Kb 串行 EEPROM&#xff0c;采用行业标准串行外设接口 (SPI) 兼容串行总线。 该器件被组织为一个 256 x 8 位块&#xff0c;并针对消…

Java安全--CC7

在学CC7的时候我有这么几个疑问 1.为什么要两个LazyMap 2.hashCode那一步怎么计算的 3.为什么要remove yy 4.为什么put两个 我们可以先看一下CC7的链子是怎么走的: 其实分析链子还是从命令执行走到readObject比较好理解&#xff0c;虽然比较麻烦&#xff0c;比较繁琐&#xff0…

机器人机械臂运动学——逆运动学解算

1.情景概述 假设最一般的情况&#xff0c;我们的机械臂有六个自由度&#xff0c;那么从初始状态想要变化到目标的状态&#xff0c;一般情况下我们至少需要进行六次的变换&#xff0c;而这六次变换的矩阵参数隐含在整体的变换矩阵中。 根据之前的知识&#xff0c;左上角的3*3代表…

m基于隐马尔科夫模型(HMM)的手机用户行为预测(MMUB)算法matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 隐马尔可夫模型&#xff08;Hidden Markov Model&#xff0c;HMM&#xff09;是一种统计模型&#xff0c;广泛应用在语音识别&#xff0c;词性自动标注&#xff0c;音字转换&#xff0c;概率文法…

R语言中的Nelson-Siegel模型在汇率预测的应用

这篇文章的目的是指导读者逐步使用R编程语言实现Nelson-Siegel模型的步骤。 最近我们被客户要求撰写关于Nelson-Siegel模型的研究报告&#xff0c;包括一些图形和统计输出。 您可能已经知道&#xff0c;估计利率期限结构是任何资产定价的关键&#xff0c;因此对投资者和政策制…

5G无线技术基础自学系列 | 基于Massive MIMO的场景化波束优化

素材来源&#xff1a;《5G无线网络规划与优化》 一边学习一边整理内容&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 前面内容提到&#xff0c; 5G中引入了M…

机器人机械臂运动学——运动学正向解算

本博客内容参考台湾大学_林沛群教授_机器人学的课程内容 1.相对运动关系的描述方法 假设有现在这样的一个情景&#xff0c;从左到右有三个转轴&#xff0c;转轴本身可以转动&#xff0c;也可以沿着轴线上下平动&#xff0c;那么我们如何描述其中的运动学关系及其位置关系呢&am…

Linux Red Hat 8.0 cat、cut、sed、tail命令

1.cat&#xff1a; 在终端设备上显示文件内容 常用选项&#xff1a; -n 显示行数&#xff08;空格有编号&#xff09; -b 显示行数&#xff08;空格没有编号&#xff09; 命令格式&#xff1a;cat 选项 文件 特殊用法tac&#xff1a;倒着显示内容 配合重定向使用 查看 test1…

DPDK 多进程

DPDK库里是支持多进程和多线程&#xff0c;本文主要总结多进程的相关的操作。 DPDK多进程使用的关键启动参数&#xff1a; --proc-type&#xff1a;指定一个dpdk进程是主进程还是副进程&#xff08;参数值就用上面的primary或是secondary&#xff0c;或者是auto&#xff09;-…

【Bio】基础生物学 - 五个重要官能团 five important functional group

文章目录1. 羟qiǎng基&#xff08;hydroxyl&#xff09;2. 羧suō基&#xff08;carboxyl&#xff09;3. 氨基&#xff08;amino&#xff09;4. 磷酸盐&#xff08;phosphate&#xff09;5. 巯基&#xff08;sulfhydryl&#xff09;1. 羟qiǎng基&#xff08;hydroxyl&#xf…

最新进展 | Android 自定义机器学习堆栈

作者 / Android 机器学习平台团队相较于服务器端机器学习 (ML)&#xff0c;设备端机器学习有其独特的优势&#xff0c;如支持离线使用、延迟更低、隐私性更高和推理成本更低&#xff0c;因此 Android 中设备端机器学习的使用迅速增长。在构建基于设备端 ML 的功能时&#xff0c…

Android基于perfetto分析native内存泄露

官方文档(可在Chome直接翻译) https://perfetto.dev/docs/data-sources/native-heap-profiler 示例 raw-trace 资源地址 https://download.csdn.net/download/CSqingchen/87321798 本文示例是windows&#xff0c;这里使用了python工具&#xff0c;在Linux和mac同样适用 首先安装…

胡凡 《算法笔记》 上机实战训练指南 3.1 简单模拟

胡凡 《算法笔记》 上机实战训练指南 3.1 持续更新中 , 菜鸡的刷题笔记… 大学到现在了还没咋好好刷过题&#xff0c;该push自己了… 文章目录胡凡 《算法笔记》 上机实战训练指南 3.13.1 简单模拟【PAT B1001】害死人不偿命的&#xff08;3n1&#xff09;猜想【PAT B1032】挖掘…

spring扩展之基于HandlerMapping实现接口灰度发布的demo

背景 面试经常被问到&#xff0c;你了解spring源码吗&#xff1f;有基于spring做过什么扩展吗&#xff1f;除了PropertyPlaceholderConfigurer处理占位符(被说烂了)还有其他的吗&#xff1f; 看了springmvc的源码&#xff0c;有了一个新的案例可讲(吹) 基于HandlerMapping实现…

运营活动没做好,是没有明确目标

有很多运营人在做项目的时候&#xff0c;其实没有一个明确的目标&#xff0c;不清楚自己究竟为什么要做这件事。 我们在做运营方案前&#xff0c;要分析用户的需求有哪些&#xff0c;如何达成目标&#xff0c;制定出一个计划&#xff1a; 1、要做什么——计划 我们需要掌握运…

ES基础入门

ES 一、简介 1、Elastic Stack ES技术栈&#xff1a; ElasticSearch&#xff1a;存数据搜索&#xff1b;QL&#xff1b;Kibana&#xff1a;Web可视化平台&#xff0c;分析。LogStash&#xff1a;日志收集&#xff0c;Log4j:产生日志&#xff1b;log.info(xxx)。。。。 使用…

软件测试面试题和面试流程

自我介绍部分 .... 项目经历 .... 技能部分 性能 使用什么工具&#xff1f;介绍下最近做的性能测试项目&#xff0c;性能测试过程中遇到哪些问题&#xff1f;性能标准谁来定&#xff1f;你们性能测试的流程是什么&#xff1f;什么是分布式&#xff1f;为什么要用分布式&a…

硅片检测半导体运动台控制器的设计(二)

虽然我国目前对于精密硅片检测运动平台的整机研发还比较空白&#xff0c;但是值得注意的是&#xff0c;对于 X、Y 两自由度运动平台&#xff0c;甚至多自由度运动平台、先进驱动方式和控制策略的研究已经积累了一定的经验&#xff0c;吸收这些经验并应用于本运动平台的研发对选…