APUE学习笔记(七)进程环境

7.1 main函数

c程序总是从main函数开始执行。在调用main前先调用一个特殊的启动例程,从内核取得命令行参数和环境变量值。

7.2 进程终止

正常终止

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) 最后一个线程对取消请求做出响应

7.3 命令行参数

当执行一个程序时,调用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);
}

7.4 环境表

每个程序都接收到一张环境表,环境表也是一个字符指针数组,其中每个指针包含一个以null结束的C字符串的地址。全局变量environ则包含了该指针数组的地址。

7.5 C程序的存储空间布局

c程序的组成:

  • 正文段。这是由CPU执行的机器指令部分,是可以共享的和只读的。
  • 初始化数据段,也叫数据段,包含了程序中需明确地赋初值的变量,指定了初值。
  • 未初始化数据段。在程序开始前内核将此段中的数据初始化为0或空指针。 未指定初值。
  • 栈。自动变量以及每次函数调用时所需保存的信息都存放在此段中。
  • 堆。通常在堆中进行动态存储分配。

栈地址从高到低,堆地址从低到高。未初始化数据段的内容并不存放在磁盘程序文件中。需要存放在磁盘程序文件中的段只有正文段和初始化数据段。

从高地址到低地址

命令行参数和环境变量
栈
|
堆
未初始化数据段(bss)  --由exec初始化为0
初始化数据段  --exec从程序文件读取
正文

size命令报告正文段、 数据段和bss段的长度。

7.6 共享库

共享库使得可执行文件中不再需要包含公用的库函数,而只需在所有进程都可引用的存储区中保存这种库例程的一个副本。

程序第一次执行或者第一次调用某个库函数时,用动态链接方法将程序与共享库函数相链接。 这减少了每个可执行文件的长度,但增加了一些运行时间开销。

7.7 存储空间分配

三种分配方式

  • malloc,分配指定字节数的存储区。 此存储区中的初始值不确定。
  • calloc,为指定数量指定长度的对象分配存储空间。 该空间中的每一位都初始化为0。
  • realloc,增加或减少以前分配区的长度。新增区域内的初始值则不确定。

这3个分配函数所返回的指针一定是适当对齐的,使其可用于任何数据对象。

7.8 环境变量

环境变量操作函数

#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);
}

7.9 栈帧跳转

goto语句是不能跨越函数的, 使用函数setjmp和longjmp能实现更深层次的跳转。

每个进程都有一组资源限制,其中一些可以用getrlimit和setrlimit函数查询和更改。

你可能感兴趣的:(学习记录)