【作业】操作系统实验一:进程和线程
文章目录
- 实验内容
- 一、进程的创建
- 1、编辑源程序
- 2、编辑结果
- 3、编译和运行程序
- 4、解释运行结果
- 二、进程共享
- 1、运行
- 2、解释运行结果
- 三、进程终止
- 1、运行
- 2、解释运行结果
- 四、进程同步
- 1、运行
- 2、解释运行结果
- 五、Linux中子进程映像的重新装入
- 1、运行
- 2、解释运行结果
- 六、线程
- 1、运行
- 2、解释运行结果
- 七、共享资源的互斥访问
- 1、运行
- 2、解释运行结果
实验内容
一、进程的创建
编写一段源程序,使用系统调用fork()创建子进程,当此程序运行时,在系统中有父进程和子进程在并发执行。观察屏幕上的显示结果,并分析原因(源代码:forkpid.c)。
1、编辑源程序
2、编辑结果
3、编译和运行程序
4、解释运行结果
假设命令./forkpid
创建的进程称为A,则A中的fork()
调用会创建一个子进程,称为B。
fork()
函数只在父进程A中成功被调用一次,但是在A和B两个进程中都会有返回值。成功创建子进程后,在父进程A中,fork
的返回值为创建的子进程的pid;在子进程中,fork
的返回值为0。
因此,if(p1==0)
后面的代码块会在子进程中被执行,而else
后面的代码块会在父进程中被执行。由上述运行结果可知,进程的创建关系如下。
pid: 389733 --> 390075(A) --> 390076(B)
那么还有一个问题,上述运行结果中的My parent is 1
,岂不是说B进程的父进程pid是1?这不是矛盾了吗?根据我所查阅的资料,这是因为父进程A比子进程B先结束,B中查找自己的父进程时,父进程A已经不在了,会使用pid为1的进程代替。在Linux中,pid号为1的进程是所有进程的祖先进程。
参考:
- https://blog.csdn.net/lein_wang/article/details/81946108 - 为什么父进程id是1
- https://www.cnblogs.com/alantu2018/p/8526970.html - linux的 0号进程 和 1 号进程
二、进程共享
父进程创建子进程后,父子进程各自分支中的程序各自私有,其余部分,包括创建前和分支结束后的程序段,均为父子进程共享。(源代码:forkshare_1.c)
1、运行
2、解释运行结果
根据运行结果我们很容易看出,进程调度顺序和上一节中一样,是先调度父进程再调度字进程。其中前三个字母xay
是父进程的输出,后面三个字母xby
则是子进程的输出。
可令人困惑不解的是,子进程不也应该从fork()
返回的地方开始运行吗?那为什么'x'
会被输出两次?我一时间有些蒙圈。
在查找资料后,我尝试了另一个示例,其中仅仅是将putchar('x')
换成了printf("x\n")
,而最主要的区别就是多了一个\n
。下面是代码及其运行结果。
子进程会从fork()
返回的地方开始执行,没有错。问题出在stdout
缓冲区的机制上,在输出xayxby
的示例中,运行putchar('x')
语句并没有直接将x
写到屏幕上,而仅仅是将x
放到了缓冲里。随后运行fork()
创建子进程,会将父进程的stdout
缓冲区也复制一份,而复制的缓冲区中就包含了刚刚放入的x
。这便是两个x
从何而来。
而如果打印的内容中包含了\n
,就会马上将内容写到屏幕上并刷新缓冲区,这便是为什么第二个示例中x
只有一个。
参考:
- https://blog.csdn.net/koches/article/details/7787468 - linux中fork–子进程是从哪里开始运行
三、进程终止
如果子进程在其分支结束处使用了进程终止exit()系统调用而终止执行,则不会再执行分支结束后的程序段。(源代码:forkshare_2.c)
1、运行
2、解释运行结果
子进程因为在分支中输出b
后就exit()
结束进程了,因此子进程不会输出后面的y
,其它与上一节情况相同,不作多解释。
四、进程同步
当父进程有许多任务要做时,往往会针对每一个任务创建一个子进程去完成,然后再等待每一个子进程的终止。其同步关系是父进程等待子进程 (源代码:wait.c)。
实现的方法是:1)子进程终止时执行exit()向父进程发终止信号,2)父进程使用wait()等待子进程的终止。
1、运行
2、解释运行结果
前面三个程序都是父进程输出的 va
在前而子进程输出的b
在后,这次ba
换子进程的输出在前了。
父进程中掉用wait(0)
函数会立即阻塞自己,并等待子进程的退出。注意阻塞和空循环等待是有区别的,进程阻塞自己后会交出cpu的控制权。
不过,wait(0)
和exit(0)
中都有一个0
,这个0是什么意思呢?其实这两个零并不是同一个东西,wait的函数头原型是int wait(int *status)
,参数是一个int类型的指针,wait(0)
其实相当于wait(NULL)
,这个参数为NULL
表示我们不关心子进程是如何退出的,只要退出就行。而如果你写成wait(1)
,那么编译时候就会报错了,因为类型不匹配。
而exit(0)
的参数是退出码,你完全可以使用任性地使用exit(123)
退出子进程,并在父进程中仍然以wait(0)
接收子进程的终止信号。
参考:
- https://zhuanlan.zhihu.com/p/549981187 - 入门篇:进程等待函数wait详解
- https://blog.csdn.net/qustdjx/article/details/7704323 - wait()以及wait(&status)\ waitpid()
- https://zhuanlan.zhihu.com/p/647776823 - Linux Shell 中的各种退出码
- https://www.cnblogs.com/shikamaru/p/5359731.html - exit(0)、exit(1)、和return
五、Linux中子进程映像的重新装入
创建一个子进程,并给它加载程序,其功能是显示“I am a child”。设被加载的程序路径名为./child。分析:由于子进程需要加载的程序比较简单,不带参数,所以可以使用execl()实现加载。
1、运行
./child_parent.c
:
./child.c
:
2、解释运行结果
我在./child_parent.c
文件的execl()
语句后加了一个printf()
语句,这样也行能够更好地体现execl
的运行机制。
上述源代码中共有两个printf
语句,但最终只有./child.c
中的printf
语句产生了输出。这是因为,execl
加载进程时并不会新建一个进程,而是使用传入路径中的程序覆盖当前进程,并从新进程的main
函数开始执行。覆盖后,使用fork()
创建的那个程序,当然也包括其中的printf("I am child A\n")
,已经不存在了。
使用execl
,子进程可以更好地干自己的活,而不只是作为父进程的拷贝。
参考:
- https://blog.csdn.net/bao_bei/article/details/48287945 - Linux下execl函数学习_linux的execl函数
六、线程
Linux 系统下的多线程遵循 POSIX 线程接口,称为 pthread。编写 Linux 下的多线程程式,需要使用头文档 pthread.h,连接时需要使用库 libpthread。顺便说一下,Linux 下pthread 的实现是通过系统调用 clone()来实现的。clone()是 Linux 所特有的系统调用,他的使用方式类似 fork,关于 clone()的周详情况,有兴趣的读者能够去查看有关文档说明。下面我们展示一个最简单的多线程程序 pthread_create.c。
1、运行
2、解释运行结果
// 线程创建函数的函数头
int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*strat_routine)(void *), void *arg)
返回值(int):创建成功时返回0,失败时返回错误码。
参数:
- thread:前面先创建了空的线程标识符
pthread_t id1
,这里传入&id1
告诉函数要将新建线程的标识符放到哪里。可以对比scanf("%d", &x)
的用法。 - attr:线程的属性,传入
NULL
表示默认。 - start_routine:线程要执行的函数的指针,上述示例代码中实参前的
(void*)
大概是强制类型转换为指针的意思。函数头中那一串void *(*strat_routine)(void *)
你可能看着有点晕,感觉自己的C语言功底不太够用了,抱歉我也是。 - arg:线程要执行的函数所需要的参数,传入
NULL
意思是不需要参数。
我在main函数return前添加了一个printf
语句。
pthread_join
的作用是让主线程进入阻塞态,等待子线程运行结束后,主线程再接着运行。否则,从子线程创建时开始,主线程与两个子线程就开始并发执行,当主线程很快运行结束并return后,整个进程也会结束,即子线程跟着结束,都来不及在屏幕上打印信息。
可以看到输出中交替打印两个字符串各四次后,最后打印出来的是This is main thread.
。
参考:
- https://blog.csdn.net/sevens_0804/article/details/102823184 - Linux多线程操作pthread_t
- https://www.jb51.net/article/176510.htm - 简单了解C语言中主线程退出对子线程的影响
七、共享资源的互斥访问
创建两个线程来实现对一个数的递加 pthread_example.c
1、运行
例程1:
代码太长,截图不便,我直接贴文本吧。
#include<pthread.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/time.h>
#include<string.h>
#include<unistd.h>
#define MAX 10pthread_t thread[2];
pthread_mutex_t mut;
int number=0;
int i;void thread1(){printf("thread1: I'm thread 1\n");for(i=0;i<MAX;i++){printf("thread1: number=%d\n",number);pthread_mutex_lock(&mut);number++;pthread_mutex_unlock(&mut);sleep(2);}pthread_exit(NULL);
}
void thread2(){printf("thread2: I'm thread 2\n");for(i=0;i<MAX;i++){printf("thread2: number=%d\n",number);pthread_mutex_lock(&mut);number++;pthread_mutex_unlock(&mut);sleep(3);}pthread_exit(NULL);
}
void thread_create(){int temp;memset(&thread,0,sizeof(thread));if( (temp=pthread_create(&thread[0],NULL,(void*)thread1,NULL)) != 0){printf("create thread1 failed!\n");}else{printf("create thread1 success!\n");}if( (temp=pthread_create(&thread[1],NULL,(void*)thread2,NULL)) != 0){printf("create thread2 failed!\n");}else{printf("create thread2 success!\n");}
}
void thread_wait(){if(thread[0]!=0){pthread_join(thread[0],NULL);printf("thread1 end!\n");}if(thread[1]!=0){pthread_join(thread[1],NULL);printf("thread2 end!\n");}
}
int main(){pthread_mutex_init(&mut,NULL);printf("I am main, I am creating thread!\n");thread_create();printf("I am main, I am waiting thread end!\n");thread_wait();return 0;
}
2、解释运行结果
进程是资源分配的基本单位,线程是调度的基本单位。上述代码中,int number
和int i
都是被两个线程所共享的变量。利用mutex
可以实现对共享资源的互斥访问,这个比较容易理解,但是上面的运行结果中,可能有些令人疑惑的地方。
1、为什么会打印两次"number=0",这正常吗?
是正常的,比如在如下图所示的执行顺序(但不唯一)中,就会出现打印两次"number=0"然后打印"number=2"的情况。注意sleep()
会直接让当前进程阻塞,因此下图中在sleep
处都是拐点。
2、为什么thread1连续输出了两个数"number=6"和"number=7"?
对进程的同步与互斥有些模糊时,容易产生这样的疑问。运行结果中,大部分输出都是thread1和thread2交替的,只有6和7这里是同一个线程连续输出两次,以至于觉得本应该交替输出、而连续输出是异常的。
其实不是,上述thread1的代码中是sleep(2)
,而thread2的代码中是sleep(3)
。如果你将thread1中的sleep(2)
修改成sleep(1)
,可以观察到大部分时候都是thread1在连续输出。总之,代码中只是使用mutex实现了对共享资源的互斥访问而已,并没有实现同步。
3、验证lock与unlock的作用。
到这里,我只知道mutex可以实现对共享资源(全局变量number)的互斥访问,程序也确实看起来正常运行。不过,它只循环了10次呢。而且不知道你有没有发现一个问题,另一个全局变量i,不也同样是thread1和thread2两个线程的共享资源吗?
让我们将总循环次数提到10万吧:#define MAX=100000
,同时将阻塞时间缩到sleep(0.001)
让它运行得快一点。然后重新编译并运行一下我们的代码:
例程2:
看!最后结果是number=100018
,而并不是我们所设置的100000,那么很可能就是由于对于共享变量 i 的访问发生了冲突。我将例程2运行了5次,记录每次最后的number值如下:
100018 100017 100011 100024 100009
为了验证确实是变量 i 导致的问题,而不是其它的什么原因——比如上帝在你的计算机里边掷骰子,我们不妨给 i 也加上互斥。
例程3:
我新增了一个新的用于互斥的变量mut_i
,并在两个线程函数的循环中都改用lock
和unlock
来保护记录循环迭代的i++
语句。
我同样将这个修改后的例程3运行了5次,其中有四次是正确的结果number=100000
。可很遗憾,我也不知道在第一次运行中为什么会出现连续两个number=99999
,不过我觉得这不一定是资源互斥中所导致的问题。
总之,凡有赋值的操作在多线程环境都要加锁,不论是上述的number++
,还是i++
。因为,它们都不是原子操作,从机器指令的层面来看,一个高级语言中的i++
包含多个步骤,而一条语句还没执行完,可能就已经发生中断,转而去执行另一个线程了。
4、在例程2和例程3的执行结果中,每次最终的number值都是偏大,为什么不会偏小呢?
例程4:
下面我解除了对于变量 number 的互斥保护,而保持对 i 的互斥保护。
可以看到我连续运行五次结果中,number 每次都是比10万要偏小。这个具体的细节,其实回忆一下以前数据库并发控制中丢失修改导致的数据不一致问题,就明白了,情况如下图所示。而当 i 偏小的时候,i 的递增次数就小于实际迭代次数,于是导致了 number 的结果偏大。
参考:
- https://blog.csdn.net/JMW1407/article/details/108318960 - int i =1 是原子操作吗?i++是原子操作吗?
相关文章:

【作业】操作系统实验一:进程和线程
文章目录 实验内容一、进程的创建1、编辑源程序2、编辑结果3、编译和运行程序4、解释运行结果 二、进程共享1、运行2、解释运行结果 三、进程终止1、运行2、解释运行结果 四、进程同步1、运行2、解释运行结果 五、Linux中子进程映像的重新装入1、运行2、解释运行结果 六、线程1…...
Linux 环境删除Conda
你可以按照以下步骤操作来删除Conda: 首先,停止所有conda环境。在终端中运行以下命令: conda deactivate然后使用以下命令获取conda安装的路径: which conda如果成功安装了conda,该命令输出的路径应该是类似于这样的&a…...

uni-app(1)pages. json和tabBar
第一步 在HBuilderX中新建项目 填写项目名称、确定目录、选择模板、选择Vue版本:3、点击创建 第二步 配置pages.json文件 pages.json是一个非常重要的配置文件,它用于配置小程序的页面路径、窗口表现、导航条样式等信息。 右键点击pages,按…...

window系统vscode 编译wvp前端代码
下载代码 wvp-GB28181-pro: WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的网络视频平台,负责实现核心信令与设备管理后台部分,支持NAT穿透,支持海康、大华、宇视等品牌的IPC、NVR、DVR接入。支持国标级联,支持rtsp/rtmp等…...

获取虎牙直播源
为了今天得LOL总决赛 然后想着下午看看 但是网页看占用高 就想起来有个直播源 也不复杂看了大概一个小时 没啥问题 进入虎牙页面只有 直接F12 网络 然后 看这个长条 一直在获取 发送 那就选中这个区间 找到都是数字这一条 如果直接访问的话会一直下载 我这都取消了 然后 打开…...

Halcon (2):Halcon基础知识
文章目录 文章专栏视频资源前言Halcon文档案例学习结论 文章专栏 Halcon开发 视频资源 机器视觉之C#联合Halcon 前言 本章我们主要讲解Halcon的基础语法 Halcon文档 按下F1,就可以看到Halcon的文档,不过都是纯英文的 如果不清楚参数如何使用&#x…...
测不准原理
测不准原理 算符的对易关系 commutation relation 测不准原理的矢量推导 Schwarz inequality: 设对易关系: 设一个新态: 投影: 那么有: 代回Schwarz inequality 即可证明:...

微机原理_12
一、单项选择题(本大题共15小题,每小题3分,共45分。在每小题给出的四个备选项中,选出一个正确的答案。〕 十进制正数56的 8位二进制补码是()。 A. 00011001 B. 10100110 C. 10011001 D. 00100110 若栈顶的物理地址为20100H,当执行完指令PUSH…...

设计模式(5)-使用设计模式实现简易版springIoc
自定义简易版springIoc 1 spring使用回顾 自定义spring框架前,先回顾一下spring框架的使用,从而分析spring的核心,并对核心功能进行模拟。 数据访问层。定义UserDao接口及其子实现类 public interface UserDao {public void add(); }public…...

数据结构与集合源码
我是南城余!阿里云开发者平台专家博士证书获得者! 欢迎关注我的博客!一同成长! 一名从事运维开发的worker,记录分享学习。 专注于AI,运维开发,windows Linux 系统领域的分享! 本…...

nodejs+vue面向中小学课堂教学辅助软件系统的设计与实现-微信小程序-安卓-python-PHP-计算机毕业设计
主要功能有,管理员通过后台会对此教学辅助进行审核,管理员在还可以进行首页、个人中心、学生管理、教师管理、班级信息管理、科目名称管理、课程信息管理、教学资料管理、作业信息管理、作业提交管理、作业成绩管理、在线考试管理、试题管理、考试管理、…...

智能配电系统解决方案
智能配电系统解决方案是一种集成了先进技术和智能化功能的配电系统,它能够提高电力系统的效率、可靠性和安全性。力安科技智能配电系统解决方案依托电易云-智慧电力物联网,具体实施的方案如下: 智能化设备和传感器:采用智能化的开…...

Python基础入门---conda 如何管理依赖包以及复制相同环境的
文章目录 创建虚拟环境:创建虚拟环境并指定Python版本:安装依赖包:从环境导出依赖包清单:从依赖包清单创建环境:复制环境:移植环境:在Conda中,你可以使用conda create命令来创建和管理虚拟环境,而使用conda install命令来安装和管理依赖包。以下是一些基本的命令和步骤…...

JVM jstat 查看内存新生代老年代回收情况,排查oom
jstat 命令 jstat - [-t] [-h] [ []] option:我们经常使用的选项有gc、gcutil vmid:java进程id interval:间隔时间,单位为毫秒 count:打印次数 每秒打印一次 jstat -gc 9162 1000S0C:年轻代第一个survivor的容量…...

Postman启动问题:Could not open Postman
Postman启动问题:Could not open Postman 状态,在单击Postman之后一直在转圈圈,无法正常启动。 细心的朋友会发现,右下角 会经常出现防火墙关闭等提示信息,表示该程序,在向外链接。 Error Could not open…...

Golang起步篇(Windows、Linux、mac三种系统安装配置go环境以及IDE推荐以及入门语法详细释义)
Golang起步篇 Golang起步篇一. 安装Go语言开发环境1. Wondows下搭建Go开发环境(1). 下载SDK工具包(2). 解压下载的压缩包,放到特定的目录下,我一般放在d:/programs下(路径不能有中文或者特殊符号如空格等)(3). 配置环境变量步骤1:先打开环境变…...
Error message “error:0308010C:digital envelope routines::unsupported“
1.降级到 Node.js v16。 您可以从 Node.js 的 website 重新安装当前的 LTS 版本。 您也可以使用 nvm。对于 Windows,请使用 nvm-windows。 2.启用传统 OpenSSL 提供程序。 在类 Unix 系统(Linux、macOS、Git bash 等)上: exp…...

解决java在idea运行正常,但是打成jar包后中文乱码问题
目录 比如: 打包命令使用utf-8编码: 1.当在idea中编写的程序,运行一切正常.但是当被打成jar包时,执行的程序会中文乱码.产生问题的原因和解决方案是什么呢? 一.问题分析 分别使用idea和jar包形式打印出System中所有的jvm参数---代码如下: public static…...

数据结构-插入排序+希尔排序+选择排序
目录 1.插入排序 插入排序的时间复杂度: 2.希尔排序 希尔排序的时间复杂度: 3.选择排序 选择排序的时间复杂度: 所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的…...
微信小程序数据传递的方式-页面数据的存取
我们在把数据显示到页面的时候,为了实现良好的互动,都希望在用户点击某个栏目后,获取这个栏目的捆绑数据,然后执行后续的操作。 例如,从数据库里取出对应的记录后,显示在页面上,是一条条的大横条…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...

关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...