c程序总是从main函数开始执行。在调用main前先调用一个特殊的启动例程,从内核取得命令行参数和环境变量值。
正常终止
(1) 从main返回;
(2) 调用exit;
(3) 调用_exit或_Exit;
(4) 最后一个线程从其启动例程返回
(5) 从最后一个线程调用pthread_exit
_exit和_Exit立即进入内核, exit则先执行一些清理处理,对于所有打开流调用fclose函数,然后返回内核。
返回值
退出函数都带一个整型参数,称为终止状态。
如果调用这些函数时不带终止状态,或main执行了一个无返回值的return语句,或main没有声明返回类型为整型,则该进程的终止状态是未定义的。
函数atexit
#include
int atexit(void (*func)(void));
exit调用这些函数的顺序与它们登记时候的顺序相反。 同一函数如若登记多次, 也会被调用多次。
#include "apue.h"
static void hello();
static void byebye();
int main(void) {
if (atexit(hello) != 0)
err_sys("atexit error");
if (atexit(hello) != 0)
err_sys("atexit error");
if (atexit(byebye) != 0)
err_sys("atexit error");
printf("main is done\n");
exit(0);
}
static void hello() {
printf("hello\n");
}
static void byebye() {
printf("byebye\n");
}
异常终止
(6) 调用abort
(7) 接到一个信号
(8) 最后一个线程对取消请求做出响应
当执行一个程序时,调用exec的进程可将命令行参数传递给该新程序。
#include
#include
int main(int argc, char* argv[]) {
for (int i = 0; i < argc; ++i) {
printf("%d, %s\n", i, argv[i]);
}
exit(0);
}
每个程序都接收到一张环境表,环境表也是一个字符指针数组,其中每个指针包含一个以null结束的C字符串的地址。全局变量environ则包含了该指针数组的地址。
c程序的组成:
栈地址从高到低,堆地址从低到高。未初始化数据段的内容并不存放在磁盘程序文件中。需要存放在磁盘程序文件中的段只有正文段和初始化数据段。
从高地址到低地址
命令行参数和环境变量
栈
|
堆
未初始化数据段(bss) --由exec初始化为0
初始化数据段 --exec从程序文件读取
正文
size命令报告正文段、 数据段和bss段的长度。
共享库使得可执行文件中不再需要包含公用的库函数,而只需在所有进程都可引用的存储区中保存这种库例程的一个副本。
程序第一次执行或者第一次调用某个库函数时,用动态链接方法将程序与共享库函数相链接。 这减少了每个可执行文件的长度,但增加了一些运行时间开销。
三种分配方式
这3个分配函数所返回的指针一定是适当对齐的,使其可用于任何数据对象。
环境变量操作函数
#include
char *getenv(const char *name);
int putenv(char *str);
int setenv(const char *name, const char *value, int rewrite);
int unsetenv(const char *name);
由于环境变量位于栈顶,所以在增加和修改环境变量时,可能需要再堆上重新分配空间。
#include
#include
#include "apue.h"
int main(int argc, char* argv[]) {
char *name = "HUAHUA";
char *value = getenv(name); //读取
printf("%s\n", value);
char *huahua = "HUAHUA=OK";
if (putenv(huahua) == -1)
err_sys("putenv error");
printf("%s\n", getenv("HUAHUA"));
if (setenv("HUAHUA", "OOKK", 0) == -1) //不覆盖
err_sys("setenv error");
printf("%s\n", getenv("HUAHUA"));
if (setenv("HUAHUA", "OOKK", 1) == -1) //覆盖
err_sys("setenv error");
printf("%s\n", getenv("HUAHUA"));
if (unsetenv("HUAHUA") == -1) //删除
err_sys("unset error");
printf("%s\n", getenv("HUAHUA"));
exit(0);
}
goto语句是不能跨越函数的, 使用函数setjmp和longjmp能实现更深层次的跳转。
每个进程都有一组资源限制,其中一些可以用getrlimit和setrlimit函数查询和更改。