# 操作系统实验
## 任务一 : 执行流与协作
### 实验目的
了解作业、进程和线程的运行、同步和通信特性;
了解操作系统提供的进程和线程创建、同步和通信编程接口。
### 实验内容
#### 第一部分
- 执行三个程序模仿作业执行(最简单job_combination.c、计算jobx_cpu.c和I/O死循环jobx_io.cpp); 运行计算jobx_cpu.c和I/O死循环jobx_io.cpp的多个程序实例过任务管理器类工具观察多个计算和I/O死循环的性能变化。
```c++
// job_combination.c
n = atoi(argv[1]), m = atoi(argv[2]);//把命令行两个参数字符串,转化为两个整数值
v = fac(n)/(fac(m)*fac(n-m));//组合计算公式n!/(m!*(n-m)!)
printf ("Value is %d\n", v);
```
![](https://www.writebug.com/myres/static/uploads/2022/3/7/786f0cf856c5079f033110277f7136e8.writebug)
文件名 **job_combination.c**
命令: job_combination.exe 5 3 (0 1 2)
那么, argc = 3
argv[0] = " job_combination.c ";
argv[1] = "5";
argv[2] = "3";
```c++
// jobx_cpu.c
while (1) {
num=-10000000;
for (i=0; i<500000000; i++) num +=1;
// -10000000 + 500000000 = 490000000
printf ("the sum is %d\n", num);
}
```
![](https://www.writebug.com/myres/static/uploads/2022/3/7/22444820f917f17b549fa9c9f20856ee.writebug)
![](https://www.writebug.com/myres/static/uploads/2022/3/7/e3f8ec33179f3c4e7b02af3ac2f4579d.writebug)
循环计算num,最后输出
```c++
// jobx_io.cpp
while (1) {
Sleep(1000);
//windows操作系统的系统调用,表示程序推迟1秒执行,Unix下有类似的系统调用sleep(1)。
printf ("the process wakes up at one second\n");
}
```
![](https://www.writebug.com/myres/static/uploads/2022/3/7/81248b31942f182a655277893451540e.writebug)
![](https://www.writebug.com/myres/static/uploads/2022/3/7/d2bd94206b322e7c58d28be1ccda0718.writebug)
调用windows 头文件的sleep函数,参数为毫秒;执行挂起一秒并循环执行;
- 利用自己程序proc_wordcreate.cpp创建开启一个Word文字处理程序
![](https://www.writebug.com/myres/static/uploads/2022/3/7/b160e6ca571710b68053882e8f304765.writebug)
![](https://www.writebug.com/myres/static/uploads/2022/3/7/d3a1c01cd60050d6d9c7d9172a6dc937.writebug)
- 打开一个word文档进程
![](https://www.writebug.com/myres/static/uploads/2022/3/7/7b61fca7d8036f291423f077536a2203.writebug)
- 通过自己程序创建开启已有的其他应用程序,或者死循环创建word实例,看操作系统的能力;
![](https://www.writebug.com/myres/static/uploads/2022/3/7/26731e46ccff3be43bcbcf44ea022ae4.writebug)
自己设计并完成死循环,不停创建word进程
- 通过包含多个死循环计算函数的多线程程序thread_mprint.cpp观察多线程的运行。
![](https://www.writebug.com/myres/static/uploads/2022/3/7/bec32e4af235b99a4e4848099f25447b.writebug)
![](https://www.writebug.com/myres/static/uploads/2022/3/7/82cf8ea198d5d364a8681b45bc1659f8.writebug)
不同线程使用公共变量nResValue,使循环变成死循环;线程本身是无限循环,要想自己退出,就要获得线程的句柄结束
![](https://www.writebug.com/myres/static/uploads/2022/3/7/2c3c2e2a00757fc132e7ee736fe68c97.writebug)
![](https://www.writebug.com/myres/static/uploads/2022/3/7/6f5be134a0ec3caa230bdf865ade0734.writebug)![](https://www.writebug.com/myres/static/uploads/2022/3/7/2f987024399deb81b0e409743196e39c.writebug)
执行前 执行后
#### 第二部分
- 通过多线程共享全局变量的程序运行观察并发程序带来执行结果不确定问题,如threadx_count.cpp执行不确定结果,而threadx_print.cpp主从线程出现的退出问题;
// threadx_count.cpp
![](https://www.writebug.com/myres/static/uploads/2022/3/7/8cc682b6ab85eaf1bba9ea69a7de84f9.writebug)
当i<500时;
占用cpu,时间片完成现场保护和现场恢复,所以当在一个时间片之内完成的话,多线程共享全局变量,一加一减为0.
![](https://www.writebug.com/myres/static/uploads/2022/3/7/a6ccc3d4846ea696c2dbf278c5e1eded.writebug)
当i<50000000时;
占用cpu,超过一个时间片,会被其他进程抢占,多线程共享全局变量,进程抢占CPU具有不确定性。
![](https://www.writebug.com/myres/static/uploads/2022/3/7/854bc9c638fe5c925abf014e904810a3.writebug)![](https://www.writebug.com/myres/static/uploads/2022/3/7/4c2f5e249314915a47fd7d74fac32bf8.writebug)![](https://www.writebug.com/myres/static/uploads/2022/3/7/30eca233c1e57bd4f969efba970ecfba.writebug)
// threadx_print.cpp
![](https://www.writebug.com/myres/static/uploads/2022/3/7/a1ed93505ffb92135452314a978de536.writebug)
![](https://www.writebug.com/myres/static/uploads/2022/3/7/7e5e3e58ade8de3a84ae76b435a09025.writebug)
LPTHREAD_START_ROUTINE // 指向一个函数,通知某个线程已经开始执行
程序并发,主进程终止带来子进程终止
子线程运行两次,有时运行三个,因为主进程sleep(3000),子线程(1000),所以执行得快会执行三个线程,如果把子线程sleep(900),会一直为三次。
```c++
for(i=0; i<cnt; i++) {
Sleep(900); //windows操作系统的系统调用,表示程序推迟1秒执行
puts ("running thread\n");
}
```
![](https://www.writebug.com/myres/static/uploads/2022/3/7/0bbd8149d44798c3e1fac47ec5b37ba9.writebug)
- 通过互斥(mutex_count.cpp)、临界区(critical_count.cpp)和信号量(semaphore_count.cpp)的接口编程观察并发程序执行问题解决结果;
```c++
// mutex_count.cpp
hMutex=CreateMutex(NULL, FALSE, NULL); //初始互斥量为有信号态
// 创建进程,根据变量i的取值,创建不同进程实现对num的加减
for (i=0; i<NUM_THREAD; i++) {
if(i%2) {
tHandles[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)threadInc,
NULL,0, NULL);
} else {
tHandles[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)threadDes,
NULL,0, NULL);
}
}
//临界资源的利用
int i;
// 进入临界区,hHandle[in]对象句柄。dwMilliseconds[in]定时时间间隔
WaitForSingleObject(hMutex, INFINITE);
for (i=0; i<50000000; i++) num +=1;
//释放临界区
ReleaseMutex(hMutex);
```
![](https://www.writebug.com/myres/static/uploads/2022/3/7/69208356b09525d83d593481036f5e25.writebug)
对一个共享资源num的单独访问
```c++
// critical_count.cpp
int i;
//加锁 接下来的代码处理过程中不允许其他线程进行操作,除非遇到LeaveCriticalSection
EnterCriticalSection(&cs);//进入临界区
for (i=0; i<50000000; i++) num +=1;
//解锁 到EnterCriticalSection之间代码资源已经释放了,其他线程可以进行操作
LeaveCriticalSection(&cs);//释放临界区
```
![](https://www.writebug.com/myres/static/uploads/2022/3/7/b88202c69a3507304e53bb705a2a2c25.writebug)
通过对多线程的串行化来访问公共资源。
```c++
// semaphone_count.cpp
semOne=CreateSemaphore(NULL, 0, 1, NULL);//初始信号量为无信号态,二进制信号量,关闭计算区
semTwo=CreateSemaphore(NULL, 1, 1, NULL);//初始信号量为有信号态,二进制信号量, 释放读数区
// 进入计算区
// hHandle[in]对象句柄。可以指定一系列的对象; dwMilliseconds为INFINITE,对象被触发信号后,函数才会返回。
WaitForSingleObject(semOne, INFINITE);
sum +=num;
// 释放读数区
// 用于对指定的信号量增加指定的值。
ReleaseSemaphore(semTwo, 1, NULL);
```
![](https://www.writebug.com/myres/static/uploads/2022/3/7/f7bb4a4533aa630096fb4b8775813d12.writebug)
为控制一个具有有限数量用户资源。
- 观察用信号量提供生产消费者模型的编程实例(producer_consumer.cpp);
```c++
case_Out=CreateSemaphore(NULL, 0, NUM_CASE, NULL); //初始箱出信号量为0
case_In=CreateSemaphore(NULL, NUM_CASE, NUM_CASE, NULL); //初始箱出信号量为NUM_CASE
hMutex=CreateMutex(NULL, FALSE, NULL); //初始互斥量为有