操作系统实验二报告_16281028

实验 进程控制

实验目的:

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

基础知识:

  1. 进程的创建

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。

实验题目:

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

实验步骤:

(1)打开一个终端,输入vi,开启一个进程

(2)再开启一个终端,输入ps

(3)输入ps -A,查看所有进程的ID

可知,vi进程的ID是9614。

(4)输入ps -p9614,查看CPU的运行时间

(5)输入ps -lax,查看进程号为9614的进程所有信息

UID是用户号, 为9614;

PID就是vi进程的进程号, 为9614,

PPID就是vi进程的父进程号,为9603;

PRI是内核调度优先级, 为20;

NI是进程优先级, 为缺省值0;

VSZ是总虚拟内存大小, 以byte计;

RSS是进程使用的总物理内存数,以 Kbytes计;

STAT是进程状态, 属于静止状态;

TTY是终端的次要装置号码;

TIME为使用cpu的时间

(6)利用pgrep命令搜索父进程,直到找到进程号为1为止(pgrep 是通过程序的名字来查询进程的工具,一般是用来判断程序是否正在运行;xargs能够捕获一个命令的输出,然后传递给另外一个命令。通过两个命令的配合,完成了显示名字为vi的进程的输出,并通过ps命令一步一步找到父进程。)

可以看到搜索父进程顺序为:

9614——>9603——>7295——>949——>938——>849——>1

(7)输入命令 pstree -p 查看进程树:

结论就是:两种方式所得结果一样。

 

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

实验步骤:

代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
  int pid=fork();
  if(pid>0){
    while(1);
  }
  else if(pid==0){
  int ret;
  ret=execlp("vi","",NULL);
    if(ret==-1){
      printf("execl error\n");
      exit(-1);
    }
  }
  else{
    printf("fork errorn");
    exit(-1);
  }
  return 0;
}

(1)查阅资料可知:

#F代表程序的旗标(flag),4代表使用者为 superuser,1代表user;

#S代表这个程序的状态 (STAT);

#UID代表执行者身份;

#PID代表进程的ID号

#PPID代表父进程的ID;

#C代表使用的CPU资源百分比;

#PRI代表进程的执行优先权(Priority的简写),其值越小越早被执行;

#NI代表进程的nice值,其表示进程可被执行的优先级的修正数值;

#ADDR代表内核函数;

#SZ表示占用内存的大小;

#WCHAN表示这个程序是否正在运作当中,其中’ -’表示正在运作;

#TTY表示登入者的终端机位置;

#TIME表示使用掉的 CPU 时间;

#CMD 表示下达指令的名称;

(2)使用命令ps -AL查看进程信息

然后找到vi进程及其状态为:

(3)使用命令ps aux查看内存中运行的程序

查阅资料可知:

• USER:该进程属于的使用者账号;

• PID:该进程的进程ID号;

• CPU:该进程使用掉的CPU资源百分比;

• MEM:该进程所占用的物理内存百分比;

• VSZ:该进程使用掉的虚拟内存量 (Kbytes)

• RSS :该进程占用的固定的内存量 (Kbytes)

• TTY :该进程是在那个终端机上面运作,若与终端机无关,则显示 ?,另外, tty1-tty6 是本机上面的登入者程序,若为 pts/0 等等的,则表示为由网络连接进主机的程序。

• STAT:该程序目前的状态,主要的状态有:

R :该程序目前正在运作,或者是可被运作;

S :该程序目前正在睡眠当中;

T :该程序目前正在侦测或者是停止了;

Z :该程序应该已经终止,但是其父程序却无法正常的终止他,造成 zombie(疆尸) 程序的状态;

• START:该进程被触发启动的时间;

• TIME :该进程实际使用 CPU 运作的时间。

• COMMAND:该进程所属的指令

(4)使用top命令将所有进程按照cpu占有率排名,可见死循环程序fork_exec已经几乎完

全占有了CPU:

 

3、使用fork系统调用,创建如下进程树,并使每个进程输出自己的ID和父进程的ID。观察进程的执行顺序和运行状态的变化。

实验步骤:

代码:

#include <stdio.h>
#include <unistd.h>
int main()
{
    pid_t pid;
    printf("p1: pid %d ppid %d\n",getpid(),getppid());
    pid = fork();
    if(pid>0){
    pid = fork();
    if(pid==0){
        printf("p3: pid %d ppid %d\n" ,getpid(),getppid());
    }
    if(pid>0){
        pid = fork();
        if(pid == 0){
            printf("p2:. pid %d ppid %d\n" ,getpid() ,getppid());
            pid = fork();
        if(pid==0){
            printf("p4: pid d ppid %d\n" ,getpid() ,getppid());
        }
        if(pid>0){
            pid=fork();
        if(pid==0){
            printf("p5: pid %d ppid %d\n",getpid() ,getppid());
        }
        }
        }
    }
    }
    return 0;
}

可以看出P1为P2和P3的父进程,P2位P4和P5的父进程。

 

4、修改上述进程树中的进程,使得所有进程都循环输出自己的ID和父进程的ID。然后终止p2进程(分别采用kill -9 、自己正常退出exit()、段错误退出),观察p1、p3、p4、p5进程的运行状态和其他相关参数有何改变。

实验步骤:

输入命令ps -Al| egrep -e a.out得到进程信息,再输入pstree查看进程树

修改程序之后运行为:

P2只循环输出2次。观察到到后面输出开始除了P2没有继续输出外,其他进程仍然满足题目要求的进程树。这和我预计的结果并不一样,所以猜测可能进程P2并没有结束。

GitHub代码链接:https://github.com/Tergerly/16281028/tree/master/%E5%AE%9E%E9%AA%8C%E4%BA%8C

参考文章:http://wang22ti.com/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值