目的

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。

  1. 第一次ps检查时:父进程号为27236,子进程号为27237,且子进程的父进程号为27236。
  2. 第二次ps检查时:父进程已经结束,子进程挂起,它的父进程号变为1。即交给init程序。
  3. 第三次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】网安|操作系统实验|实验二 进程管理与死锁的更多相关文章

  1. 操作系统实验一:进程管理(含成功运行C语言源代码)

    目录 操作系统实验一:进程管理 1.实验目的 2.实验内容 3.实验准备 3.1.1进程的含义 3.1.2进程的状态 3.1.3进程状态之间的转换 3.2 进程控制块PCB 3.2.1进程控制块的作用 ...

  2. 操作系统 资源管理 zookeeper yarn 进程管理 分布式 yarn诞生背景

    zookeeper 信息保管员 YARN 简介 https://www.ibm.com/developerworks/cn/data/library/bd-yarn-intro/index.html

  3. 学习Linux的软件管理、进程管理

    一.软件管理 1.使用yum管理软件安装包 1.1什么是yum Yum (全称为:Yellow dog Updater, Modified) 由Duke University团队,修改Yellow D ...

  4. 进程管理工具uptime,top,htop

    进程管理工具uptime,top,htop 一uptime 显示当前时间,系统已启动的时间.当前上线人数,系统平均负载(1.5.10分钟的平均负载,一般不会超过1) 系统平均负载:指在特定时间间隔内运 ...

  5. linux -- 进程管理和作业控制

    一. 作业控制 1. 直接将命令放到后台"执行": &  [root @test /root ]# command & 范例: [root @test /root] ...

  6. Linux操作系统的进程管理和作业管理

    Linux操作系统的进程管理和信号 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.lsof命令详解 1>.lsof概述 list open files查看当前系统文件的工 ...

  7. Linux基础入门(新版)(实验九-实验十二)

    实验九 简单文本入门 一.常用的文本处理命令 二.文本处理命令 1.tr 命令 tr 命令可以用来删除一段文本信息中的某些文字.或者将其进行转换. 使用方式: tr [option]...SET1 [ ...

  8. Windows 8 动手实验系列教程 实验5:进程生命周期管理

    动手实验 实验5:进程生命周期管理 2012年9月 简介 进程生命周期管理对构建Windows应用商店应用的开发者来说是需要理解的最重要的概念之一.不同于传统的Windows应用(它们即使在后台仍然继 ...

  9. 20145221 《Java程序设计》实验报告二:Java面向对象程序设计

    20145221 <Java程序设计>实验报告二:Java面向对象程序设计 实验要求 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O. ...

  10. Java实验报告二:Java面向对象程序设计

    Java实验报告二:Java面向对象程序设计                                                                               ...

随机推荐

  1. GUI编程之Swing

    窗口 面板  package com.yeyue.lesson04; ​ import javax.swing.*; import java.awt.*; ​ public class JFrameD ...

  2. RMAN备份时遇到ORA-48132 &ORA-48170且备份变慢案例

    现象描述: 环境: 操作系统:Red Hat Enterprise Linux release 8.10 数据库版本: Oracle 19.24.0.0.0 企业版 备份作业在执行RMAN备份时,告警 ...

  3. 解决Typecho文章cid不连续的教程

    Typecho下文章编号(cid)不连续,虽然不影响什么,也无关紧要,但是对于有强迫症的人(比如我)来说,真的是无法忍受.还好有大佬提供了解决办法. 将以下代码保存为php文件,上传至网站根目录,在浏 ...

  4. Springboot - [05] 彩蛋~

    题记部分 彩蛋一:如何更换Springboot启动时的logo (1)访问 https://www.bootschool.net/ascii-art/search,搜索到佛祖的ASCII艺术字(图)集 ...

  5. SpringBoot - [00] 注解大全

    原文链接:https://mp.weixin.qq.com/s/DgNhohtJyEq4vMGEzqrP8A @SpringBootApplication 这个注解用于标识一个SpringBoot应用 ...

  6. DW002 - 数据仓库模型设计

    数据模型 关系模型与维度模型 常见数据模型设计方法 数据模型 1. 什么是数据模型 模型 - Model 模型是指对于某个实际问题或者客观事物.规律进行抽象后的一种形式化表达方式 比如地图.建筑设计沙 ...

  7. 大数据之路Week08_day03 (Hive的动态分区和分桶)

    一.动态分区 先来说说我对动态分区的理解与一些感受吧. 由于我们通过hive去查询数据的时候,实际还是查询HDFS上的数据,一旦一个目录下有很多文件呢?而我们去查找的数据也没有那么多,全盘扫描就会浪费 ...

  8. 记一次Microsoft.Toolkit.Mvvm(MVVM Toolkit)的兼容性问题

    今天在目标框架为framework4.6.1的wpf项目中使用Microsoft.Toolkit.Mvvm7.1.1出现了一个比较怪异的编译时错误,前提是打开了 工具>选项>环境>预 ...

  9. CentOS7脚本检测SpringBoot项目JAR包变化后自动重启

    #!/bin/bash # 文件目录 fileDir=/usr/local/project/back logDir=/usr/local/project/logs # 设置需要检测的文件路径 file ...

  10. minecraft mods descrip

    1. [Advanced Finders]矿物探测器 mod 显示玩家周围附近矿石的方向(指针显示水平面上可到达的矿石) 探测地下深部矿脉(箭头显示最近矿脉的方向(上/下)) 发现大型矿床时发出信号( ...