操作系統第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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM