- 袁禕琦
- 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:
