天天看點

【程序】程序建立,執行與控制

程序建立

-->程序表

  系統用一個叫做“程序表”的東西來維護系統中的程序,程序表中的一個條目維護着存儲着一個程序的相關資訊,比如程序号,程序狀态,寄存器值等等...

  當配置設定給程序A的“時間片”使用完時,CPU會進行上下文切換以便運作其他程序,比如程序B,這裡所謂的“上下文切換”,主要就是在操作那個“程序表”,其将程序A的相關資訊(上下文)儲存到其對應的程序表項中, 與之相反,其會從對應于程序B的程序表項中讀取相關資訊并運作之。

  那麼,如果程序A建立了一個程序C呢?程序表會多這樣一個表項,并且該表項擁有一個唯一的ID,也就是程序号(PID),程序表項的其他值大部分與程序A的相同,具體說來,就是C和A共享代碼段,并且C将A的資料空間,堆棧等複制一份 ,然後從A建立C的地方開始運作。

-->fork()函數

pid_t fork(void);      

  其包含在 unistd.h 頭檔案中,

  1. 其中pid_t是表示“type of process id”的32位整數,
  2. 至于函數的傳回值,取決于在哪個程序中來檢測該值,如果是在新建立的程序中,其為0;如果是在父程序中(建立新程序的程序),其為新建立的程序的id; 如果建立失敗,則傳回負值。

  那麼如何建立一個新程序以運作一個新程式,那就會乃至exec函數,它們兩者相結合就可以了~

程序執行

-->執行程式

  當啟動一個新程序以後,新程序會複制父程序的大部份上下文并接着運作父程序中的代碼,如果我們使新程序不運作原父程序的代碼,轉而運作另外一個程式集中的代碼,這就相當于啟動了一個新程式。這裡的代碼我們可以了解成一個可執行程式。

  是以,要運作一個新程式,需要最基本的兩步:

  1. 建立一個可運作程式的環境,也就是程序。
  2. 将環境中的内容替換成你所希望的,也就是用你希望運作的可執行檔案去覆寫新程序中的原有映像,并從該可執行檔案的起始處開始執行。

  要做到第一點,非常簡單,fork函數就可以,要做到第二點,則可以利用exec函數族。

  exec是一族函數的簡稱,包含在<unistd.h>中它們作用都一樣,用一個可執行檔案覆寫程序的現有映像,并轉到該可執行檔案的起始處開始執行。

  

  原型如下:

1 int execl(const char *path, const char *arg0, ... /*, (char *)0 */); 
2 int execlp(const char *file, const char *arg0, ... /*, (char *)0 */); 
3 int execle(const char *path, const char *arg0, ... /*, (char *)0, char *const envp[]*/); 
4 int execv(const char *path, char *const argv[]); 
5 int execvp(const char *file, char *const argv[]); 
6 int execve(const char *path, char *const argv[], char *const envp[]);      

  名字這麼相近的函數,感覺好容易混淆,那就從l,v,p,e 這樣的字尾來區分:

  • l:參數為一個逗号分隔的參數清單,并以char* 0作為清單結尾(NULL)
  • v: 參數為字元串數組,數組的最後一個元素為char* 0
  • p: 可以通過系統環境變量查找檔案位置
  • e:調用者顯示傳入環境變量

  值得注意的是

  1. arg0是可執行檔案本身.
  2. 最後有一個注釋/*, (char*)0 */是提醒我們最後一個參數應該傳空字元串。
  3. 如果函數運作成功,則不會有任何傳回值,否則傳回-1,而具體的錯誤号會被設定在errno,errno是一個全局變量,用于程式設定錯誤号,跟win32的getLastError函數類似。
  4. 注意exec是"覆寫"程序的現有映像(新程序執行結束後便直接退出了).

程序控制

-->wait函數

  wait函數将目前程序休眠,直到該程序的某個子程序結束或者有特定的信号來喚醒。如果子程序正常結束,則講子程序的程序id(pid)作為傳回值,發生錯誤則傳回-1,而status參數講傳出子程序的結束狀态值。

  原型如下

pid_t wait (int * status); //包含在 <sys/wait.h> 中      

-->程序的各個狀态

  TASK_RUNNING

  可執行狀态。這是 “程序正在被CPU執行” 和 “程序正在可執行隊列中等待被CPU執行” 統稱。也可以将它們拆開成“RUNNING”和“READY”兩種狀态。

  TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE

  可中斷的睡眠狀态 和 不可中斷的睡眠狀态。處于睡眠狀态的程序不會被排程到CPU進行執行,而是否可中斷的意思是指程序是否會響應異步信号,如果是可中斷的,當程序收到某個信号時其會重新回到TASK_RUNNING狀态。值得注意的是,如果處于不可中斷的睡眠狀态時,程序将不響應異步信号,比如你無法“kill -9”

  TASK_STOPPED

  暫停狀态。這裡的STOPPED是指停止運作(暫停),而不是程序終止。向程序發送SIGSTOP信号可以讓程序暫停下來,相反,發送SIGCONT可以将其從TASK_STOPPED狀态喚醒而重新進入TASK_RUNNING狀态。

  TASK_TRACED

  被跟蹤狀态。一個程序被另一個程序“TRACE(跟蹤)"最經典的例子是DEBUG,比如使用gdb或任何一款ide的debug功能。

  TASK_TRACED和TASK_STOPPED非常相近,都是讓程序暫停下來,差別是不能通過向TASK_TRACED的程序發送SIGCONT信号讓其恢複,隻能由跟蹤該程序的那個程序發送PTRACE_CONT,PTRACE_DETACH等,也就是說得讓跟蹤程序來決定是否挂起或繼續被跟蹤程序,當然,跟蹤程序如果退出,被跟蹤程序也會重新回到TASK_RUNNING狀态

  TASK_DEAD

  僵屍狀态。很搞笑的名字,之是以是“僵屍”而不是“死亡”是因為程序已不響應任何信号以及大部分相關資料已被清除,但其TASK_STRUCT結構仍存在,這個結構相當于程序的“軀殼”,還保留着一些資訊,父程序可以利用這些資訊得到程序終止前的一些狀态。如果你看到某些文檔上描寫的ZOMBIE也是指的這個狀态。

-->退出/終止程序

1 void _exit(int status)
2 void exit(int status)      

  這兩個函數都是讓程序退出.

   參數status表示程序将以何種狀态退出,在<stdlib.h>中預定義了一些狀态,比如EXIT_SUCCESS(值為0)表示以成功狀态退出,EXIT_FAILURE(值為1)表示以失敗狀态退出。

  調用_exit函數時,其會關閉程序所有的檔案描述符,清理記憶體以及其他一些核心清理函數,但不會重新整理流(stdin, stdout, stderr ...).

   exit函數是在_exit函數之上的一個封裝,其會調用_exit,并在調用之前先重新整理流。

void abort ()      

  非正常地退出程序。其會産生一個SIGABORT信号,然後使程序戛然而止,也就意味着其不會進行清理工作, 但它會重新整理緩沖區。

void atexit( void (*f) () )      

  如果想在程序正常結束之前幹一點自定義的事情,就可以調用這個函數. 其簡單地利用你傳入的函數指針執行一個函數回調。

  值得注意的是:其僅僅在調用exit函數結束程序或程序執行完所有代碼後自然結束這兩種狀态下,回調函數才會被執行,也就是說如果程序是被_exit或abort結束的,則atexit函數無效

-->暫停程序

int pause()      

  暫停程序,可以使用pause函數,其會挂起目前程序直到有信号來喚醒或者程序被結束。

  如果你僅僅需要簡單地暫停一下(press any key to continue...), 可以使用 system("pause")這個系統調用,甚至是getch()之類的。

unsigned sleep(unsigned seconds)       

  sleep系列函數都是讓程序挂起一段時間,sleep隻能精确到秒,usleep能精确到微妙,而nanosleep傳說精度更高。

-->程序跟蹤

long ptrace(/*some args*/)      

  要像debug程式一樣去跟蹤程序,是一個比較複雜的問題。

-->waitpid 與 wait(等待子程序結束)      

  經常看到的關于waitpid的經典例子是:你下載下傳了某個軟體的安裝程式A,其在安裝即将結束時啟動了另外一個流氓軟體的安裝程式B,當B也安裝結束後,其告訴你所有安裝成功了。A和B分别在不同的程序中,A如何啟動B并知道B安裝完成了呢?

  可以很簡單地在A中用fork啟動B,然後用waitpid(或wait)來等待B的結束。

 

  參數pid:

  • 如果大于0,表示父程序所需要等待的子程序的程序号
  • 如果等于0,則表示任意任意group id和父程序相同的子程序
  • 如果等于-1, 則表示等待任意子程序(有多個子程序時,任意程序結束,函數都會傳回),此時waitpid和wait相同。
  • 如果小于-1,則取其絕對值作為需要等待的子程序的程序号

  參數stat_loc:

  表示程序退出時程序狀态的存儲位置,有一些專門的宏類根據該位置計算狀态值

  另外,waitpid和wait的關系: wait(&status) 等于 waitpid(-1, &status, 0)

QQ聯系方式:[email protected]

出處:lcw.cnblogs.com

郵箱:[email protected]

本文申明:本文版權歸作者和部落格園共有,歡迎轉載,轉載請注明出處.

繼續閱讀