1. 程式人生 > >Linux系統下fork函式的實驗

Linux系統下fork函式的實驗

實驗 Linux下程序管理

一、實驗目的

1.掌握vim編譯器

2.掌握gcc編譯器的使用

3.瞭解fork()程式

二、實驗工具與裝置

1.實驗裝置:計算機(帶CD-ROM)一臺。

三、實驗預備知識

1. vim的編輯器

使用語法:Vim <被編輯的檔名>

Vim有三種模式:命令模式、插入模式、最後行模式。

命令模式:

剛啟動Vim後,就處於該模式。在此模式下,允許用Vim的子命

令來編輯檔案或轉移到其它模式。如:

x命令:刪除游標上面的字元。

方向鍵:移動游標。

i命令:進入插入模式,可在當前游標處插入字元。

a命令:進入插入模式,可在當前游標後插入字元。

R命令:從當前游標處開始替換文字。

r命令:替換當前游標處字元。

~命令:對當前游標處字母進行大小寫轉換。

dd命令:刪除游標所在行。

dw命令:刪除游標所在處字。

h命令:游標左移。

l命令:游標右移。

k命令:游標上移。

j命令:游標下移。

:命令:進入最後一行模式。

插入模式:

此模式下允許輸入文字,用回車鍵換行,Esc 進入命令模式。

最後一行模式:

w命令:將檔案存檔,但不退出。

wq命令:將檔案存檔,並退出。

q! 命令:不儲存檔案並退出。

r命令:將另一個檔案內容插入當前游標處。

2.gcc編譯器

Unix 上使用的C 語言編譯器cc,在Linux上的派生就是gcc。在使用vim編寫完源程

序之後,返回到shell下面,使用gcc對源程式進行編譯的命令是:

gcc 源程式

其中,“源程式”即為你編寫的以.c 為副檔名的C 語言原始碼檔案。

如果原始碼沒有語法錯誤,使用以上命令編譯,會在當前目錄下生成一個名為a.out

的可執行檔案。如果原始碼有語法錯誤,則不會生成任何檔案,gcc編譯器會在shell中

提示你錯誤的地點和型別。

也可以使用以下方法編譯原始碼檔案,生成自命名的可執行檔案:

gcc 原始檔–o 自命名的檔名

執行當前目錄下的編譯生成的可執行檔案,使用以下格式:

./可執行檔名

當使用gcc編譯你寫的程式原始碼的時候,可能會因為原始碼存在語法錯誤,編譯

無法進行下去,這時候,就可以使用偵錯程式gdb來對程式進行除錯。

3. gdb 簡介

Linux 包含了一個叫gdb 的GNU 除錯程式。gdb 是一個用來除錯C 和C++ 程式

的強力偵錯程式。它使你能在程式執行時觀察程式的內部結構和記憶體的使用情況。以下是

gdb 所提供的一些功能:

l 能監視你程式中變數的值。

l 能設定斷點以使程式在指定的程式碼行上停止執行。

l 能一行行的執行你的程式碼。

當啟動gdb 後,能在命令列上指定很多的選項。你也可以以下面的方式來執行 gdb:

gdb <fname>

當你用這種方式執行gdb ,就能直接指定想要除錯的程式。這將告訴gdb 裝入名為

fname 的可執行檔案。你也可以用gdb 去檢查一個因程式異常終止而產生的core 文

件,或者與一個正在執行的程式相連。你可以參考gdb 指南頁或在命令列上鍵入gdb

-h 得到一個有關這些選項的說明的簡單列表。

為除錯編譯程式碼(Compiling Code for Debugging)。為了使gdb 正常工作, 你必須

使你的程式在編譯時包含除錯資訊。除錯資訊包含你程式裡的每個變數的型別和在可執

行檔案裡的地址對映以及原始碼的行號。gdb 利用這些資訊使原始碼和機器碼相關聯。

在編譯時用-g 選項開啟除錯選項。

4.fork() 函式說明

pid_t fork(void)

fork()會產生一個新的子程序。該函式包含於標頭檔案unistd.h 中。其子程序會複製

父程序的資料與堆疊空間,並繼承父程序的使用者程式碼、組程式碼、環境變數、已開啟的文

件程式碼、工作目錄和資源限制等。Linux 使用copy-on-write(COW)技術,只有當其中一

程序試圖修改欲複製的空間時才會做真正的複製動作,由於這些繼承的資訊是複製而

來,並非指相同的記憶體空間,因此子程序對這些變數的修改和父程序並不會同步。此

外,子程序不會繼承父程序的檔案鎖定和未處理的訊號。注意,Linux不保證子程序會比

父程序先執行或晚執行,因此編寫程式時要留意死鎖或競爭條件的發生

返回值,如果fork()呼叫成功則在父程序會返回新建立的子程序程式碼(PID),而在新

建立的子程序中則返回0。如果fork() 失敗則直接返回-1,失敗原因存於errno中。失

敗的原因有三個:

1) 系統記憶體不夠;

2) 程序表滿(容量一般為200~400);

3) 使用者的子程序太多(一般不超過25個)。

錯誤程式碼:EAGAIN 記憶體不足;ENOMEM 記憶體不足,無法配置核心所需的資料結構空

間。

四、實驗內容和步驟

實驗一:

在Linux環境下,用c語言程式設計,使用系統呼叫fork建立程序多個子程序。

(1)在終端裡輸入vim test.c,啟動vi.

(2)按a或者i進入插入模式,在裡面輸入以下程式碼。

#include <unistd.h> 

#include <stdio.h>  

int main ()  

{  

    pid_t fpid; //fpid表示fork函式返回的值 

    fpid=fork();  

    printf(" you print me\n"); 

    return 0; 

}

(3)按Esc鍵進入命令模式,輸入:和wq 進行儲存退出vim.

(4)用gcc編譯器編譯該程式: gcc –o test test.c –ggdb

(5)執行剛剛編譯的程式:./a

 或者進行調式執行

     用gcc編譯器編譯該程式: gcc –o test test.c –ggdb

除錯剛剛編譯的程式:gdb a

要求:

  1. 請說出執行這個程式後,將一共執行幾個程序。
  2. 觀察執行結果,並給出分析與解釋。

執行結果:

   

分析:

    由於呼叫fork()函式產生了一個新的程序,子程序複製了父程序的執行環境(上下文),由於兩個程序的執行,所以產生了兩條輸出語句。

實驗二:

#include <unistd.h> 

#include <stdio.h>  

int main ()  

{  

    pid_t fpid; //fpid表示fork函式返回的值 

    int count=0; 

    fpid=fork();  

    if (fpid < 0)  

        printf("error infork!");  

    else if (fpid == 0) { 

        printf("i am the childprocess, my process id is %d/n",getpid());  

         count++; 

    } 

    else { 

        printf("i am the parentprocess, my process id is %d/n",getpid());  

         count++; 

    } 

    printf("統計結果是: %d/n",count); 

    return 0; 

}

要求:

觀察執行結果,並給出分析與解釋

實驗結果:

分析:

在主函式中,fork函式建立了一個子程序,建立成功後在main函式中返回子程序的pid,執行else語句,count++;子程序中,返回0執行elseif 語句,count++;由於二者的count是各自獨立的,因此都是1。

實驗三:

#include <unistd.h> 

#include <stdio.h>  

int main ()  

{  

pid_t pid1;

pid_t pid2;

pid1=fork();  

pid2=fork(); 

    printf("pid1:%d,pid2:%d\n",pid1,pid2); 

    return 0; 

}

這個程式執行後,一共將執行4個程序。這4個程序並沒有嚴格的區分先後順序。

實驗結果:

分析(以結果一為例):

Main函式 :建立了兩個子程序,因此會在main函式中返回這兩個程序的pid,main                                     函式順序執行,打印出這兩個pid(617,618)

子程序1:由於執行了pid1=fork(); 因此,在新建立的子程序1中fpid1的值                              為0,由於複製了main函式的上下文,子程序1順序執行                                                    pid2=fork(); 建立之後fpid2在子程序1中的值為子子程序的pid,                                    因此列印(0,619);

子子程序(在子程序1中建立的程序):子子程序複製了子程序的所有執行環境,這其中包括fpid1的值,而在子程序1中fpid1的值為0,且fpid2在子子程序中的值為0,由此打印出(0,0)

子程序2:建立完成子程序1之後,main函式繼續執行pid2=fork();由此建立子程序2,子程序2複製了main函式的所有執行環境,這其中包括fpid1的值,且fpid2在子程序2中的值為0,因此列印(617,0);

 

實驗四:

首先分析一下程式碼執行時其輸出結果有哪幾種可能性,按照實驗1步驟編譯除錯觀察其實際輸出情況,比較兩者的差異,分析其中的原因。

void main ()

{    int  x=5;

if( fork( ) )

 {

x+=30;

       printf (“%d\n”,x);

}

 else

      printf(“%d\n”,x);

 printf((“%d\n”,x);

}

實驗結果:

                 、 

分析:

在main函式中,成功建立了一個子程序之後,返回一個非0的pid,執行else語句,列印了兩個5;

在子程序中fork函式返回了0,因此執行if語句,x+=30,會列印兩個30,由於父子程序執行順序不確定,因此結果會有四種。但總是兩個5,兩個30.

實驗五:

int main() 

{ int size=1;

  int i=0; 

  printf("i son/pa ppid pid fpid/n"); 

  //ppid指當前程序的父程序pid 

  //pid指當前程序的pid, 

  //fpid指fork返回給當前程序的值 

  for(i=0;i<size;i++){ 

      pid_t fpid=fork(); 

      if(fpid==0) 

           printf("%d child  %4d %4d%4d/n",i,getppid(),getpid(),fpid); 

      else 

           printf("%d parent %4d %4d%4d/n",i,getppid(),getpid(),fpid); 

  } 

  return 0; 

}

要求:

1、觀察執行結果,並給出分析與解釋。

2、如果size=2的時候會有什麼結果,為什麼。

實驗結果:

分析:

由於迴圈只執行一次,在main函式中,fork函式返回值不為0故執行else部分,fpid返回值為子程序pid;在子程序中,fork函式返回值為1,故執行if部分,fpid值為0;

當size為2的時候的實驗結果:

分析

五、實驗總結

1.寫出實驗報告。

2. 利用vim對linux文字檔案進行編輯。

3.編寫fork()程式,並利用gcc和gdb進行編譯、除錯和執行。