首頁  >  文章  >  運維  >  行程環境--行程管理

行程環境--行程管理

巴扎黑
巴扎黑原創
2017-07-23 11:16:271636瀏覽

程式執行時,main函數是如何被呼叫的,命令列參數是如何傳遞給程式的,典型的儲存空間佈局是什麼樣式,如何分配其他儲存空間,進程如何使用環境變量,進程的終止等等這些都是進程控制的基礎知識。


進程的啟動和終止

核心執行c程式時,利用exec函數呼叫一個特殊的啟動例程,該啟動例程叢內核中取得命令列參數和環境變數值。

進程終止的情況

5種正常終止的情況:

(1)从main函数返回;
(2)调用exit;
(3)调用_exit和_Exit函数;
(4)最后一个线程调用pthread_exit;
(5)最后一个线程从其启动例程返回;

3種異常終止情況

(1)调用abort;
(2)接到一个信号;
(3)最后一个线程对取消请求做出响应;

進程啟動和終止圖

行程環境--行程管理

atexit函數

一個行程最多可以登記32和函數(例如:signal函數),這些函數由exit函數自動呼叫。在程式終止時呼叫這些函數,形成終止處理程序,來進行結束進程前的收尾工作。而exit函數透過atexit函數的登記記錄來判斷要呼叫哪些函數。

exit函數

此函數由ISO C 定義,其操作包括處理終止處理程序,然後關閉所有標準I/O流。 要注意的是,它不會處理檔案描述子、多進程(父子進程)以及作業控制。

_e(E)xit函數
ISO C 定義這個函數的目的是要為行程提供一個無需執行終止處理程序或訊號處理函數的方法而終止程式。但ISO C 對標準I/O流是否進行沖洗,取決於作業系統的實現。在unix中,是不進行沖洗的。

exit和_e(E)ixt函數的狀態碼

無論進程怎樣結束,它都會在核心上執行同一段程式碼(由進程啟動和退出圖可知)。這段程式碼來關閉所有的檔案描述符,釋放所有的儲存空間。

程式退出後,利用退出碼告知該行程的父行程。父行程透過wait或waitpid函數來完成該子程序的善後工作(取得子程序相關資訊 釋放子程序佔用資源)。若父行程沒有處理子行程的退出狀態,子行程變成僵死行程。相反的,若父行程在子行程前終止,則子行程變成孤兒行程#。孤兒程序會由1號程序(init程序)接收,大致過程如下:

(1)进程终止时,内核逐个检查所有活动的进程;
(2)分析查找该终止进程的子进程;
(3)将该进程的子进程的父进程ID改为1;

wait和waitpid函數

程式正常或異常終止時,核心都會向父行程發送SIGNAL訊號。子程序終止是非同步事件,所以該訊號也是非同步訊號。而該訊號一般會被父進程預設忽略。或提供一個訊號處理函數來善後。 wait和waitpid函數就是其中的訊號處理函數的一部分。

wait和waitpid函數差異如下:

(1)wait会阻塞调用者进程等待直至第一个终止的子进程到来;
(2)waitpid可以通过参数设置,来实现调用者进程不阻塞,或选择要阻
塞等待的子进程;

這裡的呼叫者指的是父程序

環境表和環境變數

環境表結構圖

行程環境--行程管理

  • 每個程式都接收到一張環境表

  • 表也​​是一個字元指標數組

  • enrivon叫做環境指標

  • 指標數組叫做環境表

#各個指標指向的字串叫做環境字串

環境變數

#unix核心並沒有檢查環境字串,它們的解釋完全取決於各個應用程式

通常在一個shell啟動檔案中設定環境變數來控制shell的動作

修改或增加環境變數時,只能影響當前進程以及其後(之前的不行)生成和調用的任何子進程的環境,但不能影響其父進程的環境行程環境--行程管理

  • 和環境變數相關的函數如下:

    #include<stdlib.h>
    char *getenv(const char *name);
          返回值:指向与name关联的value的指针;若未找到,返回NULL
    
    int putenv(char *str);
                           返回值:若成功,返回0;若出错,返回非0
                           
    int setenv(const char *name, const char *value,
                int rewrite);
    int unsetenv(const char *name);
                    两个函数返回值:若成功,返回0;若出错,返回-1</stdlib.h>
    這些函數如何修改環境表的
    環境表和環境字串通常存放在記憶體空間的高位址處(頂部)。所以在修改它的值時,記憶體是不能繼續向高位址延伸;但又因為,它之下是各個棧幀,所以也不能向下延伸。如何修改它的值的過程如下:
  • (1)修改環境表

    1)新value = 旧value,调用malloc函数,在堆区开辟新的存储空间,
    将新value复制到这里,再将这片存储区首地址写到环境表相应的位置处。

    (2)新增環境表######
    1)新增一个环境变量,调用malloc函数开辟新的存储空间,将原来的环
    境表复制到该存储区,其次再添加一个环境变量,然后在尾部赋值为NULL,
    最后将environ指向该区域;
    2)在 1)过程的基础上,调用realloc函数,多次添加环境变量;
    ######注意:###以這種方式修改的環境變數只在當下程式運行時有效,當程式結束時,對應的儲存區被系統回收,這些修改就會失效。 ######記憶體儲存結構補充說明######記憶體管理結構圖#########################未初始化資料段( block started by symbol):###在程序開始執###行之前,內核將此段中的數據初始化為0或空指針;###############棧: ###每次函數呼叫時,其傳回位址以及呼叫者的環境資訊(如某些機器暫存器的值)都存放在堆疊中;###
  • 共享库:只需在所有进程都可引用的存储区中保存这种库例程的一个副本;

存储空间分配函数

#include<stdlib.h>
void *malloc(size_t size);
void *calloc(size_t nojy, size_t size);
void *realloc(void *ptr, size_t newsize);
         3个函数返回值:若成功,返回非空指针;若出错,返回NULL</stdlib.h>
  • malloc函数:初始值不确定;底层通过调用sbrk函数实现;

  • calloc函数:初始值为0;

  • realloc函数:增加或减少以前分配区的长度;当增加长度时,可能将以前分配区的内容移到另一个足够大的区域,以便在分配区末尾增加存储区,而新增存储区初始值不确定(例如:可变数组的使用);

注意:这些动态分配的函数一般在分配存储空间时,会比要求的大。因为在开辟空间的前后部分存储记录管理信息。因此,在使用时,千万不要越界访问,以免造成不可预知的后果。

函数间跳转策略

在c语言中,goto语句是不能跨函数跳转的。尤其是在函数深层调用时的跳转需求,在出错处理的情况下非常有用。

#include<setjmp.h>
int setjmp(jmp_buf env);
          返回值:若直接调用,返回0;若从longjmp返回,返回非0
void longjmp(jmp_buf env, int val);</setjmp.h>

变量值回滚问题:自动变量和寄存器变量会存在回滚现象。利用volatile属性来避免此类情况的发生。(在给变量赋值时,赋的值回首先存储在内存(存储器变量)中,然后在由cpu取走,存储在cpu的寄存器上(寄存器变量)。在做系统优化时,那些频繁使用的变量,会直接存储到寄存器中而不经过内存。)

寄存器变量会存在回滚现象的探究

在调用setjmp函数时,内核会把当前的栈顶指针保存在env变量中,所以在调用longjmp函数返回该位置时,全局变量、静态变量、易失变量和自动变量如果在调用setjmp和longjmp函数之间它们的值被修改过,是不会回滚到setjmp函数调用之前的值(当然,编译器将auto变量优化为寄存器变量除外)。因为,这些存储器变量的值是存储在内存相应的段中,回到原先栈顶状态时,同样访问的还是原先的内存空间。

然而,对于寄存器变量来说,首先要明确一点:寄存器变量是用动态存储的方式。意思是寄存器变量的值可能存在不同的寄存器中。如果在调setjmp和longjmp函数之间它们的值被修改过,这个值可能不会存到setjmp之前的对其赋值的寄存器中,而在调用longjmp函数后,又回到了调用setjmp函数时的状态。这个时候再读取寄存器变量的值时,读到的是原先那个寄存器中存储的值而不是修改过的那个寄存器中存储的值,所以出现的回滚现象。

以上是行程環境--行程管理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn