实验内容
本次实验包括如下内容:
- 基于模板
process.c
编写多进程的样本程序,实现如下功能: + 所有子进程都并行运行,每个子进程的实际运行时间一般不超过 30 秒; + 父进程向标准输出打印所有子进程的 id,并在所有子进程都退出后才退出; - 在
Linux0.11
上实现进程运行轨迹的跟踪。 + 基本任务是在内核中维护一个日志文件/var/process.log
,把从操作系统启动到系统关机过程中所有进程的运行轨迹都记录在这一 log 文件中。 - 在修改过的 0.11 上运行样本程序,通过分析 log 文件,统计该程序建立的所有进程的等待时间、完成时间(周转时间)和运行时间,然后计算平均等待时间,平均完成时间和吞吐量。可以自己编写统计程序,也可以使用 python 脚本程序——
stat_log.py
(在/home/teacher/
目录下) ——进行统计。 - 修改 0.11 进程调度的时间片,然后再运行同样的样本程序,统计同样的时间数据,和原有的情况对比,体会不同时间片带来的差异。
实验内容
解压环境文件
cd /home/shiyanlou/oslab
tar -zxvf hit-oslab-linux-20110823.tar.gz -C /home/shiyanlou
编写process.c
文件
由于process.c
处于/home/teacher/
文件夹下,且为只读格式,因此需要挂载虚拟机进行修改。
cd ~/oslab
sudo ./mount-hdc
进入~/oslab/hdc/usr/root/
目录编写process.c
文件
cd ~/oslab/hdc/usr/root/
gedit process.c
编写内容如图1所示:
函数内各组件作用:
last
: 函数实际占用CPU和I/O的总时间,不含在就绪队列中的时间,>=0是必须的cpu_time
: 一次连续占用CPU的时间,>=0是必须的io_time
: 一次I/O消耗的时间,>=0是必须的- 如果
last > cpu_time + io_time
,则往复多次占用CPU和I/O,直到总运行时间超过last为止 - 所有时间的单位为秒
核心函数cpuio_bound()
- 所有子进程都并行运行,每个子进程的实际运行时间一般不超过30秒;
- 父进程向标准输出打印所有子进程的id,并在所有子进程都退出后才退出;
日志文件建立输出
为了让linux-0.11
在启动之后就创建process.log
并开始记录,首先需要在~/oslab/linux-0.11/init/
下编写main.c
函数,编写内容如图2:
为了向日志文件输出信息,需要编写fprintk
函数,由于在内核状态下,write()
功能失效,因此参考printk()
和sys_write()
函数,在~/oslab/linux-0.11/kernel/
中添加函数到printk.c
中。
最终希望得到的效果如下:
// 向stdout打印正在运行的进程的ID
fprintk(1, "The ID of running process is %ld", current->pid);
// 向log文件输出跟踪进程运行轨迹
fprintk(3, "%ld\t%c\t%ld\n", current->pid, 'R', jiffies);
此时,系统一旦启动就会创建一个process.log
文件,并且可以向process.log
文件进行打印输出。而下一步就是打印了。
日志文件修改
Linux 0.11 支持四种进程状态的转移:就绪到运行、运行到就绪、运行到睡眠和睡眠到就绪,此外还有新建和退出两种情况。其中就绪与运行间的状态转移是通过 schedule()
(调度算法所在位置)完成的;运行到睡眠依靠的是 sleep_on()
和 interruptible_sleep_on()
,还有进程主动睡觉的系统调用 sys_pause()
和 sys_waitpid()
;睡眠到就绪的转移依靠的是 wake_up()
。所以需要在这些函数的适当位置插入适当的语句。
我们需要进入linux-0.11
下的kernel
文件夹,在其中分别对fork.c
、sched.c
、exit.c
进行修改,修改截图如图3、4、5所示。
cd ../
cd kernel
gedit fork.c
gedit sched.c
gedit exit.c
测试+输出结果
现在完成了所有的准备工作,需要将/root
文件夹下的process.c
复制到home/teacher/
下
cd ../../../
cd /home/teacher/
sudo cp ~/oslab/hdc/usr/root/process.c ./
这样就完成了/home/teacher
文件夹下process.c
的修改。
之后需要卸载虚拟机并编译内核:
cd ../../
cd ~/oslab/
sudo umount hdc
cd ~/oslab/linux-0.11/
make all
之后就可以进入boches
:
cd ~/oslab
./run
在Boches
中输入下列命令。得到结果如图6所示
gcc -o process process.c
./process
sync
ll /var
至此,成功输出了结果。
查看log
文件如图所示。
调度算法修改
为了实现数据统计,需要在终端中输入下列程序:
cd ./hdc/var/
chmod +x stat_log.py
python stat_log.py process.log 7 8 9 10 11 -g
时间片的初始值是进程0的priority,是在linux-0.11/include/linux/sched.h
的宏 INIT_TASK
中定义的,如下:我们只需要修改宏中的第三个值即可,该值即时间片的初始值。
该部分实验输出结果如下 :
时间片为15的情况:
时间片为150的情况:
实验报告
- 结合自己的体会,谈谈从程序设计者的角度看,单进程编程和多进程编程最大的区别是什么?
- 两者的执行方式存在差异:单进程编程是一个进程从上到下顺序进行;多进程编程可以通过并发执行,需要采取某种调度算法。
- 利用率存在差异:单进程编程的CPU利用率低,因为单进程在等待I/O时,CPU是空闲的;多进程编程的CPU利用率高,因为当某一进程等待I/O时,CPU会去执行另一个进程,因此CPU的利用率高。
- 数据同步方式存在差异:单线程的数据是同步更改的,而多线程数据相对独立。
- 你是如何修改时间片的?仅针对样本程序建立的进程,在修改时间片前后,log 文件的统计结果(不包括 Graphic)都是什么样?结合你的修改分析一下为什么会这样变化,或者为什么没变化?
- 修改
include/linux/sched.h/INIT_TASK
中的priority就可以改变时间片大小。 - 时间片变小,进程因时间片到时产生的进程调度次数变多,该进程等待时间越长;时间片增大,进程因中断或者睡眠进入的进程调度次数也增多,等待时间随之变长。当达到一定值后不再变化。
- 在一定的范围内,平均等待时间,平均完成时间的变化随着时间片的增大而减小。这是因为在时间片小的情况下,cpu将时间耗费在调度切换上,所以平均等待时间增加。超过一定的范围之后,随着时间片的修改,吞吐量始终没有明显的变化,这是因为在单位时间内,系统所能完成的进程数量是不变且一定的。