第七章 進程環境
本章中將學習:當程序執行時,其main函數是如何被調用的;命令行參數是如何傳遞到新程序的;典型的存儲空間佈局是什麼樣式;如何分配另外的存儲空間;進程如何使用環境變量;進程的各種不同終止方式等。另外,還將說明longjmp和setjmp函數以及它們與棧的交互作用,進程的資源控制。
1、 main函數
C程序總是從main函數開始執行,main函數的原型是:
int main(int argc,char *argv[]);
其中,argc是命令行參數的數目,argv是指向參數的各個指針所構成的數組。
2、 進程終止
有八種方式使進程終止,其中五種爲正常終止:
(1) 從main返回;
(2) 調用exit;
(3) 調用_exit或_Exit;
(4) 最後一個線程從啓動例程返回;
(5) 從最後一個線程調用pthread_exit。
異常終止有三種方式:
(6) 調用abort;
(7) 接受一個信號;
(8) 最後一個線程對取消請求做出相應。
3個函數用於正常終止一個程序:_exit和_Exit立即進入內核,exit則先執行一些清理處理(對於所有打開流調用fclose函數),然後返回內核。
#include <stdlib.h>
void exit(int status);
void _Exit(int status);
#include <unistd.h>
void _exit(int status);
3個退出函數都帶一個整形參數,稱爲終止狀態。main函數返回一個整形值與用該值調用exit是等價的:exit(0),return(0)。
shell中echo $?可以打印上次運行程序的終止碼。
函數atexit
#include <stdlib.h>
int atexit(void (*func)(void)); //參數是一個函數地址
//返回值:若成功,返回0;若出錯,返回非0.
按照ISO C規定,一個進程可以登記多至32個函數,這些函數將由exit自動調用。我們稱這些函數爲終止處理程序,並調用atexit函數來登記這些函數。
下圖顯示了一個C程序時如何啓動的,以及它終止的各種方式
內核使程序執行的唯一方法是調用一個exec函數。進程自願終止的唯一方法是顯式或隱式地(通過調用exit)調用_exit或_Exit。進程也可非自願的由一個信號使其終止。
3、 命令行參數
當執行一個程序時,調用exec的進程可將命令行參數傳遞給該新程序。下面所示的程序將其所有命令行參數都回顯到標準輸出上。
Int main(int argc, char *argv[])
{
int i;
for (i = 0; i < argc; i++) /* echo all command-line args */
printf("argv[%d]: %s\n", i, argv[i]);
exit(0);
}
下面是程序運行的情況:
4、 環境表
每個程序都接收到一張環境表。與參數表一樣,環境表也是一個字符指針數組(以null結束),全局變量environ則包含了該指針數組的地址:
extern char **environ;
通常使用getenv和putenv函數來訪問特定的環境變量。
5、 C程序的存儲空間佈局
C程序由下列幾個部分組成:
1 正文段。這是CPU執行的機器指令部分。
2 初始化數據段。通常稱爲數據段,它包含了程序中需明確地賦初值的變量。(全局變量)
3 未初始化數據段。通常稱爲bss段,在程序開始執行之前,內核將此段中的數據初始化爲0或空指針。(全局變量)
4 棧。自動變量以及每次函數調用時所需保存的信息存放在此段中。
5 堆。通常在堆中進行動態存儲分配。
size命令報告正文段、數據段和bss段的長度(以字節爲單位),如:
第4列和第5列分別以十進制和十六進制表示3段總長度。
6、 共享庫
共享庫使得可執行文件不再需要包含公用的庫函數,而只需在所有進程都可引用的存儲區中保存這種庫例程的一個副本。
程序第一次執行或第一次調用某個庫函數時,用動態鏈接的方法將程序和共享庫函數相鏈接。
共享庫的另一個優點是可以用庫函數的新版本代替老版本而無需對使用該庫的程序重新鏈接編輯。
7、 存儲空間分配
ISO C說明了3個用於存儲空間動態分配的函數。
#include <stdlib.h>
void *malloc(size_t size); //分配指定字節數的存儲區。此存儲區中的初始值不確定。
void *calloc(size_t nobj,size_t size); //爲指定數量指定長度的對象分配存儲空間。該空間中的每一位都初始化爲0
void *realloc(void *ptr,size_t newsize); //增加或減少以前分配去的長度,新增區域內的初始值不確定。
//三個函數返回值:若成功,返回非空指針;若出錯,返回NULL。
void free(void *ptr); //釋放分配的存儲區
8、 環境變量
環境字符串的形式是:name=value
下面函數用於獲取跟修改環境變量
#include <stdlib.h>
char *getenv(const char *name); //返回指向與name關聯的value的指針;若未找到,返回NULL
int putenv(char *str); //參數爲形式爲name=value的字符串,如果name已經存在,則先刪除其原來的定義
int setenv(const char *name,const char *value,int rewrite); //將name設置爲value,如果name已經存在:
//(a)rewrite非0,則首先刪除其現有的定義 (b)rewrite爲0,不刪除其現有定義,不設置新的value
int unsetenv(const char *name); //刪除name的定義
9、 函數setjmp和longjmp
setjmp和longjmp函數可以實現函數之間的跳轉,而goto語句是不能跨越函數的。
#include <setjmp.h>
int setjmp(ump_buf env);
//返回值:若直接調用,返回0;若從longjmp返回,則爲非0.
void longjmp(jmp_buf env,int val); //val將成爲setjmp處返回的值
下面演示這兩個函數的使用
#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) { //直接調用返回0,若從longjmp返回則爲非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);
}
10、 函數getrlimit和setrlimit
每個進程都有一組資源限制,其中一些可以用下面兩個函數查詢和更改。
#include <sys/resource.h>
int getrlimit(int resource,struct rlimit*rlptr);
int setrlimit(int resource,const struct rlimit*rlptr);
struct rlimit {
rlim_t rlim_cur; /* Soft limit */
rlim_t rlim_max; /* Hard limit(ceiling for rlim_cur) */
};
這兩個函數的resource參數可取下列值之一
參考:http://www.cnblogs.com/runnyu/p/4638806.html