1. 程式人生 > >《Unix環境高階程式設計》總結(五)

《Unix環境高階程式設計》總結(五)

程序環境 (第七章)

1、程序終止方式

正常終止:
- main函式返回
- exit()
- _exit() 或 _Exit()
- 最後一個執行緒返回
- 最後一個執行緒呼叫pthread_exit()

異常終止:
- abort()
- 接到一個訊號
- 最後一個執行緒對取消請求做出響應

相關函式如下:

#include <stdlib.h>
void exit(int status);
void _Exit(int status);

#include <unistd.h>
void _exit(int status);

其中exit() (或呼叫return)會呼叫終止處理函式後再呼叫_exit()/_Exit(),終止處理函式常用於處理終止前的一些資料,如關閉開啟的I/O等。status為終止狀態,同main()函式返回值。如main()宣告的返回型別不為int;或者呼叫的上述exit不帶終止狀態;或者main()執行未返回值的return,則終止狀態未定義。如main宣告為返回int,且main()執行到最後一條語句後隱藏返回,則終止狀態為0。

新增終止處理函式方法如下:

#include <stdlib.h>
int atexit(void (*func)(void));
                                    成功返回0,失敗非0

atexit()註冊函式的順序與呼叫順序相反,同個函式被註冊多次將被呼叫多次。其中支援的函式個數,各系統不一,至少為32個。

2、C程式的儲存空間

C程式的儲存空間如下:
image

需要存放在磁碟檔案中的只有正文段和初始化的資料。
- 正文段:常為可共享的只讀空間;
- 初始化資料段:即資料段,存放初始化的資料,如初始化的全域性變數;
- 非初始化資料段:即bss段,執行執行前會被自動初始化為0或NULL,如非初始化的全域性變數;
- 棧:存放自動變數及每次函式呼叫時的資訊,如函式呼叫時的返回地址、環境資訊等(會自動釋放);
- 堆:進行動態儲存分配(要手動釋放)。

檢視各段的大小,如下:

3、儲存空間分配

空間分配函式有3個,如下:

#include <stdlib.h>
void *malloc(size_t size);
                            分配空間未初始化
void *calloc(size_t nobj, size_t size);
                            分配空間初始化為0(分配size個單元,每個單元的大小為nobj)
void *realloc(void *ptr, size_t newsize); 
                            增減分配區的長度(內容未初始化,其中newsize為新的總的長度,若ptr為NULL,則同malloc

注:實現分配的長度會大小請求的長度(其中包括一些管理資訊),所以在分配空間的前後寫,會修改另一個數據單元的管理記錄資訊。對於空間的另一種常見錯誤是
重複釋放一個塊或者呼叫free()釋放非上述3個函式分配的空間。
另有一個函式 alloca() ,其用法同malloc(),但其在棧上分配空間(不用手動釋放,缺點是有的系統在函式被呼叫後,棧的空間不能增加了,便不支援該函數了)

4、環境變數

環境變數相關函式如下:

#include <stdlib.h>
char *getenv(const char *name);
    返回NULL或name對應的value
int putenv(char *str);
    成功返回0,失敗非0
    其中str形式為:"name=value",若name存在則先刪除。
int setenv(const char *name,const char *value,int rewrite);
    成功返回0,失敗-1
    若name存在,且rewrite非0,則先刪除(修改),若rewrite為0,則不修改(但不出錯)
int unsetenv(const char *name);
    成功返回0,失敗-1
    刪除指定name,若不存在也不算出錯

其中系統中定義的name如下:

putenv和setenv的區別:setenv()中會重新分配空間來存放”name=value”;而putenv不會,所以putenv的引數不能在棧中,否則會出錯。
環境表(name=value的指標陣列)和環境字串存放在程序空間的頂部(棧之上),所以該空間不能再增加。

5、實現跨函式的跳躍:setjmp()和longjmp()

相關函式定義如下:

#include <setjmp.h>
int setjmp(jmp_buf env);
    直接呼叫返回0,從longjmp返回則為非0
    指定要跳轉到的點
void longjmp(jmp_buf env,int val);
    跳轉到指定的點

注:jmp_buf用來存放恢復棧的所有資訊,通常為全域性變數,兩個函式應為同一個jmp_buf。val為setjmp的返回值(一個setjmp可能對應多個longjmp,所以用
val區分是從哪裡返回的)
在跳轉時,全域性、靜態變數的值不變,對於自動變數要想其不變,可指定 volatile 屬性。如 volatile int x;

6、查詢/更改程序的資源限制

程序的資源限制通常在系統初始化時由0程序建立,相關函式如下:

#include <sys/resource.h>
int getrlimit(int resource,struct rlimit *rlptr);
    成功返回0,出錯返回非0
int setrlimit(int resource,const struct rlimit *rlptr);
    成功返回0,出錯返回非0

struct rlimit{
    rlim_t rlim_cur;
    rlim_t rlim_max;
}

更改程序的資源限制時規則:
- 任一程序都可將其軟限制值改為小於等於其硬限制值
- 任一程序都可降低其硬限制值,但必須大於等於其軟限制值,同時對於普通使用者該操作不可逆
- 只有root可提高硬限制值

其中,RLIM_INFINITY 指定一個無限量的限制,resource 的取值如下:



注:資源限制影響到呼叫程序並由子程序繼承。shell就是這個原理。