MINIX系統呼叫EXIT分析 (轉)

worldblog發表於2007-12-13
MINIX系統呼叫EXIT分析 (轉)[@more@] 

MINIX管理:namespace prefix = o ns = "urn:schemas--com::office" />

EXIT分析

 

一、系統呼叫EXIT簡介

系統呼叫exit(status)透過程式向MM傳送EXIT型別的訊息完成,它是終止一個程式的一般手段,exit關閉程式的所有,並且如果其父程式了wait則通知父程式。這個呼叫不能返回。

exit(status)的實現請參見LIB的38504行。

38504  PUBLIC void _exit(status)

38505  int status;

38506  {

38507    message m;

38508 

38509    m.m1_i1 = status;

38510    _syscall(MM, EXIT, &m);

38511  }

呼叫exit(status)的終止程式向MM發型別為EXIT的訊息,然後等待MM的應答而阻塞(永遠不能收到相應的應答)。

自由空間或別的

堆疊段(程式2)

空  隙

資料段(程式2)

正文段(程式2)

自由空間或別的程式

堆疊段(程式1)

空  隙

資料段(程式1)

正文段(程式1)

自由空間或別的程式

  圖1  結合的I和D空間的記憶體分配

在下列兩個事件都已經發生的情況下程式才會完全終止:(1)程式自己已經退出(或已經被一個訊號殺死),(2)它的父程式已經執行了WAIT系統呼叫以觀察發生了什麼。已經退出或被殺死而它的父程式還沒有為它執行WAIT的程式將進入某種狀態,有時被稱為僵死狀態。這種程式不再參與排程,它的報警時鐘如果原來是開著的,那麼將被關閉,但它仍將留在程式表中。它的記憶體被釋放。僵死是一種臨時狀態,很少會持續較長的時間,當父程式最後執行WAIT時,將釋放程式表項,並通知檔案系統和核心。

EXIT是由記憶體管理器處理的。過程do_mm_exit(16912行)接收這個呼叫,但大部分的工作是呼叫mm_exit(16927行)完成的,這樣劃分工作是因為mm_exit也被用來處理被訊號終止執行的程式,兩者工作相同,但引數不同。MM收到EXIT型別的訊息後,執行do_mm_exit(),而它再呼叫mm_exit()來完成終止程式的工作。MM執行完do_mm_exit()後不需向終止程式發應答。mm_exit()的工作如下:

1.  關閉終止程式的定時器。

2.  透過tell_fs()通知FS處理程式exit()時與檔案有關的工作。

3.  透過sys_xit()通知SYSTASK處理程式exit()時與核心有關的工作。

4.  釋放終止程式的記憶體空間。這分為兩種情況來處理:a、記憶體分配採用結合的I和D空間,如圖1;b、採用獨立的I和D空間,如圖2。

 

自由空間或別的程式

堆疊段(程式2)

空  隙

資料段(程式2)

自由空間或別的程式

堆疊段(程式1)

空 隙

資料段(程式1)

正文段(程式1、2共享)

自由空間或別的程式

  圖2  獨立的I和D空間的記憶體分配

5.  若有父程式等待終止程式終止,則通知父程式,並釋放終止程式的mproc。否則,進入僵死狀態(HANGING),等待父程式處理後再釋放mproc。

6.  將所有的子程式變成init的子程式。

在作完上述工作後,程式就正常終止,其proc、mproc等均為空閒,可供建立新程式使用。

二、和系統呼叫EXIT有關的標頭檔案和資料結構

記憶體管理器有一個主標頭檔案mm.h(15800行),每個檔案編譯時都要用到它,它包含了每個記憶體目標模組要用到的核心標頭檔案和系統範圍的標頭檔案及其它一些標頭檔案。const.h(15900行)定義了記憶體管理器用的變數,其中有些是特別為16位機器設定的。

記憶體管理器的全域性變數在glo.h中(16200行)說明,其中mp是指向一個mproc結構的指標。它是某程式表的MM部分,而該程式的系統呼叫正在被處理。dont_reply,在每一個新的請求到來是被初始化為FALSE,但在呼叫的執行過程中如果發現沒有應答資訊應該傳送,它就被設定為TRUE。例如成功的EXEC就沒有應答傳送。proc_in_use跟蹤當前使用了多少個程式表項,使確定FORK是否可行非常簡單。訊息緩衝區mm_in和mm_out是分別用於請求和應答訊息的。who是當前程式的,它與mp的關係是mp=&mproc[who];當一條訊息到達的時候,系統呼叫號被抽取出來放到mm_call中。三個變數err_code、result2和res_ptr用來儲存在應答訊息中返回給呼叫者的值。其中最重要的變數是err_code,在呼叫沒有錯誤而結束時,它被設定為OK。最後兩個變數在發生問題是使用。在程式非正常結束時,MINIX把程式的映像寫到一個core檔案中,core_name定義了這個檔案將取的名字。core_sset是一個定義哪些訊號應該產生core轉儲檔案的點陣圖。

程式表的記憶體管理器部分在檔案mproc.h中(16300行),其中定義了與程式記憶體分配有關的全部域和一些附加的資訊。最重要的域是mp_seg陣列,它有三個表項,分別用於正文、資料和堆疊段,各個表項是一個由虛地址、實體地址和段長度組成的結構,他們都是用塊而不是位元組來量度的,主要是用於把虛地址對映成實體地址。mproc還儲存著程式自己的程式號(pid)以及父程式的程式號、號(uid)和組號(gid)。幾個域和訊號處理有關,mp_ignore、mp_catch、mp_sigmask、mp_sigmask2和mp_sigpending是點陣圖,每個位代表一個可以送往程式的訊號。型別sigset_t是32位整數。陣列mp_sigact對處理訊號非常重要。對每個訊號型別都有一個陣列元素,每個元素是一個sigaction結構(00769_00773行),每個sigaction結構由三個域組成:1、sa_handler域定義訊號的處理方式,是預設還是專門處理;2、sa_msak的型別是sigset_t,定義了訊號處理時,哪些訊號將被阻塞。3、sa_flags域是一些訊號處理用的標誌。mp_flags域是一個無符號整數,用來儲存一些位,在386及以上的上是32位的,在低端CPU上則是16位。程式表的最後一個域是mp_procargs,在一個新程式啟動時,系統將建立一個類似圖4_39的堆疊,指向新程式args陣列起始地址的指標被存放在這裡,它被ps命令使用。

param.h(16400行)包含了許多用於請求資訊中的系統呼叫引數的宏和4個用於應答訊息中域的宏。

table.c(16500行)的編譯將為glo.h和mproc.h中的各種EXTERN變數和結構保留空間。在這個檔案中有一個重要的陣列call_vec(16515行),當一個請求訊息到達時,其中的系統呼叫號將被取出作為call_vec的索引,以找到處理這個系統呼叫的過程,不是合法呼叫的系統呼叫號都會引起執行no_sys,返回一個錯誤程式碼。

三、系統呼叫EXIT涉及的

 1、set_alarm()(18067行),這個函式被do_alarm()用來設定定時器,也用來關閉正在退出程式的仍在執行的定時器。

2、tell_fs()(19192行),這個函式僅僅被記憶體管理器用來向檔案系統發訊息。對於EXIT呼叫,其格式如下:tell_fs(EXIT,proc,0,0)。

3、sys_xit(),其實這是一個系統任務的系統呼叫,其工作是由函式do_xit()(15027行)完成的。因為在MINIX中,程式表被分為三部分,核心、記憶體管理器、檔案系統各佔其一,一個程式的退出需要三個程式表狀態的同時。 do_xit()的主要功能是透過查詢核心程式表,把記憶體管理器報告處於退出狀態的程式標誌為不可執行,使它不會再被排程。

4、 find_share()(17535行),這個函式透過把要執行檔案的i結點、裝置和修改時間與現有程式比較,在記憶體管理器的程式表中尋找可共享正文的程式。

5、free_mem()(18879行),這個函式的主要功能是釋放記憶體塊,並把它加入空洞表。這要進行一些處理。如果釋放的記憶體塊和空洞表中相鄰的記憶體塊是連續的,則要進行合併。否則,可直接插入到記憶體空洞表中。

6、cleanup()(17061行),這個函式的主要功能是完成程式的退出過程,即釋放程式表項,並喚醒父程式。

7、check_sig()(18265行),這個函式當系統呼叫KILL或核心俘獲DEL等訊號時被啟用。其主要功能是檢視是否可以傳送訊號,而這個訊號往往被髮給一組程式。

四、記憶體管理EXIT系統呼叫程式碼分析

16909  /*=====================================================*

16910  *   do_mm_exit   *

16911  *=====================================================*/

16912  PUBLIC int do_mm_exit()

16913  {

16914  /* Perfothe exit(status) system call. The real work is done by mm_exit(),

16915  * which is also called when a process is killed by a signal.

16916  */

/*執行EXIT系統呼叫,真正的終止程式的工作由mm_exit()來完成,mm_exit()也用來處理被訊號終止執行的程式*/

16917 

16918    mm_exit(mp, status);

/*呼叫函式mm_exit執行退出功能,終止程式的工作由這個函式來完成*/

16919    dont_reply = TRUE;  /* don't reply to newly tenated process */

/*不向剛終止的程式傳送應答, dont_reply在呼叫的執行過程中如果發現沒有應答資訊傳送,它就被設定為TRUE。 */

16920    return(OK);  /* pro forma return code */

/* EXIT過程執行順利,返回OK */

16921  }

 

 

16924  /*===================================================*

16925   *   mm_exit   *

16926  *===================================================*/

16927  PUBLIC void mm_exit(rmp, exit_status)

16928  register struct mproc *rmp;  /* pointer to the process to be terminated */

/*指向被終止程式的指標,結構mproc定義在16307行*/

16929  int exit_status;  /* the process' exit status (for parent) */

/*變數exit_status用來記錄程式的退出狀態*/

16930  {

16931  /* A process is done.  Release most of the process' possessions.  If its

16932  * parent is waiting, release the rest, else hang.

16933  */

/*這個函式用來終止程式的執行,釋放佔有的記憶體和程式表項*/

16934 

16935    register int proc_nr;

16936    int parent_waiting, right_child;

16937    pid_t pidarg, procgrp;

/*pid_t定義在01655  行*/

16938    phys_clicks base, size, s;  /* base and size used on 68000 only */

/*phys_clicks定義在03108行*/

16939 

16940    proc_nr = (int) (rmp - mproc);  /* get process slot number */

/* 得到程式在程式表中的索引號*/

16941 

16942    /* Remember a session leader's process group. */

16943    procgrp = (rmp->mp_pid == mp->mp_procgrp) ? mp->mp_procgrp : 0;

/* 判斷是否有程式組,程式組是用來訊息的。如有程式組,記下程式組號,用來接收等會兒返回的訊息 */

16944 

16945    /* If the exited process has a timer pending, kill it. */

16946    if (rmp->mp_flags & ALARM_ON) set_alarm(proc_nr, (unsigned) 0);

/* 如果程式有一個定時器在執行,就停止它 */

16947 

16948    /* Tell the kernel and FS that the process is no longer runnable. */

/* 通知核心和檔案系統這個程式不可再執行 */

16949    tell_fs(EXIT, proc_nr, 0, 0);  /* file system can free the proc slot */

/*發一條訊息給檔案系統告訴它此程式已處於退出狀態,可以釋放檔案系統中的程式表項*/

16950    sys_xit(rmp->mp_parent, proc_nr, &base, &size);

/*透過系統呼叫sys_xit發一條訊息給系統任務告訴它標誌該程式不可執行,使它不會再被排程*/

16951 

16952    /* Release the memory occupied by the child. */

/* 釋放記憶體 */

16953    if (find_share(rmp, rmp->mp_ino, rmp->mp_dev, rmp->mp_ctime) == NULL) {

16954    /* No other process shares the text segment, so free it. */

16955    free_mem(rmp->mp_seg[T].mem_phys, rmp->mp_seg[T].mem_len);

16956    }

/*搜尋程式表,確定正文段是否正與另一程式共享,因為有的系統採用結合的I和D空間,如圖1,此情況下可直接釋放所佔有的記憶體。但具有獨立的I和D空間的程式可以共享正文,如圖2。若沒有別的程式共享正文段,釋放它*/

16957    /* Free the data and stack segments. */

16958    free_mem(rmp->mp_seg[D].mem_phys,

16959    rmp->mp_seg[S].mem_vir + rmp->mp_seg[S].mem_len - rmp->mp_seg[D].mem_vir);

/*在獨立的I和D空間的程式中,由於資料段和堆疊段獨立於正文段,兩者處在不同的空間中(可以不連續),因此無論正文段是否被共享,都可以釋放此程式的資料段和堆疊段 */

16960 

16961    /* The process slot can only be freed if the parent has done a WAIT. */

/*在下列兩個事件都已經發生的情況下程式才會完全終止:(1)程式自己已經退出(或已經被一個訊號殺死),(2)它的父程式已經執行了WAIT系統呼叫以觀察發生了什麼。因此只有在父程式處於等待狀態時才能釋放子程式的程式表項*/

16962    rmp->mp_exitstatus = (char) exit_status;

/*儲存程式退出狀態,把exit_status賦給當前程式的mp_exitstatus*/

16963    pidarg = mproc[rmp->mp_parent].mp_wpid;  /* who's being waited for? */

/*獲得父程式正在等待的程式的pid,儲存在變數pidarg中*/

16964    parent_waiting = mproc[rmp->mp_parent].mp_flags & WAITING;

/*看父程式的標誌位mp_flags是否被設定成WAITING*/

16965    if (pidarg == -1 || pidarg == rmp->mp_pid || -pidarg == rmp->mp_procgrp)

16966    right_child = TRUE;  /* child meets one of the 3 tests */

/* pidarg=-1表示WAITPID系統呼叫使呼叫程式阻塞直到子程式中的任一個結束;pidarg=rmp->mp_pid(pidarg>0)表示指向一個特定的正在等待的程式;-pidarg=rmp->mp_procgrp表示等待任何程式組號=-pidarg的子程式,pidarg是在系統呼叫WAITPID中設定的*/

16967    else

16968    right_child = FALSE;  /* child fails all 3 tests */

/*不能得到父程式正在等待程式的pid,即父程式沒有處於等待狀態*/

16969    if (parent_waiting && right_child)

16970    cleanup(rmp);   /* tell parent and release child slot */

/*父程式在等待,呼叫cleanup()釋放子程式的程式表項 */

16971    else

16972    rmp->mp_flags |= HANGING;  /* parent not waiting, suspend child */

/* 否則,讓程式進入僵死狀態 */

16973 

16974    /* If the process has children, disinherit them.  INIT is the new parent. */

/* 搜尋程式表,尋找剛才終止程式的子程式,把它們變為init的子程式*/

16975    for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) {

16976    if (rmp->mp_flags & IN_USE && rmp->mp_parent == proc_nr) {

16977    /* 'rmp' now points to a child to be disinherited. */

/*rmp指向剛才被終止程式的子程式*/

16978    rmp->mp_parent = INIT_PROC_NR;

/*把它置為init的子程式*/

16979    parent_waiting = mproc[INIT_PROC_NR].mp_flags & WAITING;

/*看init是否處於等待狀態*/

16980    if (parent_waiting && (rmp->mp_flags & HANGING)) cleanup(rmp);

/*如果init正在等待且子程式進入了HANGING狀態,呼叫cleanup()處理這個子程式*/

16981    }

16982    }

16983 

16984    /* Send a hangup to the process' process group if it was a session leader. */

16985    if (procgrp != 0) check_sig(-procgrp, SIGHUP);

/* 若程式組存在,送一個訊號SIGHUP給程式組,使系統呼叫WAITPID能夠對這個程式進行相應的處理*/

16986  }

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-992945/,如需轉載,請註明出處,否則將追究法律責任。

相關文章