当前位置:   article > 正文

操作系统实验——熟悉linux系统、fork操作

操作系统实验——熟悉linux系统、fork操作

实验一:熟悉Linux命令及进程管理

  • 实验目的
  1. 熟悉linux系统的命令
  2. 学会利用vi文件编辑系统编辑文件
  3. 掌握folk、wait等系统函数的使用与分析
  • 实验内容和步骤
  1. 安装并熟悉linux操作系统

目前流行的Linux系统很多,我选择利用virtual box中安装虚拟ubuntu系统完成本次实验。如下图:在virtual box中新建ubuntu环境后,设置好ubuntu虚拟镜像环境就可以使用了。

 为了使操作不受限,在ubuntu中设置超级用户:

 2、linux操作系统基本命令

  1. 展示当前文件夹内容以及切换目录

 

  1. who命令:该命令主要用于查看当前在线上的用户情况。
  2. ps命令:使用该命令可以确定有哪些进程正在运行和运行的状态、进程是否结束、进程有没有僵死、哪些进程占用了过多的资源等等。总之大部分信息都是可以通过执行该命令得到的。

 

  1. Man+命令获取帮助

 

Vi的基本操作:

 

3、在linux系统中编写并运行C程序

在ubuntu中主要有两种方式来运行C语言程序:

  1. 一种是首先将代码保存在文本编辑器中,然后利用gcc将代码编译成可执行文件,最后在终端执行该可执行文件,输出的结果在终端显示。
  2. 一种是利用全屏幕文本编辑器vi,首先进入vi的命令模式,输入vi +文本文件名     //用vim打开该文件,没有则自动创建,进入之后输入i进入编辑模式,编写完成后,先按esc回到命令行模式,然后再返回。

值得注意的是,将windows操作系统中的代码直接共享到ubuntu系统中,也有两种方式:

  1. 一种是设置virtual box 允许主机和虚拟机之间可以相互复制粘贴(我选的方法)
  2. 一种是为window和ubuntu设置共享文件夹
  • 代码及运行结果分析

代码段1的运行结果分析:我采用方法一(利用文本编辑器保存c代码)

程序1

  1. #include "stdio.h"
  2. main()
  3. {int i,j,k;
  4. if (i=fork())
  5. {j=wait();
  6. printf("Parent Process!\n");
  7. printf("i=%d,j=%d,k=%d\n",i,j);
  8. }
  9. else
  10. {k=getpid();
  11. printf("Child Process!\n");
  12. printf("i=%d,k=%d\n\n",i,k);
  13. }}

代码分析:

  1. 代码第4:在语句ifi=fork())之前只有一个进程在执行这段代码,但在这条语句之后,就变成两个进程在执行了,这两个进程的几乎完全相同,将要执行的下一条语句都是判断if条件语句是否为真。

    1在父进程中,fork返回新创建子进程的进程ID这时i是大于0的,因此会进入if语句中执行wait操作并等待子进程执行完毕

    2在子进程中,fork返回0,因此会执行else中的操作

    3)如果出现错误,fork返回一个负值;

  1. 代码第5

wait函数功能是:父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

当父进程忘了用wait()函数等待已终止的子进程时,子进程就会进入一种无父进程的状态,此时子进程就是僵尸进程.

wait()要与fork()配套出现,如果在使用fork()之前调用wait(),wait()的返回值则为-1,正常情况下wait()的返回值为子进程的PID.

执行步骤与结果如下图所示:

 

根据上图所示的分析结果,容易发现:

  1. 由于父进程被系统函数wait阻塞了,因此由子进程先输出,i=0说明其fork返回值是0k=1950说明其pid1950
  2. 当子进程执行完毕之后,wait检测到并将其销毁后返回。这里看到wait()函数的返回值是-1j的值),并不是子进程的pid,这是因为wait函数的传入参数不是NULL wait会把子进程退出时的状态取出并存入其中, 这是一个整数值(int),指出了子进程是正常退出还是被非正常结束的,以及正常结束时的返回值,或被哪一个信号结束的等信息。人们设计了一套专门的宏(macro)来完成这项工作, WIFEXITED(status) 这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。因此,我们可以总结出该子进程是正常退出的

为了验证上述的猜想,wait函数改写成wait(NULL),得到以下输出结果:

 

分析以上结果,发现父进程的forkwait函数的返回值都是子进程的pid=4074,父进程的pid4073,说明此时wait函数是正常返回的。

程序1运行的进程数如下图所示:

 

程序2的代码及运行结果分析:

  1. #include<stdioh>
  2. main()
  3. {
  4. int p1p2;
  5. while((p1fork())= =-1);   
  6. if(p1= =0)      

printf(“b.My process ID is %d”,getpid());

  1. else
  2. {
  3. while((p2fork())= =-1);   
  4. if(p2==0)                  

printf(“c.My process ID is %d”,getpid());

  1. else printf(“a.My process ID is %d”,getpid());         
  2. }
  3. }

执行结果:

 

从代码的第5行开始分析,首先执行forkfork执行完后,系统中出现两个进程,分别是p1p0

根据if条件语句,可以判断出p1为子进程,执行if语句,输出语句bp0为父进程继续执行else语句;进入else循环后,原父进程p0继续执行fork语句,此时根据if条件判断,系统又产生了新的子进程p2,输出语句c, 最后原父进程p0输出语句a

多次执行程序2,发现输出结果并不相同

如上图所示,有时先创建的进程的执行速度反而慢于后创建的进程的执行速度。这是因为当fork语句执行成功后创建新进程后,这些进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。

 

 

程序3的代码和结果分析;

  1. main()
  2. {int m,n,k;
  3. m=fork();
  4. printf("PID:%d\t",getpid());
  5. printf("The return value of fork():%d\t\t",m);
  6. printf("he\n");
  7. n=fork();
  8. printf("PID:%d\t",getpid());
  9. printf("The return value of fork():%d\t\t",n);
  10. printf("ha\n");
  11. k=fork();
  12. printf("PID:%d\t",getpid());
  13. printf("The return value of fork():%d\t\t",k);
  14. printf("ho\n");}

 

根据运行结果,系统共执行了三次fork,共生成了8个进程

  1. 第一次执行fork时,系统的父进程为p4607,子进程为p4608
  2. 第二次执行fork时,进程p4607p4608分别生成了子进程p4609p4611
  3. 第三次执行fork时,前面生成的四个进程,分别生成了新的子进程p4610p4612,p4613,p4614

 

程序4的运行结果:

  1. #include<stdio.h>
  2. main()
  3. {
  4. int p1,p2,i;
  5. while((plfork())= =-1);
  6. if(pl= =0)
  7. for(i=0;i<50000;i++)
  8. printf("sond\n”i);
  9. else
  10. {
  11. while((p2=fork())= =-1);
  12. if(p2= =0)
  13. for(i=0;i<50000;i++)
  14. printf("daughter%d\n”i);
  15. else
  16. for(i0i<50000;i++)
  17. printf("parentdn)”,i);}}

多次运行程序4的结果并不相同,根据上图所示,可以发现最后执行完的进程可能是父进程也可能是子进程,有时先创建的进程的执行速度反而慢于后创建的进程的执行速度。这是因为当fork语句执行成功后创建新进程后,这些程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略

编写程序实现进程树

  1. #include"stdio.h"
  2. #include"unistd.h"
  3. main()
  4. {
  5. int p1,p2;
  6. while((p1=fork())==-1);   
  7. int fpid=fork();
  8. if(fpid==0)
  9. printf("pid=%4d ,child%4d\n",getpid(),fpid);
  10. else
  11. printf("pid= %4d , child%4d\n",getpid(),fpid);
  12. if(p1>0 && fpid>0)
  13. {
  14. while((p2=fork())==-1);   
  15. if(p2==0)                     
  16. printf("pid=%4d , child%4d\n",getpid(),p2);
  17. else printf("pid=%4d , child%4d\n",getpid(),p2);    
  18. }
  19. }

程序的运行结果如下:

 

结果分析,在最初的程序设计时,创建了6个进程,后来我发现,这是因为父进程的p1共享给了它第二次创建的子进程,所以第二次创建的子进程也会进入if(p1>0)的条件语句中,修改条件语句,则成功实现程序所要求的进程树。

 

 

程序的进程树如上图所示。

  • 心得体会

在本次实验中,我安装并学习了linux系统的基础操作,并通过分析linux进程创建的核心代码,fork和wait函数,理解了Linux系统创建子进程的过程,fork函数是一个神奇的函数,通过该系统函数的调用,它调用一次可以产生两个返回值,并为当前进程产生一个新的子进程,这里有一点特别值得注意,在fork语句之前,该进程的所有资源,包括代码、变量都会等价的复制给子进程,如果忽略了这一点,很容易在编写程序自己创建进程树的时候出错。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/210441?site
推荐阅读
相关标签
  

闽ICP备14008679号