【HUST】网安|操作系统实验|实验二 进程管理与死锁
目的
1)理解进程/线程的概念和应用编程过程;
2)理解进程/线程的同步机制和应用编程;
任务
1)在Linux下创建一对父子进程。
2)在Linux下创建2个线程A和B,循环输出数据或字符串。
3)在Windows下创建线程A和B,循环输出数据或字符串。
4)在Linux下创建一对父子进程,实验wait同步函数。
5)在Windows下利用线程实现并发画圆/画方。
6)在Windows或Linux下利用线程实现“生产者-消费者”同步控制
7)在Linux下利用信号机制实现进程通信。
8)在Windows或Linux下模拟哲学家就餐,提供死锁和非死锁解法。
1/6/8选做,其余任选其一。
任务1 在Linux下创建一对父子进程。
1. 提示
提示1:分别输出各自的进程号,父进程号和特别的提示字符串信息
提示2:让父进程提前结束或后结束,观察子进程的父进程ID。
提示3:使用PS命令查看进程列表信息,核对进程号,父进程号
2. 任务代码
#include<unistd.h>
#include<stdio.h>
int main()
{
printf("'-'代表父进程输出信息;'+'代表子进程输出信息。\n");
pid_t p1=fork();
if(p1){
printf("- 1. 父进程进程号:%d\n",getpid());
printf("- 1. 创建的子进程进程号:%d\n",p1);
printf("- 3. 暂时挂起父进程以便ps检查结果(10s):\n");
sleep(10);
printf("- 2. 父进程结束\n");
}else{
printf("+ 1. 子进程进程号:%d\n",getpid());
printf("+ 1. 父进程未结束时,子进程的父进程进程号:%d\n",getppid());
printf("+ 3. 暂时挂起子进程以便ps检查结果(11s):\n");
sleep(11);
printf("+ 2. 父进程结束后,子进程的父进程进程号:%d\n",getppid());
printf("+ 3. 子进程继续挂起,同时再用ps检查结果(10s):\n");
sleep(10); //为了让子进程更晚结束
printf("+ 3. 子进程结束\n");
}
return 0;
}
ps
检查指令(我用gcc编译后的程序名称叫做a.out
):
ps -ef|grep a.out
3. 结果及说明
图中输出信息的标号分别对应不同的任务提示。
图中ps结果的第一列为用户名,第二列为pid,第三列为父进程pid。
- 第一次ps检查时:父进程号为27236,子进程号为27237,且子进程的父进程号为27236。
- 第二次ps检查时:父进程已经结束,子进程挂起,它的父进程号变为1。即交给init程序。
- 第三次ps检查时:父子进程都结束,ps显示没有27236、27237的进程在运行。
但是程序似乎并没有结束。为啥?
任务6 在Windows或Linux下利用线程实现“生产者-消费者”同步控制
1. 提示
提示1:使用数组(10个元素)代替缓冲区。2个输入线程产生产品(随机数)存到数组中;3个输出线程从数组中取数输出。
提示2: Windows使用临界区对象和信号量对象,主要函数
EnterCriticalSection | LeaveCriticalSection | WaitForSingleObject | ReleaseSemaphore
提示3:Linux使用互斥锁对象和轻量级信号量对象,主要函数:
sem_wait( ),sem_post( ),pthread_mutex_lock( ),
pthread_mutex_unlock( )
提示4:生产者1的数据:1000-1999 (每个数据随机间隔100ms-1s),生
产者2的数据:2000-2999 (每个数据随机间隔100ms-1s)
提示5:消费者每休眠100ms-1s的随机时间消费一个数据。
提示6:屏幕打印(或日志文件记录)每个数据的生产和消费记录。
2. 任务代码
Linux:
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<time.h>
#include<sys/syscall.h>
#include<semaphore.h>
int share[10]; //共享缓冲区
int indexc=0; //生产者处理的标号(临界资源)
int indexp=0; //消费者处理的标号(临界资源)
sem_t is_empty; //缓冲区中存在空位
sem_t is_full; //缓冲区已满
pthread_mutex_t mutexc = PTHREAD_MUTEX_INITIALIZER; //互斥锁
pthread_mutex_t mutexp = PTHREAD_MUTEX_INITIALIZER; //互斥锁
void* consume(void* arg){
int add=(int)arg;
while(1){
sem_wait(&is_full);
pthread_mutex_lock(&mutexc);
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
int data=ts.tv_nsec%1000+add;
share[indexc]=data;
float sleepTime=0.001*(ts.tv_nsec%900)+0.1;
printf("生产者%ld填第%d块:%d,即将沉睡%fs\n",syscall(SYS_gettid),indexc,data,sleepTime);
indexc++;
if(indexc>9)indexc-=10;
pthread_mutex_unlock(&mutexc);
sem_post(&is_empty);
sleep(sleepTime);
}
}
void* produce(void){
while(1){
sem_wait(&is_empty);
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
float sleepTime=0.001*(ts.tv_nsec%900)+0.1;
pthread_mutex_lock(&mutexp);
printf("消费者%ld取第%d块:%d, 即将沉睡%fs\n",syscall(SYS_gettid),indexp,share[indexp],sleepTime);
indexp++;
if(indexp>9)indexp-=10;
pthread_mutex_unlock(&mutexp);
sem_post(&is_full);
sleep(sleepTime);
}
}
int main(){
sem_init(&is_empty,0,0);//初值为0,用于同步生产者和消费者
sem_init(&is_full,0,9); //初值为9,表示初始缓冲区填10个满
pthread_t idc1,idc2;
pthread_t idp1,idp2,idp3;
pthread_create(&idc1,NULL,(void*)consume,(void*)1000);
pthread_create(&idc2,NULL,(void*)consume,(void*)2000);
pthread_create(&idp1,NULL,(void*)produce,NULL);
pthread_create(&idp2,NULL,(void*)produce,NULL);
pthread_create(&idp3,NULL,(void*)produce,NULL);
pthread_join(idc1,NULL);
pthread_join(idc2,NULL);
pthread_join(idp1,NULL);
pthread_join(idp2,NULL);
pthread_join(idp3,NULL);
return 0;
}
3. 结果及说明
图中的生产者是29108、29109,消费者是29110、29111、29112。沉睡时间为100ms到1s,填入数据随机。
使用了两个信号量is_empty/is_full,两个互斥锁mutexc和mutexp。
其中信号量用于监测缓冲区是否还能继续填入或已满,互斥锁分别用于生产者、消费者的互斥使用临界资源indexc和indexp操作。
任务8 在Windows或Linux下模拟哲学家就餐,提供死锁和非死锁解法。
1. 提示
提示1:同时提供提供可能会带来死锁的解法和不可能死锁的解法。
提示2:可能会带来死锁的解法参见课件。Windows尝试使用临界区对象(EnterCriticalSection,LeaveCriticalSection);Linux尝试使用互斥锁(pthread_mutex_lock, pthread_mutex_unlock)
提示3:完全不可能产生死锁的解法,例如:尝试拿取两只筷子,两只都能拿则拿,否则都不拿。 Windows 尝试使用
WaitForMultipleObjects, WaitForSingleObject和互斥量对象
ReleaseMutex等相关函数) Linux尝试使用互斥锁pthread_mutex_lock,pthread_mutex_trylock等函数。
提示4:[可选]图形界面显示哲学家取筷,吃饭,放筷,思考等状态。
提示5:为增强随机性,各状态间维持100ms-500ms内的随机时长。
2. 死锁任务
死锁代码
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<time.h>
#include<sys/syscall.h>
#include<semaphore.h>
#define thinker_num 5
pthread_mutex_t s[thinker_num]; //筷子
float getRandTime(){ //100ms~500ms
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC,&ts);
return 0.001*(ts.tv_nsec%400)+0.1;
}
void* thinker(void* arg){
int index=(int)arg;
int index2=(index+1)%thinker_num;
float sleepTime;
while(1){
sleepTime=getRandTime();
printf("哲学家%ld正在思考,需时%fs\n",syscall(SYS_gettid),sleepTime);
sleep(sleepTime);
sleepTime=getRandTime();
printf("哲学家%ld正在休息,需时%fs\n",syscall(SYS_gettid),sleepTime);
sleep(sleepTime);
pthread_mutex_lock(&s[index]);
printf("哲学家%ld拿起筷子%d\n",syscall(SYS_gettid),index);
pthread_mutex_lock(&s[index2]);
sleepTime=getRandTime();
printf("哲学家%ld拿起筷子%d,%d,开始吃饭,需时%fs\n",syscall(SYS_gettid),index,index2,sleepTime);
sleep(sleepTime);
pthread_mutex_unlock(&s[index2]);
printf("哲学家%ld放下筷子%d\n",syscall(SYS_gettid),index2);
pthread_mutex_unlock(&s[index]);
printf("哲学家%ld放下筷子%d,%d\n",syscall(SYS_gettid),index,index2);
}
}
int main(){
pthread_t id[thinker_num];
for(int i=0;i<thinker_num;++i)
pthread_create(&id[i],NULL,(void*)thinker,(void*)i);
for(int i=0;i<thinker_num;++i)
pthread_join(id[i],NULL);
return 0;
}
运行结果
解释如图。
3. 不死锁任务
不死锁代码
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<time.h>
#include<sys/syscall.h>
#include<semaphore.h>
#define thinker_num 5
pthread_mutex_t s[thinker_num]; //筷子
sem_t s_full; //最多(thinker-1)个人同时开始吃饭
float getRandTime(){ //100ms~500ms
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC,&ts);
return 0.001*(ts.tv_nsec%400)+0.1;
}
void* thinker(void* arg){
int index=(int)arg;
int index2=(index+1)%thinker_num;
float sleepTime;
while(1){
sleepTime=getRandTime();
printf("哲学家%ld正在思考,需时%fs\n",syscall(SYS_gettid),sleepTime);
sleep(sleepTime);
sleepTime=getRandTime();
printf("哲学家%ld正在休息,需时%fs\n",syscall(SYS_gettid),sleepTime);
sleep(sleepTime);
sem_wait(&s_full);
pthread_mutex_lock(&s[index]);
printf("哲学家%ld拿起筷子%d\n",syscall(SYS_gettid),index);
pthread_mutex_lock(&s[index2]);
sleepTime=getRandTime();
printf("哲学家%ld拿起筷子%d,%d,开始吃饭,需时%fs\n",syscall(SYS_gettid),index,index2,sleepTime);
sleep(sleepTime);
pthread_mutex_unlock(&s[index2]);
printf("哲学家%ld放下筷子%d\n",syscall(SYS_gettid),index2);
pthread_mutex_unlock(&s[index]);
printf("哲学家%ld放下筷子%d,%d\n",syscall(SYS_gettid),index,index2);
sem_post(&s_full);
}
}
int main(){
sem_init(&s_full,0,thinker_num-2); //初值为3,说明最多4个同时开始拿筷子
pthread_t id[thinker_num];
for(int i=0;i<thinker_num;++i)
pthread_create(&id[i],NULL,(void*)thinker,(void*)i);
for(int i=0;i<thinker_num;++i)
pthread_join(id[i],NULL);
return 0;
}
运行结果
增设初值为3的信号量s_full(信号量为0到3时该线程都可以继续执行),保证同一时间开始拿筷子的思考家少于5个,破坏死锁必要条件中的环路条件。
任务5 在Windows下利用线程实现并发画圆/画方。
1. 提示
提示1:圆心,半径,颜色,正方形中心,边长,颜色自己确定。
提示2:圆和正方形边界建议都取720个点。为直观展示绘制过程,每个
点绘制后睡眠0.2秒~0.5秒。
提示3:建议使用VS和MFC或QT对话框类型程序来绘制窗口和图形。
2. 任务代码
环境:Windows10 、VS2019。
进入下面这个网站安装图形库(天地良心,这是我见过的最好安的东西)。
https://www.easyx.cn
其实用原生也能画,但easyx真的酷炫炸了!效果爆炸!还有好多有趣的范例!!!爱了!这可是C++啊!
#include <graphics.h>
#include <conio.h>
// 使用 Bresenham 画圆法
DWORD Circle_Bresenham(LPVOID lpParam)
{
int x = 320, y = 240, r = 90, color = LIGHTBLUE;
int tx = 0, ty = r, d = 3 - 2 * r;
while (tx <= ty)
{
// 利用圆的八分对称性画点
putpixel(x + tx, y + ty, color);
putpixel(x + tx, y - ty, color);
putpixel(x - tx, y + ty, color);
putpixel(x - tx, y - ty, color);
putpixel(x + ty, y + tx, color);
putpixel(x + ty, y - tx, color);
putpixel(x - ty, y + tx, color);
putpixel(x - ty, y - tx, color);
if (d < 0) // 取上面的点
d += 4 * tx + 6;
else // 取下面的点
d += 4 * (tx - ty) + 10, ty--;
Sleep(100);
tx++;
}
return 0;
}
DWORD Square(LPVOID lpParam)
{
int x = 320, y = 240, r = 90, color = RED;
int tx = 0, ty = r;
while (tx <= ty)
{
// 利用正方形的八分对称性画点
putpixel(x + tx, y + ty, color);
putpixel(x + tx, y - ty, color);
putpixel(x - tx, y + ty, color);
putpixel(x - tx, y - ty, color);
putpixel(x + ty, y + tx, color);
putpixel(x + ty, y - tx, color);
putpixel(x - ty, y + tx, color);
putpixel(x - ty, y - tx, color);
Sleep(70); //保证圆和方基本上同时结束
tx++;
}
return 0;
}
// 主函数
int main()
{
HANDLE hThread[2];
DWORD ThreadID;
initgraph(640, 480);
// 测试画圆
hThread[0] = CreateThread(NULL, 0, Circle_Bresenham, NULL, 0, &ThreadID);
// 测试画方
hThread[1] = CreateThread(NULL, 0, Square, NULL, 0, &ThreadID);
WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
// 按任意键退出
_getch();
closegraph();
return 0;
}
3. 结果及说明
没有什么特殊的,就是创建两个线程然后描点。我没按提示来。
绘画过程引起极度舒适!!!
为获得更好的视觉效果,使用正方形和圆的八方对称性,四个边八个方向轮流画。
其中正方形和圆使用了不同的线程。
夹带一张别的。好看!!充分体现了并发的相互覆盖。
【HUST】网安|操作系统实验|实验二 进程管理与死锁的更多相关文章
- 操作系统实验一:进程管理(含成功运行C语言源代码)
目录 操作系统实验一:进程管理 1.实验目的 2.实验内容 3.实验准备 3.1.1进程的含义 3.1.2进程的状态 3.1.3进程状态之间的转换 3.2 进程控制块PCB 3.2.1进程控制块的作用 ...
- 操作系统 资源管理 zookeeper yarn 进程管理 分布式 yarn诞生背景
zookeeper 信息保管员 YARN 简介 https://www.ibm.com/developerworks/cn/data/library/bd-yarn-intro/index.html
- 学习Linux的软件管理、进程管理
一.软件管理 1.使用yum管理软件安装包 1.1什么是yum Yum (全称为:Yellow dog Updater, Modified) 由Duke University团队,修改Yellow D ...
- 进程管理工具uptime,top,htop
进程管理工具uptime,top,htop 一uptime 显示当前时间,系统已启动的时间.当前上线人数,系统平均负载(1.5.10分钟的平均负载,一般不会超过1) 系统平均负载:指在特定时间间隔内运 ...
- linux -- 进程管理和作业控制
一. 作业控制 1. 直接将命令放到后台"执行": & [root @test /root ]# command & 范例: [root @test /root] ...
- Linux操作系统的进程管理和作业管理
Linux操作系统的进程管理和信号 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.lsof命令详解 1>.lsof概述 list open files查看当前系统文件的工 ...
- Linux基础入门(新版)(实验九-实验十二)
实验九 简单文本入门 一.常用的文本处理命令 二.文本处理命令 1.tr 命令 tr 命令可以用来删除一段文本信息中的某些文字.或者将其进行转换. 使用方式: tr [option]...SET1 [ ...
- Windows 8 动手实验系列教程 实验5:进程生命周期管理
动手实验 实验5:进程生命周期管理 2012年9月 简介 进程生命周期管理对构建Windows应用商店应用的开发者来说是需要理解的最重要的概念之一.不同于传统的Windows应用(它们即使在后台仍然继 ...
- 20145221 《Java程序设计》实验报告二:Java面向对象程序设计
20145221 <Java程序设计>实验报告二:Java面向对象程序设计 实验要求 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O. ...
- Java实验报告二:Java面向对象程序设计
Java实验报告二:Java面向对象程序设计 ...
随机推荐
- 【Unity】投影矩阵和线性深度推导
[Unity]投影矩阵和线性深度推导 网络上有很多投影矩阵的推导,也有很多声称是基于 Unity 的,但和我的实测都不一致(现在看来是因为这些文章并不全面),此外有一些 Unity 本身的函数我也搞不 ...
- 2024.11.12随笔&联考总结
前言 心情不好,因为考试时 T2T3 全看错题了,导致 T2 没做出来,T3 一份没得.然后下午打球眼镜架子坏了,回机房才发现被高二的盒了. 但还是稍微写一下总结吧. 总结 感觉我今天做题状态还行,思 ...
- CF889E题解
\(\text{Problem - 889E - Codeforces}\) \(\text{*3000}\) 修正 感谢学长 \(\text{fs}\) 指出状态数原因解释的错误. 题意 给一个序列 ...
- 基于Microsoft.Extensions.VectorData实现语义搜索
大家好,我是Edison. 上周水了一篇 Microsoft.Extensions.AI 的介绍文章,很多读者反馈想要了解更多.很多时候,除了集成LLM实现聊天对话,还会有很多语义搜索和RAG的使用场 ...
- 震撼揭秘:LLM幻觉如何颠覆你的认知!
LLM幻觉 把幻觉理解为训练流水线中的一种涌现认知效应 Prashal Ruchiranga Robina Weermeijer 在 Unsplash 上的照片 介绍 在一个名为<深入剖析像Ch ...
- Web前端入门第3问:前端需要学习哪些技术?
Web前端开发技术学习路径 基础知识 必备 HTML+CSS+JavaScript ,就目前来看,这三板斧是入门前端开发的门槛,无论如何都是逃不掉了. 进阶知识 必须会一门主流的前端框架,比如:Rea ...
- `go install`指令行为分析
分析go install [build flags] [packages]指令做了什么,如何实现安装GO软件,我们如何编写一个软件使得可以使用该指令安装自己编写的程序. 参考go官方文档 安装位置 $ ...
- nuxt,从开发到线上部署
起因: 前段时间,同组小伙伴使用vue做了一个小后台,使一位后端沉寂多年得求道之心又躁动了起来...然后,当我和这个躁动起来得后端要合作重构一个网站得时候,后端小哥哥说,就知道套页面,套页面,不用新技 ...
- 基础指令:sudo提权、通配符、特殊符号、stat命令、id命令、正则表达式
目录 4.4 sudo提权 授权的两种方法 4.4 通配符-查文件 4.5 特殊符号 4.6 stat输出文件的详细内容 4.7 id命令查看用户基本信息 4.8 正则表达式 4.8.1 符号 ^ 4 ...
- linux 日常工作常用软件(持续更新)
1.开发工具:jetbrain全家桶,先安装jetbrain toolbox,从其中安装,eclipse.dbeaver.sqlliteman.anypoint studio.spring tool ...