16281005操作系统实验二

实验题目

一、实验目的:

  • 加深对进程概念的理解,明确进程和程序的区别。
  • 掌握Linux系统中的进程创建,管理和删除等操作。
  • 熟悉使用Linux下的命令和工具,如man, find, grep, whereis, ps, pgrep, kill, ptree, top, vim, gcc,gdb, 管道|等。

二、基础知识:

  • 进程的创建
    Linux中,载入内存并执行程序映像的操作与创建一个新进程的操作是分离的。将程序映像载入内存,并开始运行它,这个过程称为运行一个新的程序,相应的系统调用称为exec系统调用。而创建一个新的进程的系统调用是fork系统调用。
  • exec系统调用
#include <unistd.h>
int execl (const char *path, const char *arg,...);

execl()将path所指路径的映像载入内存,arg是它的第一个参数。参数可变长。参数列表必须以NULL结尾。
通常execl()不会返回。成功的调用会以跳到新的程序入口点作为结束。发生错误时,execl()返回-1,并设置errno值。
例如,编辑/home/kidd/hooks.txt:

int ret;
ret = execl (”/bin/vi”, ”vi”,”/home/kidd/hooks.txt”, NULL);
if (ret == -1)
	perror (”execl”);
  • fork系统调用
#include <sys/types.h>
#include <unistd.h>
pid_t fork (void);

成功调用fork()会创建一个新的进程,它与调用fork()的进程大致相同。发生错误时,fork()返回-1,并设置errno值。

例如:

pid_t pid;
pid = fork ();
if (pid > 0)
	printf (”I am the parent of pid=%d!\n”, pid);
else if (!pid)
	printf (”I am the baby!\n”);
else if (pid == -1)
	perror (”fork”);
  • 终止进程
    exit()系统调用:
#include <stdlib.h>
void exit (int status);
  • 进程挂起
    pause() 系统调用:
int pause( void );

函数pause会把进程挂起,直到接收到信号。在信号接收后,进程会从pause函数中退出,继续运行。

  • wait(等待子进程中断或结束)
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait (int * status);

wait()会暂时停止目前进程的执行,直到有信号来到或子进程结束。
如果在调用 wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。
子进程的结束状态值会由参数status返回,而子进程的进程识别码也会一起返回。
如果不在意结束状态值,则参数status可以设成 NULL。

  • VIM常用命令速查
    16281005操作系统实验二

三、实验题目:

根据课堂所学内容和基础知识介绍,完成实验题目。

  1. 打开一个vi进程。通过ps命令以及选择合适的参数,只显示名字为vi的进程。寻找vi进程的父进程,直到init进程为止。记录过程中所有进程的ID和父进程ID。将得到的进程树和由pstree命令的得到的进程树进行比较。
  2. 编写程序,首先使用fork系统调用,创建子进程。在父进程中继续执行空循环操作;在子进程中调用exec打开vi编辑器。然后在另外一个终端中,通过ps –Al命令、ps aux或者top等命令,查看vi进程及其父进程的运行状态,理解每个参数所表达的意义。选择合适的命令参数,对所有进程按照cpu占用率排序。
  3. 使用fork系统调用,创建如下进程树,并使每个进程输出自己的ID和父进程的ID。观察进程的执行顺序和运行状态的变化。
    16281005操作系统实验二
  4. 修改上述进程树中的进程,使得所有进程都循环输出自己的ID和父进程的ID。然后终止p2进程(分别采用kill -9 、自己正常退出exit()、段错误退出),观察p1、p3、p4、p5进程的运行状态和其他相关参数有何改变。

四、实验解答:

  1. 打开一个vi进程。通过ps命令以及选择合适的参数,只显示名字为vi的进程。寻找vi进程的父进程,直到init进程为止。记录过程中所有进程的ID和父进程ID。将得到的进程树和由pstree命令的得到的进程树进行比较。

    在终端输入vi,打开vi进程,效果如下图。16281005操作系统实验二
    通过查阅资料我们可以得知ps -A命令可以查阅当前所有的进程信息,效果如下。在终端输出信息如下。16281005操作系统实验二
    所以我们可以选用了ps -A | pgrep vi的方式命令,筛选名字为vi的进程,效果如下图。pgrep vi命令是进行参数匹配查找筛选出名字为vi的进程。而中间的“|”是管道命令,是指pps命令与grep同时执行。
    16281005操作系统实验二
    在上述图片中我们可以看出此种搜索方式只能获取进程号,不能获取进程信息。所以为获取更加详尽的进程信息采取了如下方式:使用了# pgrep vi | xargs ps -u --pid命令实现对于名字为vi的进程查找。可以从下图中看出两种查找方式搜索出来的进程号是相同的,只不过第二种搜索方式具有更多的进程详细信息。
    16281005操作系统实验二
    我们由上面查找进程可知,vi进程的进程号为1866,为了寻找vi进程的父进程一直寻找到init进程位置,我们使用ps -l [进程号]的方式逐步寻找当前进程号的父进程,执行过程如下图。
    16281005操作系统实验二
    接下来使用pstree -p命令获取当前的进程树,如图所示16281005操作系统实验二
    16281005操作系统实验二
    16281005操作系统实验二
    通过进程树和上述的单步查找父进程,我们可以得知,当前vi进程的进程序列为
    Systemd(1)->lightdm(864)->lightdm(1133)->upstart(1143)->gan(1848)->bash(1853)->vi(1866)

  2. 编写程序,首先使用fork系统调用,创建子进程。在父进程中继续执行空循环操作;在子进程中调用exec打开vi编辑器。然后在另外一个终端中,通过ps –Al命令、ps aux或者top等命令,查看vi进程及其父进程的运行状态,理解每个参数所表达的意义。选择合适的命令参数,对所有进程按照cpu占用率排序。

    程序执行效果如下:(程序见githup源代码2.cpp)
    16281005操作系统实验二
    16281005操作系统实验二

  • 打开另一中断测试命令ps -Al,得出如下结果
    16281005操作系统实验二
参数 含义
F 标志
S 程序状态
UID 执行者身份
PID 进程号
PPID 父进程号
C CPU使用百分比
PRI 进程执行优先权
NI 进程的nice值(负值表示高优先级)
ADDR 内核函数
SZ 占用内存大小
WCHAN 进程正在睡眠的内核函数
TTY 终端机位置
TIME 使用掉的CPU时间
CMD 下达的指令名称
  • 执行ps aux命令效果如下:

16281005操作系统实验二

参数 含义
USER 用户
PID 进程号
%CPU 占用CPU百分比
%MEM 记忆体使用率
VSZ 占用虚拟记忆体大小
RSS 占用记忆体大小
TTY 中断的次要装置号
STAT 进程的状态
START 进程开始时间
TIME 进程执行时间
COMMAND 所执行的命令
  • top命令
    16281005操作系统实验二
    部分参数含义如下
参数 含义
PR 优先级
VIRT 进程使用的虚拟内存总量
RES 进程使用的、未被换出的物理内存
S 进程状态
TIME+ 进程使用的CPU时间总计
  1. 使用fork系统调用,创建如下进程树,并使每个进程输出自己的ID和父进程的ID。观察进程的执行顺序和运行状态的变化。
    16281005操作系统实验二
    执行程序结果如下图:(程序见githup源代码3.cpp)
    16281005操作系统实验二
    系统函数fork实现的功能为启动一个新的与原有进程相同的新进程,称为子进程。子进程复制父进程的堆栈段和数据段。在fork函数使用后含有两个返回值,我们根据返回值的不同,可以区分子进程和父进程,其中子进程=0,父进程>0。

  2. 修改上述进程树中的进程,使得所有进程都循环输出自己的ID和父进程的ID。然后终止p2进程(分别采用kill -9 、自己正常退出exit()、段错误退出),观察p1、p3、p4、p5进程的运行状态和其他相关参数有何改变。
    程序执行效果如下:(程序见githup源代码4-1.cpp)
    16281005操作系统实验二

  • Kill -9 [进程号],结束相关进程号的对应进程。在之后的循环过程中该进程被终止,效果如图。
    16281005操作系统实验二16281005操作系统实验二
  • 设置exit()函数,自动停止,。(程序见githup源代码4-2.cpp)
    16281005操作系统实验二
  • 设置段错误:所谓段错误,一般是访问了未申请的内存或非法的内存时产生的,概括点说在代码中一般是由指针的不当使用引起的。(程序见githup源代码4-3.cpp)16281005操作系统实验二
    githup源码链接.