天天看點

《UNIX環境進階程式設計》第七章程序環境

7.2 main函數

1.C程式總是從main函數開始執行的,原型:int main(int argc,char *argv[]);

argc是指令行參數的個數

argc是指向參數的各個指針所構成的數組

2.核心執行C程式時,在調用main前先調用一個特殊的啟動例程。可執行程式檔案将此啟動例程作為程式的起始位址。啟動例程從核心取得指令行參數和環境變量值,然後為按照上述方式調用main函數做好安排。(這是由連接配接編輯器設定的,而連接配接編輯器則由C編譯器調用)

啟動例程有點像這樣子:

exit(main(argc, argv));

7.3 程序終止

有8中方式使程序終止,其中5種是正常終止,分别是:

1)從main傳回

2)調用exit

3)調用_exit或Exit

4)最後一個線程從其啟動例程傳回

5)從最後一個線程調用pthread_exit

異常終止有三種:

1)調用abort

2)接到一個信号

3)最後一個線程對取消請求做出相應

1.退出函數

3個函數用來正常終止一個程式:_exit和_Exit立即進入核心,exit會先執行一些清理處理,然後傳回核心。

這3個函數都帶一個整型參數,稱為終止狀态(或退出狀态,exit status)。可以通過shell檢查程序終止的狀态。

下面三種情況程序終止狀态是未定義的:

a.調用這寫函數時不帶終止狀态

b.main函數執行了一個無傳回值的return語句

c.main沒有聲明傳回類型為整型

特殊情況:若main的傳回類型是整型,并且main執行到最後一條語句時傳回,那麼程序是終止狀态是0.

2.atexit函數

這個函數可以用來登記函數進給程序。登記的函數将由exit自動調用。

按照ISO C的規定,一個程序可以登記多至32個函數。先登記的後調用,同一函數登記多次則會被調用多次。

原型:

#include<stdlib.h>

int atexit(void(fun*)(void));  // 調用時傳一個函數指針就可以了。

7.4 指令行參數

7.5 環境表

每個程式都接收到一張環境表。跟參數清單一樣,環境表也是一個字元指針數組。其中每個指針包含一個以null結束的C字元串的位址。

全局變量environ則包含了該指針數組的位址。

列印環境表:

extern char** environ;
void printEnv()
{
    char **env = environ;
    while(*env)
    {
        printf("%s\n", *env);
        env++;
    }    
}      

7.6 C程式的存儲空間布局(這個是重點)

曆史沿襲至今,C程式一直由下列幾部分組成:

位址由低到高:

正文段(代碼段):由CPU執行的機器指令部分。通常,正文段是可共享的。存放的有:代碼、const全局變量、const靜态變量、字元串字面值

資料段(已經初始化了):包含了程式中明确的賦初值的變量。例如:C程式任何函數之外的聲明。

BSS段(未初始化資料段):未初始化的全局變量和靜态變量

堆區:用來動态配置設定的記憶體,malloc出來的東西(由低向高擴散)

棧區:自動變量以及每次函數調用時所需儲存的資訊儲存在此段中。(由高向低擴散)。主要有非靜态的普通局部變量,函數參數,函數傳回值,匿名變量。

指令行參數和環境變量:

可以用size檢視正文段、資料段、bss段的長度(以位元組為機關):

xcy@xcy-virtual-machine:~/test/unix$ sizea.out 
  text       data       bss       dec       hex   filename
  1943      584       16     2543      9ef   a.out
xcy@xcy-virtual-machine:~/test/unix$      

第4清單示十進制的3段總和,第5清單示十六進制的三段總和。

7.7 共享庫

共享庫使得可執行檔案中不再需要包含公用的庫函數,而隻需要在所有程序都可引用的存儲區中儲存這種庫例程的一個副本。

程式第一次執行或第一次調用某個庫函數時,用動态連結方法将程式與共享庫函數相連結。

這減少了每個可執行檔案的長度,但是增加了一些運作時間開銷。這種時間開銷發生在該程式第一次被執行時。

還有一個優點:可以用庫函數的新版本代替老版本而不需要對使用該庫的程式重新連接配接編輯。(這麼一看有點像動态庫)

在不同的系統中,程式可能使用不同的方法說明是否要使用共享庫。

例如:

xcy@xcy-virtual-machine:~/test/unix$ gcc -static hello.c   // 阻止gcc使用共享庫
xcy@xcy-virtual-machine:~/test/unix$ size a.out 
  text       data       bss       dec       hex   filename
783675     7532     9600 800807   c3827   a.out
xcy@xcy-virtual-machine:~/test/unix$ gcc hello.c  // gcc 預設使用共享庫,可以看到正文和資料段的長度明顯減小。
xcy@xcy-virtual-machine:~/test/unix$ size a.out 
  text       data       bss       dec       hex   filename
  1943      584       16     2543      9ef   a.out
xcy@xcy-virtual-machine:~/test/unix$      

7.8 存儲空間配置設定

1)malloc:配置設定指定位元組數的存儲區。次存儲區中的初始值不确定

2)calloc:為指定數量指定長度的對象配置設定存儲空間。該空間中的每一位(bit)都初始化為0

3)realloc:增加或減少以前配置設定區長度。當增加長度時,可能需要将以前配置設定區的内容移到另一個足夠大的區域,以便在尾端提供增加的存儲區,而新增區域内的初始值是不确定的。

這三個配置設定函數所傳回的指針一定是适當對齊的,使其可以用于任何資料對象。傳回值都是void*

7.9 環境變量

環境變量字元串的形式是: name=value

UNIX 核心并不檢視這些字元串,它們的解釋完全取決于各個應用程式。

ISO C定義了一個函數getenv。可以用其取環境變量值。

還可以設定環境變量,比如改變環境變量、增加新的環境變量。

int putenv(char *str);   //取形式為name=value的字元串,将其放到環境表中。若name已經存在,則會删除之前的定義。

int setenv(const char *name, const char*value, int rewrite); // 設定環境變量。若name存在,rewrite表示是否删除現有的定義。

int unsetenv(const char *name); // 删除name的定義,即使不存在也不算出錯。

注意:環境表和環境字元串通常占用的是程序位址空間的頂部,是以它不能在向高位址擴充了;同時也不能移動在它之下的各棧幀,是以也不能向低位址方向擴充。

那麼是如何實作上述操作的呢?(這個也需要了解)

1)删除:比較簡單,先在環境表中找到改指針,然後将所有後續指針都向環境表首部順次移動一個位置。

2)修改:分兩種情況

a:若新value的長度小于等于現有value的長度,那麼就直接複制到原來的空間中就好了

b:假如新value更長,就需要先malloc為新字元串配置設定空間,然後将新字元串複制到該空間中,接着使環境表中的針對name的指針指向新配置設定區。

3)增加:先調用malloc為name=value字元串配置設定空間。接着:

a:如果是第一次增加一個新的name,就需要調用malloc為新的指針表配置設定空間。接着将原來的環境表配置設定到新的配置設定區,并将指向新name=value字元串的指針存放在改指針表的末尾,然後又存放一個null指針在最後。最後使environ指向新的指針表

b:如果不是第一次增加name,就知道已經用malloc在對中為環境表中配置設定了空間,就隻要調用realloc,以配置設定比原空間多存放一個指針的空間,然後将指向新的name=value字元串的指針存放在該表尾,後面接一個空指針。

7.10 函數setjmp和longjmp

這兩個函數可以實作跨越函數類型的跳轉。

棧幀:每個被調用的函數在棧裡都有自己的函數棧。每個函數棧就叫棧幀。(這個是我自己的了解)

這兩個函數不是普通的C語言goto語句在一個函數内實施的跳轉,而是在棧上調用若幹調用幀,傳回到目前函數調用路徑上的某一函數。

詳細的東西沒有仔細看。有人看我再寫這節的内容。

靜态變量,程式在啟動的時候,便為該變量配置設定了記憶體空間,程式中用extern和static關鍵标志,程式一開始執行的時候就已經存在了,但是不等于它們在整個程式中可用。

動态變量,也叫自動存儲變量。c++把變量預設為自動存儲。用static說明的局部變量隻能在定義該變量的函數體中使用。不過與自動變量不同的是,static靜态變量在第一次使用時進行初始化(預設初始值為0)。

函數退出時,系統保持該變量的值和存儲空間。然後你下次調用這個函數時,static變量還是上次退出函數時的值。

7.11 函數getrlimit和setrlimit

每個程序都有一組資源限制,其中一些可以用getrlimit和setrlimit函數查詢和更改

      #include <sys/time.h>

       #include <sys/resource.h>

      int getrlimit(int resource, struct rlimit *rlim);

      int setrlimit(int resource, const struct rlimit *rlim);

在更改資源限制時,須遵循下列三條規則:

1)任何一個程序都可以将一個軟限制值更改為小于或等于其硬限制值

2)任何一個程序都可降低其硬限制值,但是它必須大于等于其軟限制值。這種降低對于普通使用者而言是不可逆的。

3)隻有超級使用者程序才能提高硬限制值

下面的函數可以列印出各個限制,具體限制的定義就不列出來了:

《UNIX環境進階程式設計》第七章程式環境
《UNIX環境進階程式設計》第七章程式環境
#include<stdio.h>
#include<sys/resource.h>
#include"comm.h" // 自定義頭檔案,内容見下面
/*
#ifndef __COMM_H__
#define __COMM_H__
#include<errno.h>
#include<stdlib.h>
#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)
#endif // __COMM_H__
*/
#define doit(name) pr_limits(#name, name)
static void pr_limits(char *name, int resource)
{
    struct rlimit limit;
    unsigned long long lim;
    if(getrlimit(resource, &limit) < 0)
        ERR_EXIT("getrlimit");
    printf("%-14s ", name);
    if(limit.rlim_cur == RLIM_INFINITY)
    {
        printf("(infinite) ");
    }
    else
    {
        lim = limit.rlim_cur;
        printf("%10lld ", lim);
    }
    if(limit.rlim_max == RLIM_INFINITY)
    {
        printf("(indinite) ");
    }
    else
    {
        lim = limit.rlim_max;
        printf("%10lld", lim);
    }
    putchar((int)'\n');
}
int main()
{
#ifdef RLIMIT_AS
    doit(RLIMIT_AS); // 程序總的可用存儲空間的最大長度(位元組)
#endif
    doit(RLIMIT_CORE); // core檔案的最大位元組數。為0表示阻止建立core檔案
    doit(RLIMIT_CPU); // CPU時間的最大量值(秒)。超過此軟限制時,向該程序發送SIGXCPU信号
    doit(RLIMIT_DATA); // 資料段的最大位元組長度
    doit(RLIMIT_FSIZE); // 可用建立的檔案的最大位元組長度
#ifdef RLIMIT_MEMLOCK
    doit(RLIMIT_MEMLOCK); // 一個程序使用mlock(2)能夠鎖定在存儲空間中的最大位元組長度
#endif
#ifdef RLIMIT_MSGQUEUE
    doit(RLIMIT_MSGQUEUE); // 程序為POSIX消息隊列可配置設定的最大存儲位元組數
#endif
#ifdef RLIMIT_NICE
    doit(RLIMIT_NICE); // 影響程序的排程優先級
#endif
    doit(RLIMIT_NOFILE); // 每個程序能打開的最多檔案數
#ifdef RLIMIT_NPROC
    doit(RLIMIT_NPROC); // 每個實際使用者ID可擁有的最大子程序數
#endif
#ifdef RLIMIT_NPTS
    doit(RLIMIT_NPTS); // 使用者可同時打開的僞終端的最大數量
#endif
#ifdef RLIMIT_RSS
    doit(RLIMIT_RSS); // 最大駐記憶體集位元組長度
#endif
#ifdef RLIMIT_SBSIZE
    doit(RLIMIT_SBSIZE); // 任意時刻一個使用者可以占用的套接字緩沖區的最大長度
#endif
#ifdef RLIMIT_SIGPENDING
    doit(RLIMIT_SIGPENDING); // 一個程序可排隊的信号的最大數量
#endif
    
    doit(RLIMIT_STACK); // 棧的最大位元組長度
#ifdef RLIMIT_SWAP
    doit(RLIMIT_SWAP); // 使用者可消耗的交換空間的最大位元組數
#endif
#ifdef RLIMIT_VMEM // RLIMIT_AS的同義詞
    doit(RLIMIT_VMEM);
#endif
    exit(0);
}      

View Code

在虛拟機中運作:烏班圖14.04,64位的結果如下:

xcy@xcy-virtual-machine:~/test/unix$ ./a.out 
RLIMIT_AS      (infinite) (indinite) 
RLIMIT_CORE    (infinite) (indinite) 
RLIMIT_CPU     (infinite) (indinite) 
RLIMIT_DATA    (infinite) (indinite) 
RLIMIT_FSIZE   (infinite) (indinite) 
RLIMIT_MEMLOCK      65536      65536
RLIMIT_MSGQUEUE     819200     819200
RLIMIT_NICE             0          0
RLIMIT_NOFILE        1024       4096
RLIMIT_NPROC        15636      15636
RLIMIT_RSS     (infinite) (indinite) 
RLIMIT_SIGPENDING      15636      15636
RLIMIT_STACK      8388608 (indinite) 
xcy@xcy-virtual-machine:~/test/unix$