操作系统实战(二)(linux+C语言)
实验内容
通过Linux 系统中管道通信机制,加深对于进程通信概念的理解,观察和体验并发进程间的通信和协作的效果 ,练习利用无名管道进行进程通信的编程和调试技术。
管道pipe是进程间通信最基本的一种机制,两个进程可以通过管道一个在管道一端向管道发送其输出,给另一进程可以在管道的另一端从管道得到其输入。管道以半双工方式工作,即它的数据流是单方向的。因此使用一个管道一般的规则是读管道数据的进程关闭管道写入端,而写管道进程关闭其读出端。
示例程序
效果为:两个进程交替分别对X进行+1操作
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{int pid; //进程号int pipe1[2]; //存放第一个无名管道标号int pipe2[2]; //存放第二个无名管道标号int x; // 存放要传递的整数//使用pipe()系统调用建立两个无名管道。建立不成功程序退出,执行终止if(pipe(pipe1) < 0){perror("pipe not create");exit(EXIT_FAILURE);}if(pipe(pipe2) < 0){perror("pipe not create");exit(EXIT_FAILURE);}//使用fork()系统调用建立子进程,建立不成功程序退出,执行终止if((pid=fork()) <0){perror("process not create");exit(EXIT_FAILURE);}//子进程号等于0 表示子进程在执行else if(pid == 0){//子进程负责从管道1的0端读,管道2的1端写//所以关掉管道1的1端和管道2的0端。close(pipe1[1]);close(pipe2[0]);//每次循环从管道1 的0 端读一个整数放入变量X 中,//并对X 加1后写入管道2的1端,直到X大于10do{read(pipe1[0],&x,sizeof(int));printf("child %d read: %d\n",getpid(),x++);write(pipe2[1],&x,sizeof(int));}while( x<=9 );//读写完成后,关闭管道close(pipe1[0]);close(pipe2[1]);//子进程执行结束exit(EXIT_SUCCESS);}//子进程号大于0 表示父进程在执行else{//父进程负责从管道2的0端读,管道1的1端写,//所以关掉管道1 的0 端和管道2 的1端。close(pipe1[0]);close(pipe2[1]);x=1;//每次循环向管道1 的1 端写入变量X 的值,并从//管道2的0 端读一整数写入X 再对X加1,直到X 大于10do{write(pipe1[1],&x,sizeof(int));read(pipe2[0],&x,sizeof(int));printf("parent %d read: %d\n",getpid(),x++);}while(x<=9);//读写完成后,关闭管道close(pipe1[1]);close(pipe2[0]);}//父进程执行结束return EXIT_SUCCESS;
}
执行结果:

几个关键点
一、pipe系统调用的使用

- 创建管道两个端口 :int pipe[2]
- 调用pipe系统调用在两个端口间建立管道
- 后续可利用read、write通过管道端口,利用管道进行进程间通信
- 为了防止出现死锁以及消息冲突,需要进行close处理
- 读写操作传输的值都是实际地址
pipe管道端口不与进程绑定,而是可以更改的;pipe管道端口的作用是固定的,0端口读,1端口写
二、perror函数的使用
perror()是一个C语言标准库函数,用于打印错误信息。它接受一个字符串参数作为错误信息的前缀,并将系统的错误消息附加到该前缀后面。
一般用于打印系统调用的错误,能够自动输出系统调用错误的编码。见下面示例代码:
#include <stdio.h>
#include <errno.h>int main() {FILE *file = fopen("nonexistent_file.txt", "r");if (file == NULL) {perror("Error opening file: ");return 1;}// 其他文件操作...fclose(file);return 0;
}
其输出是:
Error opening file: No such file or directory
三、read、write函数的使用
(1)读取时:要先关闭管道的写入端口,才能从输出端口进行读出
read函数的三个参数分别为:
close(port[1]);
read(port[0],数据,要传输的数据长度);
(2)输出时:
write函数的三个参数分别为:
close(port[0]);
write(port[1],数据,要传输的数据长度);
本次实验
实验内容

实验代码
#include <stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include <wait.h>//三个递归函数的定义,每个函数用一个进程来运行,运行结果利用pipe通信
//f(x)
int fx(int x){if(x <= 0){printf("the number you input must be positive!");return 0;}else if(x == 1){return 1;}else if(x > 1){return fx(x-1) * x;}
}
//f(y)
int fy(int y){if(y <= 0){printf("the number you input must >2!");return 0;}else if(y == 1 || y == 2){return 1;}else if(y > 2){return fy(y-1) + fy(y-2);}
}
//f(x,y)
int fxy(int fx, int fy){return fx + fy;
}int main(int argc,char* argv[]){int pid1; //子进程1int pid2; //子进程2int pid; //父进程int pipe1[2]; //第一个管道:父进程写,子进程读int pipe2[2]; //第二个管道:父进程读,子进程写int result1; //保存f(x)和f(y)的计算结果int result2;int result;int status; //记录子进程状态int x;int y;//从键盘输入x和yprintf("please input number x: ");scanf("%d",&x);printf("\n");printf("please input number y: ");scanf("%d",&y);printf("\n");//开始创建管道if(pipe(pipe1)<0){perror("pipe1 not create");exit(EXIT_FAILURE);}if(pipe(pipe2)<0){perror("pipe2 not create");exit(EXIT_FAILURE);}//创建子进程开始执行操作pid1=fork(); if(pid1<0){ //第一个子进程,注意以下!!!!我在这里踩了坑perror("process1 not create");exit(EXIT_FAILURE);}//子进程1在执行if(pid1==0){//子进程负责在管道1的1端写,父进程在管道1的0端读//所以关掉管道1的0端close(pipe1[0]);result1=fx(x);printf("子进程1完成了运算,f(x)=%d\n",result1);//将运行结果发送出去write(pipe1[1],&result1,sizeof(int));//写完成后,关闭管道close(pipe1[1]);//子进程执行结束exit(EXIT_SUCCESS);}//父进程运行else{waitpid(pid1, &status, 0); //等待子进程运行结束再执行父进程(主动阻塞父进程,也可以让其因为read被动阻塞)printf("我是父进程%d,已经等待子进程%d完成,现开始运行\n",getpid(),pid1);close(pipe1[1]); //在访问共享资源前都要避免互斥//从管道1的0端口获得数值read(pipe1[0],&result1,sizeof(int));close(pipe1[0]);//创建另一个进程2执行f(y)程序printf("父进程%d已获取结果1,先创建新子进程运行f(y)\n ",getpid());pid2=fork();//使用fork()系统调用建立子进程,建立不成功程序退出,执行终止if(pid2 <0){perror("子进程2没有创建成功");exit(EXIT_FAILURE);}//第二个子进程,pipe2[1]用来写if(pid2 == 0){//关掉pipe2[0]端close(pipe2[0]);//计算f(y)result2 = fy(y);printf("子进程2完成了运算,f(y)=%d\n",result2);//发送消息write(pipe2[1],&result2,sizeof(int));close(pipe2[1]);}//父进程else{waitpid(pid2, &status, 0);close(pipe2[1]);//接受第二个子进程从管道里发来的信息read(pipe2[0],&result2,sizeof(int));result = fxy(result1,result2);printf("f(x) = %d\n",result1);printf("f(y) = %d\n",result2);printf("f(x,y) = %d\n",result);//读完成后关闭管道close(pipe2[1]);//父进程执行结束return EXIT_SUCCESS;}}
}
运行结果

踩的坑
1、读只能从端口0进行,写从端口1进行
2、编程思路:对于一个进程它必须只要要完成一个操作单位体,计算一个递归函数就是一个操作单位体
3、
赋值运算优先级小于比较运算:所以if(pid1=fork()>0)此时执行的是if(pid1=(fork()>0)),也就是说pid1并未得到fork()返回的子进程pid而是得到比较运算结果1。
解决方案:1、可以把pid=fork,与pid>0分成两步去实现;2、可以修改if(pid1=fork()>0)为if((pid1=fork())>0)
makefile文件编写
# DEPEND 代替 依赖文件# CC 代替 gcc# CFLAGS 代替 编译命令# PARA 代替 参数# OBJS 代替 目标文件DEPEND=expr_2.cOBJS=expr_2CC=gccCFLAGS=-oexpr_1:$(DEPEND)$(CC) $(DEPEND) $(CFLAGS) $(OBJS)run:$(OBJS)./$(OBJS) clean:rm *.o $(OBJS) -rf

实验感悟
一、进程协作的特点:
- 共享资源:进程协作和通信允许多个进程共享资源,本示例中父子进程共享变量x
- 数据传输:进程可以通过通信机制相互传输数据,以实现信息交换和共享。本实验代码中进程之间传输不同函数运行的结果,从而实现协作
- 进程间控制:进程协作可以通过管道、消息队列、共享内存等实现进程间的控制和协调。本实验中采用管道控制
二、进程通信机制:
目前我们已经学习的有四种类型,如下:
-
管道:管道是一种单向通信机制,用于在具有亲缘关系的进程之间传递数据。它可以通过创建一个管道文件描述符来实现进程间的通信
-
消息队列:消息队列是一种存放消息的容器,进程可以通过发送和接收消息来实现通信。消息队列提供了一种异步通信的方式
-
共享内存:共享内存允许多个进程共享同一块内存区域,进程可以通过读写共享内存来交换数据
-
信号量(Semaphore):信号量是一种用于进程间同步和互斥访问共享资源的机制。进程可以使用信号量来控制对共享资源的访问
其中管道主要用于父子两个进程之间的简单通信,是单向的。实现起来也简单快捷,但是无法处理多个进程之间的复杂协作
三、进程管道通信的具体流程:
-
创建管道:通过调用系统的管道函数,创建一个管道,它会返回两个文件描述符,一个用于读取数据,一个用于写入数据
-
创建子进程:使用系统调用(如fork())创建一个新的子进程
-
父子进程通信:父进程可以通过写入管道的文件描述符将数据发送给子进程,子进程可以通过读取管道的文件描述符接收数据
-
关闭管道:当通信结束后,父进程和子进程都需要关闭管道的文件描述符,释放相关的资源
总结
本文到这里就结束啦~~
本篇文章重点在于利用linux系统的完成操作系统的实验,巩固课堂知识
本篇文章的撰写+实验代码调试运行+知识点细致化学习,共花了本人3h左右的时间
个人觉得已经非常详细啦,如果仍有不够希望大家多多包涵~~如果觉得对你有帮助,辛苦友友点个赞哦~
知识来源:山东大学《操作系统原理实用实验教程》张鸿烈老师编著

相关文章:
操作系统实战(二)(linux+C语言)
实验内容 通过Linux 系统中管道通信机制,加深对于进程通信概念的理解,观察和体验并发进程间的通信和协作的效果 ,练习利用无名管道进行进程通信的编程和调试技术。 管道pipe是进程间通信最基本的一种机制,两个进程可以通过管道一个在管道一…...
哪些情况下会触发MySQL的预读机制?
MySQL的预读机制主要与其底层存储引擎的实现有关,尤其是InnoDB存储引擎。预读(Pre-reading)或预取(Prefetching)是一种性能优化技术,其中数据库系统主动读取可能很快就会被查询到的数据页到缓冲池ÿ…...
react使用谷歌人机验证
在项目中,需要对请求验证,防止被爆破,这里使用的是谷歌的recaptcha-v3。 1.申请谷歌人机验证的api 申请链接,申请完后需要将两个谷歌颁发的key分别写入前,后端的配置环境中,后面会使用. 2.前端部分 前端使用的是viteC…...
java JMH 学习
JMH 是什么? JMH(Java Microbenchmark Harness)是一款专用于代码微基准测试的工具集,其主要聚焦于方法层面的基准测试,精度可达纳秒级别。此工具由 Oracle 内部负责实现 JIT 的杰出人士编写,他们对 JIT 及…...
本地运行AI大模型简单示例
一、引言 大模型LLM英文全称是Large Language Model,是指包含超大规模参数(通常在十亿个以上)的神经网络模型。2022年11月底,人工智能对话聊天机器人ChatGPT一经推出,人们利用ChatGPT这样的大模型帮助解决很多事情&am…...
图像处理:时域、空域、频率的滤波介绍
首先要搞清楚为什么会呈现出不同域的维度,来理解和处理图像,原因是图像的构成有多个维度的信息特点。比如一段视频从时间顺序来看,相邻的2个图像帧绝大部分信息是相同的,这就构成了前向预测的理论基础;比如一帧图像从空…...
TC8002D 是一颗带关断模式的音频功放IC
一、一般概述 TC8002D是一颗带关断模式的音频功放IC。在5V输入电压下工作时,负载(3Ω)上的平均功率 为3 W,且失真度不超过10%。而对于手提设备而言,当VDD作用于关断端时,TC8002D将会进入关断模式,此时的功耗极…...
深度学习之基于Vgg19预训练卷积神经网络图像风格迁移系统
欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 在数字艺术和图像处理领域,图像风格迁移技术一直备受关注。该技术可以将一幅图像的内容和…...
MySQL:多表查询练习
#1.出版社信息 与 图书信息 交叉连接 select * from 出版社信息 cross join 图书信息; #2.从“客户信息”和“订单信息”两张数据表中查询购买了商品的客户信息,要求查询结果显示客户姓名、订单编号、订单状态。 select 客户信息.客户姓名,订单信息.订单编号,订单…...
# 从浅入深 学习 SpringCloud 微服务架构(八)Sentinel(1)
从浅入深 学习 SpringCloud 微服务架构(八)Sentinel(1) 一、sentinel:概述 1、前言 – 服务熔断 Hystrix 的替换方案。 1)2018年底 Netflix 官方宣布 Hystrix 已经足够稳定,不再积极开发 Hys…...
[微信小程序] 入门笔记2-自定义一个显示组件
[微信小程序] 入门笔记2-自定义一个显示组件 0. 准备工程 新建一个工程,删除清空app的内容和其余文件夹.然后自己新建pages和components创建1个空组件和1个空页面. 设定 view 组件的默认样式,使其自动居中靠上,符合习惯.在app.wxss内定义,作用做个工程. /**app.wxss**/ /* 所…...
YOLO代码复现
睿智的目标检测66——Pytorch搭建YoloV8目标检测平台_pytorch_quantization yolov8-CSDN博客 Mask rcnn代码实现_pytorch版_适用30系列显卡_mask rcnn 30显卡-CSDN博客 完整且详细的Yolov8复现训练自己的数据集-CSDN博客...
使用fitten code插件(vscode),替换通义千问,识别需求中的输入输出
今天我们介绍一个工具,具体介绍可以参考我的这篇文章的介绍,支持vs code 插件,Fitten Code是一款由非十科技开发的AI代码助手,旨在通过大模型驱动来提升编程效率和体验-免费神器-CSDN博客https://blog.csdn.net/lijigang100/article/details/137833223?spm=1001.2014.3001…...
vue使用pdfjs-dist在电脑上展示PDF文件
安装 安装的时候一定要带上版本号,这里采用的是2.0.943(因为这个版本对于我目前的项目比较合适可以正常使用,其他版本大概率会报错),当前项目使用的是vue2,vue的版本是2.5.10 npm install pdfjs-dist@2.0.943 查看版本发现这玩意版本非常之多 使用 在使用pdfjs-dist库…...
【网站项目】戒烟网站
🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板ÿ…...
慢性软组织疼痛如何使用DMS深层肌肉刺激仪进行治疗?
使用DMS深层肌肉刺激仪治疗慢性软组织疼痛,可以遵循以下步骤: 准备工作:首先,确保DMS设备已经充电或插上电源,并根据需要调整振动强度和频率。DMS深层肌肉刺激仪是通过快速连续的振动和打击来刺激深层肌肉的设备&#…...
自动化测试常用工具
自动化测试工具是非常重要的。自动化测试工具可以帮助程序员提高工作效率,减少重复劳动和人为错误,提高产品质量。下面我将介绍几个常用的自动化测试工具。 Selenium:Selenium是一个开源的自动化测试框架,用于Web应用程序的自动化…...
【Osek网络管理测试】[TG4_TC4]tWaitBusSleep
🙋♂️ 【Osek网络管理测试】系列💁♂️点击跳转 文章目录 1.环境搭建2.测试目的3.测试步骤4.预期结果5.测试结果1.环境搭建 硬件:VN1630 软件:CANoe 2.测试目的 验证DUT的tWBS时间参数是否符合NM标准 本处规定tWBS在[1350ms,1650ms]范围内符合要求 3.测试步骤…...
java08基础(值传递和引用传递 类和对象)
目录 一. 值传递和引用传递 1. 值传递 2. 引用传递 二. 面向对象思想 三. 类和对象 1. 类 2. 对象 2.1 使用 2.2 成员变量和局部变量区别 2.3 操作成员方法 2.4 this关键字(初识) 2.5 构造方法 (见java09) 一. 值传递和引用传递 1. 值传递 值传递是指在调用函数时将…...
高级数据结构与算法习题(9)
一、判断题 1、Let S be the set of activities in Activity Selection Problem. Then the earliest finish activity am must be included in all the maximum-size subset of mutually compatible activities of S. T F 解析:F。设S是活动选择问题中的一…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...
从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
