OS实验二:进程控制

OS实验二:进程控制 16281021

  • OS实验二:进程控制 16281021
    • 一、实验目的
    • 二、基础知识
    • 三、实验题目

OS实验二:进程控制 16281021

GitHub地址:https://github.com/RebekahYuuu/OS_Experiment_02

一、实验目的

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

二、基础知识

  • 进程的创建
    Linux中,载入内存并执行程序映像的操作与创建一个新进程的操作是分离的。将程序映像载入内存,并开始运行它,这个过程称为运行一个新的程序,相应的系统调用称为exec系统调用。而创建一个新的进程的系统调用是fork系统调用。
  • exec系统调用
    #include
    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
    #include
    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
    void exit (int status);

  • 进程挂起
    pause() 系统调用:
    int pause( void );
    函数pause会把进程挂起,直到接收到信号。在信号接收后,进程会从pause函数中退出,继续运行。

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

  • VIM常用命令速查

OS实验二:进程控制_第1张图片

三、实验题目

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

  1. 打开一个vi进程。通过ps命令以及选择合适的参数,只显示名字为vi的进程。寻找vi进程的父进程,直到init进程为止。记录过程中所有进程的ID和父进程ID。将得到的进程树和由pstree命令的得到的进程树进行比较。
    1.1只显示名字为vi的进程
    通过ps -ef可显示当前全部进程
    OS实验二:进程控制_第2张图片OS实验二:进程控制_第3张图片OS实验二:进程控制_第4张图片OS实验二:进程控制_第5张图片可以看出,刚刚打开的vi进程的pid=2203,对应的cmd为vi experiment2.txt
    尝试用ps aux|grep vi筛选出所有cmd里含vi的进程,结果如图
    OS实验二:进程控制_第6张图片可以看出,cmd里包含字符串vi的进程都被显示,无法单独筛选出刚刚打开的vi进程。由此可见,pid才是唯一确定进程的方法,
    因此,使用ps --pid 2203命令,达到预期,结果如下:
    在这里插入图片描述1.2寻找vi进程的父进程,直到init进程为止
    因为ppid是该进程的父进程的pid,所以,通过ppid依次向上寻找父进程,最终结果为:
    2203->2077->2068->1049->1
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述1.3将得到的进程树和由pstree命令的得到的进程树进行比较
    通过pstree -p指令显示当前所有进程的进程号和进程id
    在这里插入图片描述在这里插入图片描述OS实验二:进程控制_第7张图片由进程树可得,2203->2077->2068->1049->1
    与通过查ppid得到的结果一致

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

#include 
#include 
#include 
#include 
int main()
{
    int flag;
    flag=fork();
    if(flag<0)//error in fork
    {
        printf("error in fork\n");
    }
    else if(flag>0)//parent
    {
        printf("This is parent process, its pid is %d\n",getpid());
        while(1);//vacant cycle
    }
    else//flag==0 child
    {
        printf("This id child process, its pid is %d\n",getpid());
        //open vi editer with "exec"
        int ret;
        ret=execl("/usr/bin/vi","vi","test.c",NULL);
        if(ret==-1)//error in execl
            perror("execl");
    }
    return 0;
}

编译运行,试验结果如下:
父进程与子进程都输出了pid,再回车,就进入了test.c——子进程通过execl函数打开的vi编辑器
在这里插入图片描述OS实验二:进程控制_第8张图片
结果思考:
由于在程序中,我把对父进程的处理放到了子进程的处理之前,由于父进程里我设置里while(1)空循环,我本以为程序运行到这里就应该陷入死循环无法跳进子进程输出子进程pid并创建vi编辑器。
但是结果与预想相反,在终端打印了子进程的pid,我本来以为是程序出了问题,但是,调用ps命令和top命令之后发现,父进程和子进程在同时运行,程序并没有出问题,可能这就是所谓的“并发”吧。

2.2 打开另一个终端,查看vi进程和父进程的运行状态

2.2.1 ps aux命令
ps aux命令的作用是查看当前正在运行的进程
OS实验二:进程控制_第9张图片在这里插入图片描述由截图可知,运行./a.out命令之后,当前正在运行的进程既有父进程a.out,也有子进程vi test.c

参数意义:
USER:该进程属于的使用者的账号,用来区分用户进程和系统进程
PID:进程的唯一标识符
%CPU:该进程使用掉的CPU资源百分比
%MEM:该进程所占的物理内存百分比
VSZ:该进程所使用的虚拟内存、
RSS:该进程所占用等等固定内存
STAT:该进程目前的状态。S表示该进程正在睡眠状态,但可能被某些signal唤醒;R表示正在运行
START:该进程被触发启动的时间
TIME:该进程实际使用CPU运作的时间
COMMAND:该进程的实际指令

2.2.2 top命令
top命令的作用是实时查看系统运行状况,即查看当前正在运行的进程对cpu的占用率
可以看出,a.out对cpu的占用率占用率是100%,符合空循环的特性。
OS实验二:进程控制_第10张图片
3. 使用fork系统调用,创建如下进程树,并使每个进程输出自己的ID和父进程的ID。观察进程的执行顺序和运行状态的变化。
OS实验二:进程控制_第11张图片
3.1编写程序
代码如下:

#include 
#include 
#include 
#include 

int main()
{
    pid_t p1,p2,p3,p4,p5;
    
    //if error occurred while creating root process, keep create it.
    while((p1=fork())==-1);

    if(p1==0)//root process
    {
        while((p2=fork())==-1);

        if(p2==0)//left child
        {
            while((p4=fork())==-1);

            if(p4==0)//left child
            {
                while(1)
                {
                    sleep(1);
                    printf("p4's pid is %d and its parent's pid is %d\n",getpid(),getppid());
                }

            }
            else
            {
                while((p5=fork())==-1);

                if(p5==0)//right child
                {
                    while(1)
                    {
                        sleep(1);
                        printf("p5's pid is %d and its parent's pid is %d\n",getpid(),getppid());
                    }
                }
            }
            while(1)
            {
                sleep(1);
                printf("p2's pid is %d and its parent's pid is %d\n",getpid(),getppid());
            }
        }
        else
        {
            while((p3=fork())==-1);

            if(p3==0)
            {
                while(1)
                {
                    sleep(1);
                    printf("p3's pid is %d and its parent's pid is %d\n",getpid(),getppid());
                }
            }

        }
        while(1)
        {
            sleep(1);
            printf("p1's pid is %d and its parent's pid is %d\n",getpid(),getppid());
        }
    }
        return 0;
}

运行结果如图:
OS实验二:进程控制_第12张图片 以第二次运行为例,其建立的进程树如图,符合题意
OS实验二:进程控制_第13张图片
3.2 结果分析
运行ps -ef命令,查看进程
OS实验二:进程控制_第14张图片可以看出系统执行这几个进程的顺序并不确定,取决于系统的调度策略
4. 修改上述进程树中的进程,使得所有进程都循环输出自己的ID和父进程的ID。然后终止p2进程(分别采用kill -9 、自己正常退出exit()、段错误退出),观察p1、p3、p4、p5进程的运行状态和其他相关参数有何改变。

4.1 执行kill -s 9 7972
重新执行a.out
OS实验二:进程控制_第15张图片可以看出,p2的pid是7972
执行kill -s 9 7972
在这里插入图片描述可以看出,p4 p5和p1变成了同一个父进程的子进程
4.2执行exit(0)
修改代码如下:
OS实验二:进程控制_第16张图片编译运行:
OS实验二:进程控制_第17张图片可以看出第一遍执行p2之后执行了exit(0)函数,下一次执行时,p4 p5的父进程又变成了p1的父进程

4.3 段错误退出进程
修改代码如下:
首先,添加全局变量指针a(char *a)
然后在p2执行完一次之后制造段错误——引用未初始化的指针
OS实验二:进程控制_第18张图片编译执行结果如下:
可以看出,其结果与前两次实验相同
OS实验二:进程控制_第19张图片结果分析:
(1)以上三种方式都可以结束进程,但是通过ps -ef指令查看当前正在运行的进程可知,该进程的生命并没有结束,是僵尸进程
(2)终止一个进程并不会级联终止其子进程,系统只会修改其子进程的父进程的pid

你可能感兴趣的:(操作系统实验)