UNIX进程环境
当内核执行C程序时,在调用main前先调用一个特殊的启动例程。可执行程序文件将此启动例程指定为程序的起始地址(由链接器设置的),启动例程从内核取得命令行参数和环境变量值,为调用main函数做好安排。
启动例程的逻辑就是:使得从main返回后立即调用exit函数。即:exit(main(argc, argv)) ;
(启动例程的作用就是把命令行参数传给main,在main结束时做一些清理工作)
1、exit函数
有三个函数用于正常终止一个程序:
#include <stdlib.h>
void exit(int status) ;
void _Exit(int status) ;
#include <unistd.h>
void _exit(int status) ;
_exit和_Exit立即进入内核,exit则先执行一些清理处理(调用执行各终止处理程序,关闭所有标准I/O流等),然后进入内核。
exit函数总是执行一个标准I/O库的清理关闭操作:为所有打开流调用fclose函数,这会造成所有缓冲的输出数据都被冲洗。
main函数的return 0;等效于 exit(0) ;
2、atexit函数
我们可以用atexit函数来登记终止处理函数,这些函数将由exit自动调用。
exit调用这些函数的顺序与它们登记时候的顺序相反。同一函数如若登记多次,则也会被调用多次。
#include <stdlib.h>
int atexit(void (*func)(void)) ;
例如:
#include <stdio.h>
#include <error.h>
#include <stdlib.h> //包含atexit
static void My_exit1(void) ;
static void My_exit2(void) ;
int
main(int argc, char** argv)
{
//注册退出处理函数
if (atexit(My_exit2) != 0)
{
perror("can't register My_exit2!\n") ;
}
if (atexit(My_exit1) != 0)
{
perror("can't register My_exit1!\n") ;
}
if (atexit(My_exit1) != 0)
{
perror("can't register My_exit1!\n") ;
}
printf("main is done!\n") ;
exit(0) ;
}
static void
My_exit1(void)
{
printf("first exit handler\n") ;
}
static void
My_exit2(void)
{
printf("second exit handler\n") ;
}
环境表
每个程序都会接收到一张环境表。与参数表一样,环境表也是一个字符指针数组。其内容如:”HOME=/home/sar\0” “PATH=:/bin:usr/bin\0” “USER=sar\0” “LOGNAME=sar\0”等
环境表(字符串的指针数组)和环境字符串通常存放在进程存储空间的顶部(栈之上)。
全局变量environ则包含了该指针数组的地址:extern char** environ ;我们可使用此指针查看整个环境。
//用系统定义的全局变量environ指针,打印出进程的整个环境表。
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
int
main(void)
{
char** ptr ;
extern char** environ ;
for (ptr = environ; *ptr != 0; ptr++)
{
printf("%s\n", *ptr) ;
}
exit(0) ;
}
ISO C定义了一个函数getenv,可以用其取特定环境变量的值。
#include <stdlib.h>
char* getenv(const char* name) ;
此函数返回一个指针,它指向name=value字符串中的value。
改变环境变量的值:
int setenv(const char* name, const char* value, int rewrite) ;
将name环境变量设置为value。如果在环境中name已经存在,那么若rewrite非0,则先删除现有的定义;若rewrite为0,则不删除其现有定义。
int unsetenv(const char* name) ;
删除name的定义。
注意:我们能影响的只是当前进程及调用的任何子进程的环境,但不能影响父进程的全局变量。(环境变量就像进程的全局变量一样,子进程会得到它的一份副本。)
资源限制
每个进程都有一组资源限制,其中一些可以用getrlimit和setrlimit函数查询和更改。
进程的资源限制通常是在系统初始化时由进程0建立的,然后每个后续进程继承。
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit * rlptr) ;
int setrlimit(int resource, const struct rlimit * rlptr) ;
详细使用,略。
C程序的存储空间布局
a.out中还有若干其他类型的段,例如:包含符号表的段、包含调试信息的段以及包含动态共享库连接表的段等。这些部分并不装载到进程执行的程序映像中。