2016-02-02
命令行参数
当执行一个程序时,调用exec的进程可将命令行参数传递给该新程序。
其中argv[0]是 程序名称
环境表
每个程序都接收一张环境表,与参数表一样,环境表也是一个字符指针数组,其中每个指针包含一个以null借宿的字符转的地址。全局变量environ则包含了该指针数组的地址。
extern char **environ
按照惯例环境由name=value这样的字符串组成。大多数预定义名全由大写字母组成。通常用getenv和putenv函数来存取特定的环境变量,而不是用environ变量,如果要查看整个环境则必须使用environ指针。
c程序的存储空间布局
由于历史原因,c程序一直由下列几部分组成
- 正文段。这是由CPU执行的机器指令部分。通常正文段是可以共享的。多以即使是经常执行的程序在存储器中也只需有一个副本,另外正文段常常是只读的。防止程序由于意外事故而修改其自身的指令。
- 初始化数据段。通常将此段成为数据段,它包含了程序中需要赋值的变量。例如c程序中任何函数之外的说明 int maxcount=99;使此变量以初值放在树池花数据段中。
- 非初始化数据段。通常将此段成为bss,这一名称来源于早期汇编程序的一个操作符,在程序开始执行之前,内核程序将此段初始化为0,函数外的说明 long sum[1000];将次变量存放在非初始化数据段中。
- 栈。 自动变量及每次函数调用时所保存的信息都存放在此段中。每次函数调用时其返回地址,以及调用者的环境信息都存放在栈中。然后新被调用的函数在栈上为其自动和临时变量分配储存空间。通过以这种方式使用栈,c函数可以递归调用。
- 堆。同长在堆中进行动态内存分配。由于历史上形成的惯例,堆位于非初始化数据段顶和栈底之间
命令行参数和环境变量-->栈-->堆-->非初始化数据-->初始化数据-->正文
size命令报告正文段、数据段、bss段的长度
共享库
现在很多unix系统支持共享库,共享库是的可执行程序文件中不再需要包含常用的库函数,而只需在所有进程都可存取的存储区中保存这一种库例程的一个副本。程序第一次执行或则第一次调用某个库函数时,用动态链接方法将程序与共享库函数相连接。这就减少了每个可执行文件的长度,但增加了一些运行时的开销。共享库的另一个优点是可以用库函数的新版本代替老版本而无需对使用该库的程序从新连接编辑。
不同的系统使用不同的方法使说明程序是否要使用共享库。比较典型的有cc和ls命令可选
存储器分配
ANSI C说明了三个用于存储空间动态分配的函数
- malloc分配指定字符数的存储区。此存储区中的初始值不确定。
- calloc为指定长度的对象,分配能容纳指定个数的存储空间,该空间中的每一位都初始化为0
- realloc 更改以前分配去的长度。当增加长度时,可能需要将以前分配去的内容移到另一个足够大的区域,而新增区域内的初始值不确定。
void *malloc(size_t size);
void *calloc(sieze_t nobj, size_t size);
void *realloc(void *ptr, size_t newsize);
void free(void *ptr);
这单个分配函数所犯规的指针一定是适当对其的,使其可用于任何数据对象。例如,在一个特定的系统上,如果最苛刻的对其要求是double,则对其必须在8的倍数的地址单元处,那么这三个函数返回的指针都应这样对齐。
realloc在改变存储区大小的时候有可能会移动存储区的位置,因此不应当使用任何指针指向此存储区
realloc中若ptr为null则与malloc的功能相同。
这些分配例程通常调用sbrk系统调用实现。虽然sbrk可以扩从或者缩小一个进程的存储空间,但大多数malloc和free实现都不减小进程的存储空间,释放的空间可以供以后再分配,但他们被保存在malloc池而不返回给内核。
应当注意的是大多数实现所分配的存储空间比所要求的要稍微大一些,额外的空间用来记录管理信息分配块的长度,指向下一个分配快的指针等。这就意味着如果写过一个已分配区的尾端,则会改写后一块的管理信息。这种类型的错误是灾难性的。
其他的可能产生的致命性的错误是:释放了一个已经释放的块,调用free时所用的指针不是三个alloc函数返回值。
因为存储器分配出错很难跟踪,所以某些系统提供了这些函数的另外一种实现方法,每次调用者单个分配函数中的任意一个或者free时都进行附加的出错检查。
alloca函数
其调用序列与malloc相同,但是它是在当前函数的栈帧上分配存储空间,而不是在堆中。其优点是:当函数返回时,自动释放它所使用的栈帧,多以不必再为释放空间而费心。其缺点是:某些系统在函数一杯调用后不能增加栈帧的长度,于是也就不能支持alloca函数。
环境变量
如同前面所述,环境字符串的形式是:name=value
unix内核并不关心这种字符串的意义,他的解释完全取决于各个应用程序。
char *getenv(const char *name)
除了取环境变量,有时也需要设置环境变量,或者改变现有的变量的值,或者是增加新变量的(我们能影响的是当前进程及其后生成的子进程的环境,但不能影响父进程的环境)
int putenv(const char *str)
int setenv(const char *name, const char *value, int rewrite)
void unsetenv(const char *name)
setjmp和longjmp
在C中不允许使用跳跃函数goto 而执行这种跳转功能的是函数setjmp和longjmp这两个函数对于处理发生在很深的嵌套函数调用中出错情况非常有用。
int setjmp(jmp_buf env)
void longjmp(jmp_buf env, int val)
在希望返回到达的位置调用setjmp。setjmp的参数env是一个特殊类型jmp_buf这一数据类型是某种形式的数组,其中UC南方在调用longjmp时能用来恢复栈状态的所有信息。一般,env变量是个全局变量,因为需要从另一个函数中引用它。
当检查到一个错误时,则以两个参数滴啊用longjmp第一个就是在调用setjmp时所用的env第二个val是个非0值,它成为setjmp处返回的值。使用第二个参数的原因是对于一个setjmp可以有多个longjmp。
自动、寄存器和易失变量
当longjmp返回时,大多数实现并不回滚自动变量和寄存器变量。如果你有一个自动变量而又不想使其值回滚,则可以定义其为具有volatile属性。说明为全局和静态变量的值在执行longjmp时保持不变。因此如果要编写一个使用非局部跳转的可移植程序,则必须使用volatile属性。
自动变量的潜在问题
基本规则是说明自动变量的函数已经返回后,就不能再引用这些自动变量。
getrlinit和setrlimit函数
每个进程都有一组资源限制,其中某一些可以用getrlimit和setrlimit函数查询和更改
int getrlimit(int resource, struct rlinit *rlptr)
int setrlinit(int resource, const struct rlimit *rlptr)
对这两个函数的每一次调用都制定一个资源以及一个指向下列结构的指针
struct rlimit {
rlimi_t rlim_cur;
rlimi_t rlim_max;
}
进程的资源限制通常是在系统初始化时由0进程建立的,然后由后续进程继承。
更改资源限制时,需准守下列三条原则:
- 任何一进程都可将一个软限制更改为小于等于其硬件限制
- 任何一个进程都可降低其银监限制值,但它必须大于等于其软件限制值。这种降低对于普通用户而言是不可逆反的。
- 只有超级用户可以提高硬件限制
资源限制影响到调用进程并由其子进程继承。