UNIX高級編程總結-----進程環境

1、進程的終止方式

        通常情況下,進程有八種終止方式(5種正常終止 + 3種異常終止)

        正常終止:

      (1) main 的 正常 return

      (2)調用 exit 退出

      (3)調用 _exit 和 _Exit

      (4)最後一個線程從啓動歷程返回

      (5)最後一個線程調用pthread_exit退出

        異常終止:

      (1)調用 abort()

      (2)接到一個信號

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

1、有三種退出函數如下:

        這三個函數都屬於進程正常退出的函數,_exit 和 _Exit 會直接進入內核,exit會先執行一些清理處理,然後進入到內核。

exit 函數總會執行一個標準IO的清理關閉操作,對所有打開的文件流都會調用fclose().這三個函數都有一個整型參數,代表終止狀態。

2、atexit 函數

        可以登記一個在退出時執行的函數,有點析構函數的感覺。在ISO C裏面規定了,一個進程最多隻能登記32個函數。登記的函數無需參數,也沒有返回值。atexit 也沒有傳參和處理返回值的方式。注意:exit 之後調用他們的順序與登記時候的順序相反,並且同一個函數被登記多次,也會被調用多次。下面是函數原型以及源碼例子:

#include "apue.h"

static void    my_exit1(void);
static void    my_exit2(void);

int
main(void)
{
    if (atexit(my_exit2) != 0)
        err_sys("can't register my_exit2");

    if (atexit(my_exit1) != 0)
        err_sys("can't register my_exit1");
    if (atexit(my_exit1) != 0)
        err_sys("can't register my_exit1");

    printf("main is done\n");
    return(0);
}

static void
my_exit1(void)
{
    printf("first exit handler\n");
}

static void
my_exit2(void)
{
    printf("second exit handler\n");
}
 

運行結果:

main is done
first exit handler
first exit handler
second exit handler

3、環境表

        每一個程序都有一個環境表,它是以字符串數組形式呈現的,每一項都以 '\0' 結尾。其格式也是name=value, 如書中下表:

主函數中的參數形式如下:

          

4、C程序的存儲空間佈局

       C語言的存儲空間佈局主要有:正文段、初始化數據段、未初始化數據段、棧、堆

        (1) 正文段:CPU執行的機器指令部分。通常是可共享的,頻繁執行的程序在存儲器中也只需要一個副本。並且他是隻讀的,以防止被修改。

        (2) 初始化數據段:通常稱爲數據段。主要存放一些已初始化的全局變量。

        (3) 未初始化數據段:通常稱爲 bss 段(block started by symbol 有符號開始的塊)。在程序開始時,內核會將這些數據初始化爲0,或者是NULL指針。

        (4) 棧。程序在運行時,編譯器自動分配的一塊連續存儲區域,存放函數中的參數、局部變量等。不能動態申請、一切都靠編譯器控制。如果超出了存儲大小會出現棧溢出錯誤。入棧彈棧方式:後進先出

        (5) 堆。可以動態申請的存儲區。通常是大塊存儲。該區域需要手動申請手動釋放,若不釋放會造成內存泄漏。或是在程序結束後系統回收。

注意:

        A 、static 局部變量,初始化的放在數據段,未初始化的放在 bss 段。數據段和bss段的區別就在於,是否初始化。在網上會有靜態存儲區一說,我覺得就是整個數據段 + bss段 + 代碼段,數據段和bss段主要包含的是全局變量、靜態局部變量、常量。

        B、linux 中 可以使用size命令,查看各段長度:

書中有張圖:

 

5、對存儲空間進行動態分配

        主要有三個函數:

        malloc 和 free 不解釋;

        calloc :爲指定的對象,分配一塊相應個數的空間,並且將這段空間清零。

        realloc:在一個存儲空間的位置上,申請一塊空間,可能小,可能大。如果大,並且影響到了其他內存,則會申請一塊新區域將該區域的數據拷貝到這塊新區域並返回指針。老區域會釋放。 

        alloca 棧上的分配:

 

6、可跨函數跳躍的 setjmp 和 longjmp

        C語言的 goto 只能實現函數內的跳躍,要跨函數的跳躍需要用到 setjmp 和 longjmp。書上舉了一段代碼的例子,他存儲的地址如下:

        

        在上述的案例中,假設 cmd_add 出現了錯誤,函數會打印錯誤信息後返回,一直到main函數。該例子cmd_add比main函數低兩個層級,如果低五個層級的話,一層一層的返回就顯得特別麻煩。爲了解決這一問題,就是用了setjmp 和 longjmp。

        goto是在函數內跳轉;setjmp 和 longjmp 是在棧上跳轉。

#include <setjmp.h>

int setjmp(jmp_buf env);

           返回值:若直接調用,返回0;若從longjmp返回,則爲非0

void longjmp(jmp_buf env, int val);

#include "apue.h"
#include <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);
    }

    /*
     * Change variables after setjmp, but before longjmp.
     */
    globval = 95; autoval = 96; regival = 97; volaval = 98;
    statval = 99;

    f1(autoval, regival, volaval, statval);    /* never returns */
    exit(0);
}

static void f1(int i, int j, int k, int l)
{
    printf("in f1():\n");
    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);
}

 運行結果:

in f1():
globval = 95, autoval = 96, regival = 97, volaval = 98, statval = 99
after longjmp:
globval = 95, autoval = 96, regival = 97, volaval = 98, statval = 99 

         將希望返回的位置調用一次 setjmp,然後通過 longjmp 可以跳躍回 setjmp 的地方。

        在設置這個棧跳躍點的時候setjmp 返回的是0,

        在調用 longjmp 跳躍到棧點時,setjmp 返回的是longjmp的二參,這樣可以方便判斷這個跳躍是從哪裏來的

        setjmp 的參數是一個特殊類型的 jmp_buf  的變量,在調用 longjmp 時需要一個保存了棧信息的變量,就用 jmp_buf 數據類型保存棧的信息。

        自動變量、寄存器變量、易變變量是否會出現值得回滾(也就是上例中的1、2、3、4、5)?

        根據上例的結果看是不會回滾的,但是還要看情況,大多數的實現不會回滾自動變量和寄存器變量中的值,但這都不一定。如果有一個自動變量,我們不想讓他回滾,那麼我們可以加一個volatile 屬性。聲明爲static 或者是 全局變量,在執行longjmp時保持不變。

注意:

        1、如果將上述程序進行1級優化,得到的結果如下:

gcc -O testjmp.c

in f1():
globval = 95, autoval = 96, regival = 97, volaval = 98, statval = 99
after longjmp:
globval = 95, autoval = 2, regival = 3, volaval = 98, statval = 99

        -O、-O1:優化編譯需要更多時間,並且大型函數需要更多內存。使用-O選項,編譯器會嘗試減小代碼尺寸減少執行時間,不執行任何需要大量編譯時間的優化。

        經過優化之後,自動變量 和 寄存器變量現在在寄存器中,在調用longjmp之後,恢復爲之前setjmp的棧環境,這時如果一直在內存中的數據就會改變,在寄存器中的數據不改變。 

       2、書中舉了一個局部變量作爲緩衝區的錯誤例子:

GCC -O 優化等級詳解

 

7、getrlimit 函數 和 setrlimit 函數   

 注意:

        進程資源的限制,會影響到子進程的繼承。爲了影響一個用戶的所有後續進程,需將資源限制的設置構造在shell中,可以使用ulimit 或者 limit

#include "apue.h"
#include <sys/resource.h>

#define    doit(name)    pr_limits(#name, name)

static void    pr_limits(char *, int);

int
main(void)
{
#ifdef    RLIMIT_AS
    doit(RLIMIT_AS);
#endif

    doit(RLIMIT_CORE);
    doit(RLIMIT_CPU);
    doit(RLIMIT_DATA);
    doit(RLIMIT_FSIZE);

#ifdef    RLIMIT_MEMLOCK
    doit(RLIMIT_MEMLOCK);
#endif

#ifdef RLIMIT_MSGQUEUE
    doit(RLIMIT_MSGQUEUE);
#endif

#ifdef RLIMIT_NICE
    doit(RLIMIT_NICE);
#endif

    doit(RLIMIT_NOFILE);

#ifdef    RLIMIT_NPROC
    doit(RLIMIT_NPROC);
#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
    doit(RLIMIT_VMEM);
#endif

    exit(0);
}

static void
pr_limits(char *name, int resource)
{
    struct rlimit        limit;
    unsigned long long    lim;

    if (getrlimit(resource, &limit) < 0)
        err_sys("getrlimit error for %s", name);
    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("(infinite)");
    } else {
        lim = limit.rlim_max;
        printf("%10lld", lim);
    }
    putchar((int)'\n');
}

運行結果:

RLIMIT_AS       (infinite)  (infinite)
RLIMIT_CORE              0  (infinite)
RLIMIT_CPU      (infinite)  (infinite)
RLIMIT_DATA     (infinite)  (infinite)
RLIMIT_FSIZE    (infinite)  (infinite)
RLIMIT_MEMLOCK       65536       65536
RLIMIT_MSGQUEUE      819200      819200
RLIMIT_NICE              0           0
RLIMIT_NOFILE         1024     1048576
RLIMIT_NPROC          3611        3611
RLIMIT_RSS      (infinite)  (infinite)
RLIMIT_SIGPENDING        3611        3611
RLIMIT_STACK       8388608  (infinite)

         

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章