[APUE] 進程環境

📓 APUE 一書的第七章學習筆記。java

進程終止

有 8 種方式可使得進程終止,5 種爲正常方式:數組

  1. Return from main
  2. Calling exit()
  3. Calling _exit or _Exit
  4. Return of the last thread from its start routine
  5. Calling pthread_exit from the last thread

3 種非正常終止方式:bash

  1. Calling abort
  2. Receipt of a signal
  3. Response of the last thread to a cancellation request

退出函數

函數原型:架構

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

區別:_exit/_Exit 會當即進入內核;exit 先執行清理處理(對全部打開的 Stream 執行 fclose ),後進入內核。函數

exit(k) 至關於在 main 函數中 return k ,最近一個進程的退出碼能夠在 Shell 中使用 echo $? 獲取。佈局

回調函數 atexit

按照 ISO C 標準,一個進程最多能夠登記 32 個回調函數,這些函數稱爲終止處理程序 (exit handler),由 exit 來調用。學習

函數原型:spa

int atexit(void (*func)(void));
// Returns: 0 if OK, nonzero on error

exit 調用終止處理函數的順序與登記順序相反(_exit/_Exit 則不會調用);若是登記屢次,也會被調用屢次。命令行

例子:指針

void test1() { printf("A "); }
void test2() { printf("B "); }
int main()
{
    atexit(test1);
    atexit(test1);
    atexit(test2);
    // 若是調用下面 2 個函數,則不會有輸出
    // _exit(0), _Exit(0)
}
// Output: B A A

下圖展現了一個 C 程序如何啓動和終止的過程,也顯示了 exit, _exit, _Exit, atexit 這 4 個函數的關係。

環境表

環境表 (Environment List), 與命令行參數 argv 同樣,是一個 char* 數組。

下列程序能夠打印全部的環境參數:

#include <stdio.h>
extern char **environ;
int main()
{
    int i;
    for (i = 0; environ[i] != NULL; i++)
        puts(environ[i]);
}

輸出一大片:

TERM=xterm-256color
SHELL=/bin/bash
...
USER=sinkinben
LS_COLORS=rs=0:di=01;...
PATH=...
MAIL=/var/mail/sinkinben
PWD=/home/sinkinben/workspace/apue
HOME=/home/sinkinben
...
_=./a.out

C 存儲佈局

以下圖所示,一個 C 程序由如下部分組成:

  • text 段:這是 CPU 執行的機器指令部分。text 段一般是隻讀(防止意外修改)和可共享的,即便是頻繁執行的程序,它們的 text 段在內存中只會存在 1 個副本。(難怪個人 Mac 開機第一次調用 clang++ 會特別慢)
  • 初始化數據段(簡稱數據段):包括初始化的全局變量和 static 修飾的變量。
  • 未初始化的數據段(簡稱 bss 段):在程序開始執行前,由 exec 初始化爲 0 或者空指針。(確實,我還覺得是隨機值)
  • 棧:局部變量,函數調用。
  • 堆:動態內存分配。

這是一種典型的邏輯佈局,但不是全部的實現都是如此,具體取決於實際的 OS 和硬件。對於 32 位 Intel x86 架構的 Linux,text 段從 0x80480000 開始,棧從 0xC0000000 開始向低地址增加。

能夠經過 size 命令獲取一個 C 程序的各個段大小,dec, hex 分別是前 3 個數字的總和的十進制和十六進制:

$ size /bin/bash 
   text    data     bss     dec     hex filename
 997958   36496   23480 1057934  10248e /bin/bash

共享庫

共享庫 (Shared Libraries): 對於一些經常使用的公共函數庫,只須要在全部進程均可引用的存儲區保存一份副本。程序第一次調用某個庫函數的同時,用動態連接的方式將程序與庫函數相連接。這就減小了可執行文件的大小,但增長了一些運行時開銷。

使用 Shared Libraries 的另一個優勢是:能夠動態更新某個庫的版本,而不須要從新對使用該庫的程序從新連接。

使用 gcc -static 可指定生成的可執行文件使用靜態連接。

$ gcc test.c ; size ./a.out 
   text    data     bss     dec     hex filename
   1099     544       8    1651     673 ./a.out
$ gcc test.c -static ; size ./a.out 
   text    data     bss     dec     hex filename
 823142    7284    6360  836786   cc4b2 ./a.out

動態內存分配

相關函數:

#include <stdlib.h>
void *malloc(size_t size);
void *calloc(size_t nobj, size_t size); 
void *realloc(void *ptr, size_t newsize);
// All three return: non-null pointer if OK, NULL on error
void free(void *ptr);

做用:

  • malloc: 申請指定字節數的內存,該內存的值不肯定。
  • calloc: 爲指定數量,指定長度的對象分配內存,並初始化爲 0 。
  • realloc: 增長或者減小 ptr 指向內存區的長度。當增長長度時,可能須要將以前的數據拷貝到另一個足夠大的內存區(也有多是在原有基礎上增長一段連續內存),新增區域的初始值不肯定。

環境變量

getenv

#include <stdlib.h>
char *getenv(const char *name);
// Returns: pointer to value associated with name, NULL if not found

獲取環境變量。例子:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    puts(getenv("JAVA_HOME"));
}
// Output: /usr/local/java/jdk1.7

putenv, setenv, unsetenv

相關 API:

#include <stdlib.h> 
int putenv(char *str);
// Returns: 0 if OK, nonzero on error
int setenv(const char *name, const char *value, int rewrite); 
int unsetenv(const char *name);
// Both return: 0 if OK, −1 on error

改變當前進程以及後續產生的子進程的環境變量(其實是修改進程的環境表)。做用分別以下:

  • putenv: 把 name=value 的環境變量添加到環境表,若是 name 已存在,則刪除原來的定義。
  • setenv: 將 name 設置爲 valuerewrite = 0/1 表示是否覆蓋已有的 name (若是有的話)。
  • unsetenv: 刪除 name ,若是不存在則什麼都不作。

getrlimit, setrlimit

每一個進程都有一組資源限制,能夠經過 getrlimit, setrlimit 進行查詢和修改:

#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlptr);
int setrlimit(int resource, const struct rlimit *rlptr);
// Both return: 0 if OK, −1 on error

resource 是形如 RLIMIT_CPU 的一組宏定義。

結構體 rlimit 的定義以下:

struct rlimit {
     rlim_t  rlim_cur;  /* soft limit: current limit */
     rlim_t  rlim_max;  /* hard limit: maximum value for rlim_cur */
};

修改操做須要遵循如下規則:

  • cur <= max
  • 能夠下降 rlim_max ,但必須大於等於 rlim_cur.
  • 只有超級用戶進程能夠提升 rlim_max

修改資源限制會應當當前進程和它的子進程,因此 Shell 中通常會內置 ulimit 命令來修改當前 Shell 的資源限制。

相關文章
相關標籤/搜索