APUE(7)---进程环境
一、main函數
C程序總是從main函數開始執行。main函數的原型是:
int main(int argv, char *argv[]);當內核執行C程序時,在調用main前先調用一個特殊的啟動例程。可執行程序文件將此啟動例程指定為程序的起始地址---這是由連接編輯器設置的,而連接編輯器則由C編譯器調用。
二、進程終止
一共有8種方式進程終止,其中五種為正常終止,他們是:1、從main返回;2、調用exit;3、調用_exit或_Exit;4、最后一個線程從其啟動例程返回;5、從最后一個線程調用pthread_exit。異常終止有三種:1、調用abort;2、接到一個信號;3、最后一個線程對取消請求做出響應。
1、退出函數
3個函數用于正常終止一個程序,_exit和_Exit立即進入內核,exit則限制性一些清理處理,然后返回內核。
#include <stdlib.h> void exit(int status); void _Exit(int status); #include <unistd.h>] void _exit(int status);由于歷史原因,exit函數總是執行一個標準I/O庫的清理關閉操作:對于所有打開流調用fclose函數,這造成輸出緩沖中的所有數據都被沖洗。3個退出函數都帶一個整型參數,稱之為終止狀態。如果main沒有聲明返回類型為整型,則該進程的終止狀態是未定義的。但是若main的返回類型是整型,并且main執行到最后一條語句時返回,那么該進程的終止狀態是0。
2.函數atexit
按照ISO C的規定,一個進程可以登記多至32個函數,這些函數將有exit自動調用。我們稱這些函數為終止 處理程序(exit handler),并調用atexit函數來登記這些函數。
#include <stdlb.h> int atexit(void (*func)(void));atexit的參數是一個函數地址,當調用次函數時無需向他傳遞任何參數,也不期望它返回一個值。exit調用這些函數的順序與他們登記時候的順序相反。同一函數如登記多次,也會被調用多次。exit首先調用個終止處理程序,然會關閉所有打開流。注意,內核是程序執行的唯一方法是調用一個exec函數,進程自愿終止的唯一方法是顯式或隱式調用_exit或_Exit。進程也可非自愿地由一個信號使其終止。
7-2 一個C程序如何啟動和終止的
三、環境表
每個程序都接收到一張環境表。與參數表一樣,環境表也是一個字符指針數組,其中每個指針包含一個以null結束的C字符串的地址。全局變量environ則包含了該指針數組的地址: extern char **environ。
四、C程序的存儲空間布局
歷史沿襲至今,C程序一直由下列及部分組成:
1、正文段:CPU執行的極其指令部分。通常正文段是可共享的,所以即使是頻繁執行的程序在存儲器中也只需有一個副本,另外正文段常常是只讀的,以防止程序由于意外而修改其指令。
2、初始化數據段:程序中需明確地賦初值的變量,通常稱之為數據段。
3、未初始化數據段:通常將此段稱為bbs段(block started by symbol),在程序開始執行之前,內核將此段中的數據初始化為0或空指針。未初始化的全局變量就放在這個段。
4、棧:自動變以及每次函數調用時所需保存的信息都存放在此段中。每次函數調用時,其返回地址以及調用者的環境信息都存放在棧中。然后,最近被調用的函數在棧上為其自動和臨時變量分配存儲空間。通過以這種方式使用棧,C遞歸函數可以工作。遞歸函數每次調用時自身時,就用一個新的幀棧,因此一次函數嗲用實力中的變量集不會影響另一次函數調用實例中的變量。
5、堆、通常在堆中進行動態存儲分配,由于歷史原因,堆位于未初始化數據段和棧之間
7-6 典型的存儲空間安排
未初始化數據段的內容并不存放在磁盤程序文件中。其原因是內核在程序開始運行前將他們都設置為0,需要放在磁盤程序文件中的段只有正文段和初始化數據段。
五、共享庫
共享庫是的可執行文件中不再需要包含公用的庫函數,而只需要在所有進程都可引用的存儲區中保存這種庫例程的一個副本,程序第一次執行或第一次調用某個庫函數時,用動態連接方法與共享庫函數相連接,者減少了每個可執行文件的長度,但增加了一些運行時間。共享庫還有一個優點,就是可以用新版本庫函數替換老版本庫函數,而無需對程序進行重新編譯。
六、存儲空間分配
ISOC說明了3個用于儲存空間動態分配的函數:
#include <stdlib.h> void *malloc(size_t size); void *calloc(size_t nobj, size_t size); void *realloc(void *ptr, size_t newsize);void free(void *tpr);malloc、分配指定字節數的存儲區。此存儲區中的初始值不確定;calloc,為指定數量長度的對象分配存儲空間。該空間中的每一位都初始化為0;realloc,增加或減少以前分配區的長度,當增加長度時,可能需將以前分配區的內容移到另一個足夠大的區域,一邊在尾端提供增加的存儲去,而新增區域內的初始值不確定。這3個函數所返回的指針一定是適當對齊的,使其可用于任何數據對象。
這些分配例程通常用sbrk系統調用實現。該系統調用擴充進程的堆。大多數實現分配的存儲空間比所要求的要稍大一些,額外的空間用來記錄慣例信息----分配快的長度、指向下一個分配快的指針等。這就意味著,如果超過一個已分配的尾端或者在已分配區起起始位置之前進行寫操作,則會該寫另一塊的管理信息。
七、環境變量
環境字符串的形式是: name=value,unix內核并不查看這些字符串,他們的解釋完全取決于各個應用程序。ISO C定義了一個函數getenv,可以用其取環境變量值,但是該標準又稱環境的內容是由實現定義的。ISO C沒有定義任何環境變量
#include <stdlib.h> char *getchar(const char *name);putenv取形式為name=value的字符串,將其防盜環境表中,如果name已經存在,則先刪除其原來的定義;setenv將name設置為value。如果name已經存在,如果rewrite非0,則首先刪除其現有的定義,否則不刪除其現有定義。unsetenv刪除name的定義。即使不存在這種定義也不算出錯。
#include <stdlib.h> int putenv(char *str); int setenv(const char *name, const char *value, int rewrite); int unsetenve(const char *name);環境表和環境字符串通常存放在進程存儲空間的頂部(棧之上)。刪除一個字符串很簡單---只要先在環境表中找到該指針,然后將所有后續指針都向環境表順不順序移動一個位置就可以了。如果修改一個現有的name:如果新value的長度少于或等于現有value的長度,則只要將新字符串復制到元字符串所用的空間中即可;如果新value的長度大于元長度,則必須調用malloc為新字符串分配空間,然后將新字符串復制到空間中,接著使黃競標中針對name的指針指向新分配區。
八、函數setjmp和longjmp
在C中,goto語句是不能跨越函數的,而執行這種類型跳轉功能的函數是setjmp和longjmp。這兩個函數對于處理發生很深層嵌套函數調用中的出錯情況是非常有用的。
#include "apue.h" #define TOK_ADD 5void do_line(char *); void cmd_add(void); int get_token(void);int main(void) {char line[MAXLINE];while(fgets(line, MAXLINE, stdin) != NULL){do_line(line);}exit(0); }char *tok_ptr;void do_line(char * ptr) {int cmd;tok_ptr = ptr;while((cmd = get_token()) > 0){switch(cmd){case TOK_ADD:cmdd_add();break;}} }void cmd_add(void) {int token;token = get_token(); }int get_token(void) {}7-9 進行命令處理程序的典型骨架部分
自動變量的存儲單元在每個函數的幀棧中。數組line在main的幀棧中,整型cmd在do_line的幀棧中,整型token在cmd_add的幀棧中。多層次嵌套調用返回錯誤可以使用非局部goto---setjmp和longjmp函數。非局部指的是,這不是由普通的C語言goto語句在一個函數內實施的跳轉,二是在棧上跳過若干調用幀,返回到當前函數調用路徑上的某一個函數中。
#include <setjmp.h> int setjmp(jmp_buf env); void longjmp(jmp_buf env, int val);#include "apue.h" #include <setjmp.h> #define TOK_ADD 5jmp_buf jmpbuffer;int main(void) {char line[MAXLINE];if(setjmp(jmpbuffer) != 0){printf("error");}while(fgets(line, MAXLINE, stdin) != NULL){do_line(line);}exit(0); }...void cmd_add(void) {int token;token = get_token();if(token < 0){longjmp(jmpbuffer, 1);} }
7-11 setjmp和longjmp實例
#include "apue.h" #incldue <setjmp.h>static void f1(int, int, int, int); static void f2(void);static jmp_buf jmpbuffer; static int globval;int main(void) {int autoval;register int regival;volatile int volaval;static int statval;globval = 1; autoval = 2; regival = 3; volaval = 4; statval = 5;if(setjmp(jmpbuffer) != 0){printf("after longjmp:\n");printf("globval = %d, autoval = %d, regival = %d, volaval = %d, statval = %d\n",globval, autoval, regival, volaval, statval);exit(0);} globval = 95; autoval = 96; regival = 97; volaval = 98; statval = 99;f1(globval, autoval, regival, volaval, statval);exit(0); }static void f1(int i, int j, int k, int l) {printf("globval = %d, autoval = %d, regival = %d, volaval = %d, statval = %d\n",globval, i, j, k, l);f2(); }static void f2(void) {longjmp(jmpbuffer, 1); }7-13 longjmp對各類變量的影響
全局變量、靜態變量和易失變量不受優化的影響,在longjmp之后,他們的值是最近所呈現的值。通過這一實例我們可以理解到,如果要編寫一個使用非局部跳轉的可移植程序,則必須使用volatile屬性。
九、函數getrlimit和setrlimit
每個進程都有一組資源限制,其中一些可以用getrlimit和setrlimit函數查詢和更改。
#include <sys/resource.h> int getrlimit(int resource, struct rlimit *rlptr); int setrlimit(int resource, const struc rlimit *rlptr);在更改資源限制時,遵循以下條件:1、任何一個進程可將一個軟限制值更改為小于或等于其硬件限制值;2、任何一個進程都可降低其硬限制值,但必須大于或等于其軟限制值;3、只有超級用戶進程可以提高硬限制值;4、資源限制影響到調用進程并由子進程繼承。
?
轉載于:https://www.cnblogs.com/cauchy007/p/5678401.html
總結
以上是生活随笔為你收集整理的APUE(7)---进程环境的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 端口号大全「建议收藏」
- 下一篇: spss如何选择需要的变量?