Linux 关于读者与写者同步互斥问题的解析

2023-11-07

      在操作系统课程中,读者与写者问题(或是称作生产者与消费者问题),一直是同步与互斥问题的经典例子,在大学的操作系统课程中也是作为经典例子进行讲解,今天我们就来解析一下读者与写者互斥同步关系。

      首先要做的是先了解一下问题的内容,首先需要肯定的是先要有写者将文件进行编辑,即书写内容,然后才会有读者进行阅读,这个是符合我们基本的常识的,进而开始分析这个比较令人脑热的复杂关系。

     先从写者开始,写者需要的限制条件比较明显:

                       写者与任何人都是互斥关系,若有读者在进行读操作时,要等待其完成后才可以进行操作;若有写者进行操作时,也需要等待。

因此我们需要一个互斥变量进行限制读者和写者的操作,仅允许一个线程可以进行操作,要完成该操作我们需要调用pthread_mutex_t writez (包含在头文件<pthread.h>)作为我们的互斥信号量,该信号量同时用于读者线程中,这样,写者与写者互斥,写着与读者互斥,同时争用一个信号量writez,一旦有一个线程争得,就将这个信号量锁定,则其他线程必须等待其用完后将信号量释放才可以继续操作。同时可以增加线程的功能,真正实现写者的写功能,调用函数ssize_t write(int fd, const void *buf, size_t count)(包含在头文件<unistd.h>)

参数:   
fd:要进行写操作的文件描述词。
buf:需要输出的缓冲区
count:最大输出字节计数

还需要调用open()函数对文件进行操作(函数原型:int open(const char *pathname,int flags,int perms)

//所需头文件:#include <sys/types.h>,#include <sys/stat.h>,#include <fcntl.h>

以及read()函数

所需头文件: #include <unistd.h>

函数原型:ssize_t read(int fd, void *buf, size_t count);

参数:  
fd: 将要读取数据的文件描述词。
buf:指缓冲区,即读取的数据会被放到这个缓冲区中去。
count: 表示调用一次read操作,应该读多少数量的字符。

从而实现写的功能,但此过程中我们仍需要一个互斥信号量进行对过程的互斥限制,在此我们先埋下一个伏笔,这在很多程序中都难免会遗漏的一个具危险的buger。

    在开始对读者的分析:

             读者的限制比较多,首先我们还需要强调一个关键点,那就是第一个读者的问题,众所周知,只有第一个读者需要与写者竞争互斥信号量,因为一旦第一个读者竞争到资源后,后续的读者会同步的使用资源,而无需判断是否有写者在操作,因此需要对第一个读者进行标记,这就是在程序开头我们进行强调的readert = 1作为是否是第一个读者的判断依据,在此,必须进行互斥控制,让多个线程竞争成为第一个读者。这样第二个信号量产生pthread_mutex_t readz。若是第一个读者先将readert--,清除标记,好为下一次的第一个读者使用,调用pthread_mutex_lock(&writez),进行互斥竞争,若竞争成功,就锁定信号量,但无需释放信号量,此后,所有的读者都尅同步的进行读操作,直到最后一个读者结束,因此,还需要一个readll作为对读者数量的统计,但必须是互斥进行操作,否则多个线程同时进行时,上一个进行"+"操作,而下一个进行"-"操作,则这个readll就失去意义了。因此必须只有一个线程使用readll,则第三个互斥信号量产生pthread_mutex_t kz,作为控制对读者个数的操作,最重要的是在readll == 0 时,即最后一个读者结束后要释放信号量,以供写者竞争。

     经过梳理我们现在拥有三个信号量,基本上已经可以完成对读者与写者问题的程序分析,但编写完程序后发现无论如何都无法控制写者的写操作,总是现出来提示可以输入内容时,又有读者进行读操作,这就是我们在开始留下的伏笔,因为在整个过程中,我们都没有对文件的操作进行控制,导致在写者进行写操作时读者依旧可以对文件进行操作,因此最后一个互斥信号量pthread_mutex_t file产生,控制对文件的操作。

//readert = 1 作为是否是第一个读者

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#define BUFFER_SIZE 128        //
最大字符数
#define SRCV "srcv.txt"               //文件名
#define OFFSET 0
int number = 0;                            //写者标号
char ch = 'A';                               //读者标号
int real_read_len = 0;
int readert = 0;
int flag_t = 0;                               //作为是否有内容可读,=1表示有内容可读
int readll = 0;                               //作为统计读者个数的
pthread_mutex_t writez;
pthread_mutex_t readz;
pthread_mutex_t file;
pthread_mutex_t kz;
void destroy(void)                            //信号量消除
{
    pthread_mutex_destroy(&writez);
    pthread_mutex_destroy(&readz);
    pthread_mutex_destroy(&file);
    pthread_mutex_destroy(&kz);
}
/
void *readerl(void *p)                          //读者
{
   char num = (char) p;
   char src_buff[BUFFER_SIZE];
   int src;

if(readll == 0)                                 //若无其他读者就设下一个读者为第一个
pthread_mutex_lock(&kz);
  readert++;
pthread_mutex_unlock(&kz);
  if(readert == 1)                            //如果是第一个读者需要判断是否有写者运行
{
  pthread_mutex_lock(&writez);    //互斥开始
pthread_mutex_lock(&kz);
readert = 0;
pthread_mutex_unlock(&kz);
if(flag_t == 0)                                //判断有无内容可读
{
 printf("NO.%c:No text to read...process is exiting..\n",num);
 return ;
}
pthread_mutex_lock(&readz);
   readll++;                                       //读者个数++
pthread_mutex_unlock(&readz);
  printf("NO.%c reader is reading(This is the first reader Now)\n",num);
pthread_mutex_lock(&file);
  src = open(SRCV,O_RDONLY,S_IRUSR|S_IWUSR|S_IRGRP,S_IROTH);//作为只读文件
  if(src < 0)
  {
    printf("OPen the file Error!\n");
    return ;
  }
pthread_mutex_unlock(&file);
   lseek(src,OFFSET,SEEK_SET);  //将指针指向文件起始位置
   real_read_len = 1;
while(real_read_len > 0)
   real_read_len = read(src,src_buff,sizeof(src_buff));//读文件
   printf("NO.%c reader reads the text:%s\n",num,src_buff);
//usleep(5000);
   printf("NO.%c reader reads finish\n",num);
pthread_mutex_lock(&readz);
   readll--;
pthread_mutex_unlock(&readz);
return ;
}
/
 else  //非第一个读者
{
pthread_mutex_lock(&readz);
  readll++;
pthread_mutex_unlock(&readz);


pthread_mutex_lock(&file);
  printf("NO.%c reader is reading\n",num);
 // printf("NOW  the Readll = %d\n",readll);
  src = open(SRCV,O_RDONLY,S_IRUSR|S_IWUSR|S_IRGRP,S_IROTH);//作为只读文件
  if(src < 0)
  {
    printf("Open the file Error!\n");
    return ;
  }


   lseek(src,OFFSET,SEEK_SET);          //将指针指向文件起始位置
   real_read_len = 1;
   real_read_len = read(src,src_buff,sizeof(src_buff));//读文件
   printf("NO.%c reader reads the text:%s\n",num,src_buff);
   printf("NO.%c reader finish\n",num);
pthread_mutex_unlock(&file);


pthread_mutex_lock(&readz);
   readll--;
pthread_mutex_unlock(&readz);
//printf("NOW the Readll = %d;the readert = %d\n",readll,readert);
 if(readll == 0){


     pthread_mutex_unlock(&writez);}
}
return ;
}
//
void * writerl(void * p)
{
  long int num = (long int)p;
   char dest_buff[BUFFER_SIZE];
   int  dest;


pthread_mutex_lock(&writez);                     //写者对所有的都互斥
printf("NO.%ld writer is writing\n",num);
pthread_mutex_lock(&file);


   dest = open(SRCV,O_WRONLY|O_TRUNC|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP,S_IROTH);//以只写方式打开,若文件已经存在,那么会删除文件中的全部原有数据,并且设置文件大小为0
if(dest < 0)
{
  printf("Open file Error\n");
  return ;
}


  flag_t = 1;
  printf("Please input message(< 512)\n");
  scanf("%s",dest_buff);
  write(dest,dest_buff,sizeof(dest_buff));


pthread_mutex_unlock(&file);
  printf("NO.%ld writer writing finish\n",num);
pthread_mutex_unlock(&writez);
return ;
}
///
void *moon(void *p)
{
pthread_t we,re;
 int ad,cd;
while(1){
printf("Please input num of writer\n");
scanf("%d",&ad);
printf("Please input num of reader (end : 0)\n");
scanf("%d",&cd);
if(cd == 0)
{
printf("Please wait ,the process is exiting\n");
sleep(3);
break;
return ;
}
int i;
for(i = 1; i <= ad; i++)
{
pthread_create(&we,NULL,writerl,(void *)(++number));}


int j;
for(j = 1; j <= cd; j++){
pthread_create(&re,NULL,readerl,(void *)(++ch));}
sleep(10);
}
destroy();
return ;
}
//
int main()
{
pthread_t pt;
pthread_create(&pt,NULL,moon,NULL);
pthread_join(pt,NULL);
return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux 关于读者与写者同步互斥问题的解析 的相关文章

  • 如果文件没有行尾字符,则 wc -l 不计算文件的最后一个

    我需要计算 unix 文件的所有行数 该文件有 3 行 但是wc l仅给出 2 个计数 我知道它不计算最后一行 因为它没有行尾字符 任何人都可以告诉我如何计算这一行吗 grep c返回匹配行的数量 只需使用一个空字符串 作为您的匹配表达式
  • 如何通过ssh获取远程命令的退出代码

    我正在通过 ssh 从远程计算机运行脚本 ssh some cmd my script 现在 我想在本地计算机上存储 shell 脚本的退出状态 我该怎么做 假设没有任何问题ssh其本身 其退出状态是在远程主机上执行的最后一个命令的退出状态
  • Apache 端口转发 80 到 8080 并访问 Apache (80) 中托管的应用程序,即 phpMyadmin 和 Tomcat (8080)

    我想访问托管在 tomcat 服务器 8080 中的应用程序 myapp 当前可以通过以下方式访问http example com 8080 myapp http example com 8080 myapp in http example
  • Linux 内核使用的设备树文件 (dtb) 可视化工具? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个可以图形化表示Linux内核中使用的硬件设备树的工具 我正在尝试了解特定 Arm 芯片组
  • Vagrant 遇到问题 - “404 - 未找到”

    我正在尝试使用 Vagrant 制作一个 LAMP 盒子 有人告诉我它使用起来非常简单 我对网络和虚拟机完全陌生 对 Linux Ubuntu 的经验也很少 我目前已尝试按照官方文档页面上的教程进行操作 http docs vagrantu
  • 在ubuntu中打开spyder

    我想在ubuntu中打开spyder Python IDE 通常我会在 shell 中编写 spyder 它会打开spyder IDE 现在 当我在shell中编写spyder时 它只是换行 什么也没有发生 类似于按 enter 我如何找回
  • 在 Linux 中重新启动时,新创建的文件变为 0 kb(数据被覆盖为空)

    我遇到了一个奇怪的问题 这让我发疯 当前的任务是在 root 用户第一次登录时启动一组文件 并在同一用户第二次登录时启动另一组文件 我决定使用 profile 和 bashrc 文件 并在第一次登录期间发生的任务结束时重新加载 bashrc
  • 更新Linux中的包含路径

    我的 my path to file 文件夹中有几个头文件 我知道如何将这些文件包含在新的 C 程序中 但每次我都需要在包含它之前输入头文件的完整路径 我可以在linux中设置一些路径变量 以便它自动查找头文件吗 您可以创建一个 makef
  • Python glob,操作系统,相对路径,将文件名放入列表中[重复]

    这个问题在这里已经有答案了 我正在尝试创建一个目录中所有文件的列表 其中文件名以 root 结尾 在阅读了论坛中的一些文章后 我尝试使用 glob 和 os listdir 的基本策略 但我都遇到了麻烦 首先 当我使用 import glo
  • 为什么此 NASM 代码会打印我的环境变量?

    本学期我刚刚完成计算机体系结构课程 除其他外 我们一直在涉足 MIPS 汇编并在 MARS 模拟器中运行它 今天 出于好奇 我开始在我的 Ubuntu 机器上摆弄 NASM 基本上只是将教程中的内容拼凑起来 并感受一下 NASM 与 MIP
  • “git add”返回“致命:外部存储库”错误

    我刚刚进入 git 的奇妙世界 我必须提交我对程序所做的一系列更改 位于名为的目录中 var www myapp 我创建了一个新目录 home mylogin gitclone 从这个目录中 我做了一个git clone针对公共回购 我能够
  • /sys/device/ 和 dmidecode 报告的不同 CPU 缓存大小

    我正在尝试获取系统中不同缓存级别的大小 我尝试了两种技术 a 使用 sys device 中的信息 这是输出 cat sys devices system cpu cpu0 cache index1 size 32K cat sys dev
  • 正则表达式删除块注释也删除 * 选择器

    我正在尝试使用 bash 从 css 文件中删除所有块注释 我有以下 sed 命令的正则表达式 sed r s w s w d 这可以很好地去除块注释 例如 This is a comment this is another comment
  • Ubuntu Python shebang 线不工作

    无法让 shebang 线在 Ubuntu 中为 python 脚本工作 我每次只收到命令未找到错误 test py usr bin env python print Ran which python usr bin python 在 sh
  • 如何在线程创建和退出时调用函数?

    include
  • grep 排除文件的数组参数

    我想从我的文件中排除一些文件grep命令 为此我使用参数 exclude excluded file ext 为了更容易阅读 我想使用包含排除文件的 bash 数组 EXCLUDED FILES excluded file ext 然后将
  • awk 在循环中使用时不打印任何内容[重复]

    这个问题在这里已经有答案了 我有一堆使用 file 1 a 1 txt 格式的文件 如下所示 A 1 B 2 C 3 D 4 并使用以下命令添加包含每个文件名称的新列 awk print FILENAME NF t 0 file 1 a 1
  • vmsplice() 和 TCP

    在原来的vmsplice 执行 有人建议 http lwn net Articles 181169 如果您的用户态缓冲区是管道中可容纳的最大页面数的 2 倍 则缓冲区后半部分成功的 vmsplice 将保证内核使用缓冲区的前半部分完成 但事
  • ioctl 命令的用户权限检查

    我正在实现 char 驱动程序 Linux 并且我的驱动程序中有某些 IOCTL 命令仅需要由 ADMIN 执行 我的问题是如何在 ioctl 命令实现下检查用户权限并限制非特权用户访问 IOCTL 您可以使用bool capable in
  • 批量删除文件名中包含 BASH 中特殊字符的子字符串

    我的目录中有一个文件列表 opencv calib3d so2410 so opencv contrib so2410 so opencv core so2410 so opencv features2d so2410 so opencv

随机推荐

  • js刷新当前页面的5种方式

    1 reload reload 方法 该方法强迫浏览器刷新当前页面 语法 location reload bForceGet 参数 bForceGet 可选参数 默认为 false 从客户端缓存里取当前页 true 则以 GET 方式 从服
  • 设计模式之状态模式

    一 背景 状态这个词汇我们并不陌生 在日常生活中 不同时间就有不同的状态 早上起来精神饱满 中午想睡觉 下午又渐渐恢复 晚上可能只想睡觉 这就对应着一天中不同的状态 二 定义 状态 State 模式的定义 对有状态的对象 把复杂的 判断逻辑
  • Rancher 2.2.2 - HA 部署高可用k8s集群

    对于生产环境 需以高可用的配置安装 Rancher 确保用户始终可以访问 Rancher Server 当安装在Kubernetes集群中时 Rancher将与集群的 etcd 集成 并利用Kubernetes 调度实现高可用 为确保高可用
  • 已知两点经纬度,计算两点间的距离

    通过两点的经纬度计算两点之间的距离 def getDistance lng1 lat1 lng2 lat2 param lng1 A点的经度 param lat1 A点的纬度 param lng2 B点的经度 param lat2 B点的纬
  • 房屋交接时需要注意些什么?

    在房屋交接时应该注意以下几点 一 进行水电煤气抄表 并计算金额 确定上下家各自承担的份额 上下家应在双方交接房屋时做好此项工作 以免交房后双方就这些小费用产生扯皮 二 办理有线电视的结算和更名手续 买卖双方在房屋买卖交易后一般不会忘了结算水
  • C++ 学习之旅(1)——编译器Compiler

    简单来说 由C 代码文件生成可执行文件的过程如下 mermaid svg GQamCVEXMVkYEemz font family trebuchet ms verdana arial sans serif font size 16px f
  • C语言字符串的经典例题

    1 统计单词的个数 include
  • 《编程珠玑》--读书笔记12章:取样问题

    第十二章 作者提出了一个问题 程序的输入包含两个整数m和n 其中m lt n 输出是0 n 1范围内的m个随机整数 不允许重复 有两种方法达到目的 1 思路 从r个剩余的整数中选出s个 以概率s r来选择下一个数 比如 m 2 n 5 选择
  • SaaS企业怎么做决策

    帆软会议室的墙上出现了 永不上市 利润分享 的字样 所有参观的SaaS创业者都被这八个字吸引 纷纷拿出手机拍照 帆软创始人薛宝表示 在帆软全国30多家办公室的墙上都贴着这8个大字 2006年 帆软用薛宝仅有的5000元积蓄创办了这家公司 从
  • linux进程及进程间同步通信机制

    进程 一 进程的存储器安排 这是 UNIX环境高级编程 中介绍的典型存储器安排 但现实也不一定非要如此 对于c c 来说 数据的存储方式还是认为3种 堆 栈 全局数据区 包括全局数据 静态数据 常量 二 僵尸进程和孤儿进程 进程在终止前向父
  • shiro基础

    shiro基础 shiro中有三大核心架构 subjects security manager realms 分别代表 使用shiro的主体 核心安全管理器和数据和安全管理的连接 使用shiro首先需要引入shiro需要的依赖 可以通过ma
  • 前端如何进行用户权限管理

    1 问题 假如在做一个管理系统 面向老师学生的 学生提交申请 老师负责审核 或者还需要添加其他角色 功能权限都不同 现在的问题是 每种角色登录看到的界面应该都是不一样的 那这个页面的区分如何实现呢 2 要不要给老师和学生各自设计一套页面 这
  • [论文阅读]《Database Maanagement Systems》-第九章

    第九章 TREE STRUCTURED INDEXING 树结构索引 P271 301 gracefully 优雅的 适当的 because it adjusts well to changes 因为它能很好地适应变化 page bound
  • 2013年11月21日星期四(地图)

    初始 DDraw BOB bob BOB textures tool mytool char buffer 80 BITMAP FILE bitmap8bit int world x 0 current position of viewin
  • 【vue2对接萤石云接口,实现摄像头直播页面的调取显示】

    vue2对接萤石云接口 实现摄像头直播页面的调取显示 使用ezuikit js插件 accessToken url 附上最新代码和demo 公司前端vue项目需要实现萤石云接口对接 买好萤石摄像头 注册萤石云账号并将设备添加到萤石云平台 开
  • 端口被占用怎么办?关闭占用端口的进程

    当你发现某个端口被占用时 但不知道是哪一个进程占用了端口 需要关闭占用该端口的进程 1 启动系统命令行 windows系统 win r 2 输入命令 netstat ano 可查看所有端口的使用情况 netstat aon findstr
  • Buck电路

    如果我们希望得到一个20V的直流电压源 我们能想到什么办法 很简单 从220V市电开始 通过变压器降压得到有效值为20V的电压 通过整流桥整流得到不稳定的直流电 接着用一个大的电解电容滤波 就可以得到20V的电压 那么如果我们现在希望得到一
  • 微信python小课_微信上其实还有很多你不知道的事,Python微信平台开发编写实录...

    本文主要讲述如何利用Python开发微信公众平台 说明 如果你是Python小白 爬虫小白 觉得本节课程的代码晦涩难懂 没关系 不需要懂 按照步骤一步步操作就可以了 这一节我们主要是搭框架 几乎没有真正的爬虫代码 这些代码和操作 你可能一生
  • POI生成excel表格——简单例子

    导入包 poi 2 5 1 jar User类 提供数据用 package com xk poi import java util ArrayList import java util List public class User priv
  • Linux 关于读者与写者同步互斥问题的解析

    在操作系统课程中 读者与写者问题 或是称作生产者与消费者问题 一直是同步与互斥问题的经典例子 在大学的操作系统课程中也是作为经典例子进行讲解 今天我们就来解析一下读者与写者的互斥同步关系 首先要做的是先了解一下问题的内容 首先需要肯定的是先