1) gcc编译器调用ld链接器, 链接器一开始启动一个特殊的启动例程(此启动例程是程序的起始地址),启动立场从进程空间高地址处取得命令行参数和环境变量参数,然后调用main函数;
1) exit函数先执行一个标准的IO库的清理关闭操作:调用fclose函数关闭所有的流,使得所有的缓冲数据被冲洗;然后调用_exit或是_Exit;
2) atexit函数, 用来等级终止处理程序,经过登记的函数,在进程退出的时候,会被逆序调用;
void atexit(void (*func)(void));
3) 内核使程序执行的唯一方法是调用exec函数,进程退出的唯一方法是调用_exit, _Exit ;
环境表和命令行参数表都是字符数组指针, 每个指针指向一个字符串,该字符串以null结尾
char ** argv[];
char ** environ[];
1) 程序空间分布
正文段:共享、只读;
初始化数据: 函数外定义的,局部静态变量, 有初始值;
非初始化数据段:无初始值
栈:局部变量、调用函数地址、调用者的环境信息(某些寄存器的值);
栈内部分为栈帧,注意递归函数没调用一次自身,就要使用一个新的栈帧;栈帧的存在,使得一个函数调用中的变量实例不会影响另外一个函数实例中的变量;
堆:动态存储分配的空间;
2) 程序正文和初始化的数据段存放在磁盘上,由exec程序读入;未初始化的数据.bss段由exec分配并设定为0;(需要存放在磁盘上的文件只有正文段和初始化数据段);
1) 可执行程序不用包含公用的库例程, 只需要在所有进程可以引用的存储区维护一个库例程的副本;
2)共享库的优点是更新库例程的时候只需要重新链接,不需要重新编译用户程序,前提是库例程的函数接口不变;
gcc -static hello.c
gcc hello.c
1) malloc(size):存储器的初始值不确定
2) calloc(nobj, size) :存储区的初始值为0;
3) realloc(ptr, newsize):存储区的初始值不确定,存储器实际内存地址可变,不能让任何指针指向该存储区;
三个函数返回的类型都是void *, 并且是适当对齐的,这样就可以用于任何数据类型;
4) void *数据类型可以自动转化为其他不同类型的指针;
5) free释放空间,但实际上只是将存储空间送到可用存储区,即malloc池而不是返回内核;
内存分配函数实际上调用的是sbrk系统调用。sbrk系统调用扩充、缩小进程的存储空间;
6)分配的存储空间,还附加有存储空间管理记录区,在存储空间头部;
7) 忘记调用free函数,进程的存储空间一直增长,导致过度的分页开销,使性能下降;
1) 环境变量存放在environ中,不能直接访问environ,而是应该调用函数getenv
char *getenv(const char *name);
int putenv(char *str); //新增环境变量
int setenv(const char *name, const char *value, int rewrite);
int unsetenv(const char *name); //删除环境变量
2) 环境变量存放在进程空间地址的头部, 向上和向下都不能增长;因此对于putenv函数新增环境变量,具体的底层实现会很复杂;
1) goto函数是在一个函数内部进行跳转, setjmp 和longjmp是非本地跳转,在调用函数中跳转(每一个调用函数都创建一个栈帧,在栈帧间跳转);
2)需要用一个全局变量jmp_buf jmpbuffer;(主函数和子函数都要访问,所以是全局变量;jmpbuffer是某种形式的数组,其中存放调用longjmp时用来恢复栈状态的所有信息;
3) 使用非本体跳转后, 自动变量和存储器状态是不确定的;即,子函数调用longjmp后返回主函数,你不能确定此时程序的自动变量和环境变量是子函数时候的值,还是主函数时候的值;如果你想保持子函数中的自动变量不变,就将自动变量声明为volatile;
4) 在调用longjmp之后, 存放在存储器中的变量将具有longjmp时候的值, 而在CPU和浮点寄存器中的变量将恢复到setjmp时候的值;
5) 要编写一个使用非局部跳转的可移植程序,必须使用volatile属性的变量;
1)每个进程都有一组资源限制,进程资源限制通常是由进程0创建,由后面的进程继承;
struct rlimit{
rlim_t rlim_cur; //软限制值
rlim_t rlim_max; //硬限制值
};
2)更改资源限制的三个规则