操作系统第2次实验报告:创建进程

  • 袁祎琦
  • 201821121033
  • 计算1812

1. 编写程序

在服务器上用VIM编辑器编写一个程序:一个进程创建(fork)两个子进程。给出源代码:

以下是fork.c文件的代码:

 1 #include<sys/types.h> //提供pid_t定义
 2 #include<unistd.h>
 3 #include<stdio.h>
 4 int main(){
 5      pid_t fpid1,fpid2;//fpid表示fork函数返回的值
 7      fpid1= fork();
 8      if(fpid1<0){
 9          printf("error in fork!\n");
10          return 0;
11      }
12      else if(fpid1==0){//子进程1
13          printf("Child1 process,my process id is %d\n",getpid());
14      }
15      else{
16          fpid2=fork();
17          if(fpid2<0){
18              printf("error in fork!\n");
19              return 0;
20          }
21          else if(fpid2==0){//子进程2
22              printf("Child2 process,my process id is %d\n",getpid());
23          }
24          else{//父进程
25              printf("Parent process,my process id is %d\n",getpid());
26          }
27          sleep(100);//使用sleep函数,便于打印进程树
28      }
29 }

2. 打印进程树

2.1、对与上述代码,使用gcc进行编译,操作命令: gcc -o fork fork.c 

  如果编译成功,则终端不会报错。

 

2.2、编译成功后,使用 ls 命令,会发现在目录下多了一个fork名称的可执行程序Hello:

使用 ./fork &运行可执行文件,&符号的意思是后台运行,这时候会给出一个pid。但是如果已经使用了sleep函数进行延时,也可以另外开一个终端,这时候跑出来的pid有三个(因为一个父进程,两个子进程)你需要找出父进程的pid,使用./fork命令。

 

2.3、 使用 pstree -p 995 命令,其中995是父进程的PID。

3. 解读进程相关信息

3.1 ps -ef  使用ps -ef给出所创建进程的信息,并分析每个字段的含义。

将上面的输出整理为:

ps -ef|grep "fork"
UID        PID    PPID  C  STIME   TTY      TIME        CMD
yuanyiqi   995    751   0  20:37   pts/1    00:00:00  ./fork
yuanyiqi   996    995   0  20:37   pts/1    00:00:00   [fork] <defunct>
yuanyiqi   997    995   0  20:37   pts/1    00:00:00  ./fork
yuanyiqi  1214    1125  0  20:38   pts/5    00:00:00  grep --color=auto fork
  • UID:进程所属用户;
  • PID:进程号;
  • PPID:父进程号;
  • C:占用的CPU使用率;
  • STIME:进程运行的时间;
  • TTY:终端的次要装置号码;
  • TIME:执行的时间;
  • CMD:进程所执行的指令。

各字段的值释义,以第一行为例:

(1)yuanyiqi是用户ID;

(2)995是996(子进程),997(子进程)父进程ID;

(3)751是995的父进程ID;

(4)pts/1,虚拟终端号,打开一个终端,这个终端就是pts/0,再打开一个,就是pts/1;

(5)TIME:进程0时0分0秒就执行完了;

(6)CMD:第一行的指令为./fork。

3.2 ps -aux 使用ps -aux给出所创建进程的信息,并分析每个字段的含义。

由于没来得及,还没输入该语句,程序已经执行完了,所以另外又执行了一次 ./fork 命令。

此时输入 ps -aux|grep "fork" 命令,得出:

将上面的输出整理得出以下:

USER      PID   %CPU %MEM  VSZ   RSS  TTY     STAT  START   TIME COMMAND
yuanyiqi  3861  0.0  0.0   4508   720 pts/1    S+   20:56   0:00 ./fork
yuanyiqi  3862  0.0  0.0      0     0 pts/1    Z+   20:56   0:00 [fork] <defunct>
yuanyiqi  3863  0.0  0.0   4508    72 pts/1    S+   20:56   0:00 ./fork
yuanyiqi  3869  0.0  0.0  13772  1116 pts/5    S+   20:56   0:00 grep --color=auto fork
  • USER:进程所属用户的用户名;
  • PID:进程号;
  • %CPU:占用的CPU使用率;
  • %MEM:占用的内存使用率;
  • VSZ:占用的虚拟机内存大小;
  • RSS:占用的内存大小;
  • TTY:终端的次要装置号码;
  • START:进程开始时间;
  • COMMAND:进程执行的指令。

各字段的值释义,以第一行为例:

(1)yuanyiqi为用户ID;

(2)3861为3862(子进程)、3863(子进程)的父进程;

(3)CPU使用率为0;

(4)内存使用率为0;

(5)占用虚拟机内存大小为4508K;

(6)占用内存大小为720K;

(7)pts/1为虚拟终端号;

(8)3861处于睡眠状态,“+”位于后台的进程组。(1、R:正在运行,可执行状态;

                       2、S:处于可中断的睡眠状态;

                       3、D:无法中断的休眠状态;

                       4、T:暂停状态或跟踪状态;

                       5、Z:退出状态,进程成为僵尸进程;

                       6、X:退出状态,进程即将被销毁;

                              <高优先级

                        n低优先级

                        s(小写)包含子进程

                        +位于后台的进程组)

(9)START:进程开始时间为20:56分;

(10)进程的持续时间:0分0秒;

(11)3861所执行的指令为 ./fork 

4. 通过该实验产生新的疑问及解答

问题一:根据父子进程这个名称,是不是会根据名字,认为父进程一定比子进程先执行?

答:子进程和父进程执行的是同一个程序,绝大部分就是原进程(也就是该进程的父进程)的拷贝,使用fork()以后,子进程就相当于父进程的兄弟一样了,这个子进程和父进程不同的地方只有它的ID和其父进    程的ID。

所以fork()之后,除非采用了同步手段,否则不能确定谁先运行,也不确定谁先结束它们是处于竞争关系,在父进程执行过程中,子进程可以抢占资源。

问题二:sleep()函数在C语言中的意义?

答:这个函数具有不同含义,功能:执行挂起一段时间,用法:usigned sleep(unsigned seconds),注意,如果使用的头文件是#include<windows.h>,则Sleep(1000)表示休息1秒,当然,此次实验是在linux平台下进行的,使用的头文件是#include<unistd.h>,sleep(1)表示休息1秒。

问题三:既然可以创建进程,那怎样实现撤销进程呢?

答:进程终止的一般方式是调用exit()库函数。进程终止:

1、有exit_group()系统调用,它终止整个线程组;

2、exit()系统调用,它终止某一个线程。那么进程删除呢?wait()库函数,会检查子进程是否终止,如果子进程已经终止,那么它的终止代号将告诉父进程这个任务是否已成功地完成。

问题四:根据多方了解,fork()和vfork()都是创建一个进程函数,那么它们有什么区别呢?

答:(1)、fork():子进程拷贝父进程的数据段,代码段;vfork()子进程与父进程共享数据段。

  (2)、fork()父子进程的执行次序不确定(如我的问题一所说);vfork()保证子进程先运行。

  (3)、其他我还不了解 。

5. 加分项

结合实例(实例可以是写一个输出Hello World的简单程序)分析Linux可执行文件构成。

PS:在VIM下查看可执行文件

  • :%!xxd 将当前文本转换为16进制格式
  • :%!xxd -r 将当前文件转换回文本格式

5.1、在linux下,程序是一个普通的可执行文件,以下列出了一个二进制可执行文件的基本情况:

由此可以看出,此可执行文件在存储时,分为代码区数据区未初始化数据区3个部分。

代码区(text):存放CPU执行的机器指令。代码区的指令包括操作码和操作对象(或对象地址引用)。

静态数据/全局初始化数据区(数据段data):包含了在程序中已经初始化的静态变量、明确被初始化的全局变量和常数变量。

未初始化数据区(BSS区):存入的是全局未初始化变量和未初始化静态变量。未初始化数据区的数据在程序开始执行之前被内核初始化为0或者空(NULL)。

  进程是linux事务管理的基本单元,所有的进程均拥有自己独立的处理环境和系统资源。一个进程是一个运行着的代码段,一个进程主要包括在内存中申请的空间,代码(包括代码段,数据段,BSS),堆,栈,以及内核提供的内核进程信息结构体。

5.2、编写Hello程序,使用 vim Hello 查看可执行文件,使用 :%!xxd 将Hello转换为十六进制。 :%!xxd -r的作用是将图三转换回图二。

——Hello.c文件及其十六进制表示

   

——Hello(可执行文件及其十六进制表示)

——124、125行为输出的Hello World!

使用 objdump -h Hello 命令打印各个段及的基本信息

其中包含了刚才说的代码段、数据段、BSS段、只读段、注释信息段和堆栈提示段(照理说应该有,但是在图上没找到)。

上面通过一个实例了解了可执行文件的基本轮廓,大致得出可执行文件的总体结构:

Reference:

https://blog.csdn.net/tennysonsky/article/details/45113863

https://blog.csdn.net/QX_a11/article/details/89925782

posted @ 2020-04-04 23:25  DAY--BY--DAY  阅读(1000)  评论(0编辑  收藏  举报