首页 > 系统 > Unix > 正文

《Unix环境高级编程》读书笔记 第7章-进程环境

2024-06-28 13:24:48
字体:
来源:转载
供稿:网友
《Unix环境高级编程》读书笔记 第7章-进程环境1. main函数
  • int main( int argc, char *argv[] );
  • argc是命令行参数的数目,包括程序名在内
  • argv是指向参数的各个指针所构成的数组,即指针数组
  • 当内核执行C程序时(使用exec函数),在调用main前先调用一个特殊的启动例程。可执行程序文件将此启动例程指定为程序的起始地址——这是由连接器设置的,而连接器则是由C编译器调用。启动例程从内核取得命令行参数和环境变量值,然后按上述方式调用main函数做好安排。
2. 进程终止
  • 有8种方式使进程终止,其中5种为正常终止
    1. 从main返回
    2. 调用exit
    3. 调用_exit或_Exit
    4. 最后一个线程从其启动例程返回
    5. 从最后一个线程调用pthread_exit
  • 异常终止有3种方式:
    1. 调用abort
    2. 接到一个信号
    3. 最后一个线程对取消请求做出响应
2.1 退出函数
  1. #include <stdlib.h>
  2. void exit(int status);
  3. void _Exit(int status);
  4. #include <unistd.h>
  5. void _exit(int status);
  • _exit(由POSIX.1说明)和_Exit(由ISO C说明)立即进入内核,而exit(由ISO C说明)则先执行一些清理处理,然后返回内核。
  • 3个退出函数都带一个整型参数,称为终止状态退出状态。大多数Unix系统shell都提供检测进程终止状态的方法。
  • 下面几种情况下,该进程的终止状态是未定义的:
    1. 调用这些函数时不带终止状态
    2. main执行了一个无返回值的return语句
    3. main没有声明返回类型为整型
2.2 函数atexit
  1. #include <stdlib.h>
  2. int atexit(void (*func)(void)); // 参数是一个函数指针
  3. Returns: 0 if OK, nonzero on error
  • 按照ISO C的规定,一个进程可以登记多至32个函数,这些函数将由exit自动调用。称这些函数为终止处理程序
  • exit首先调用各终止处理程序,然后关闭所有打开流。exit调用这些函数的顺序与它们登记的顺序相反。
  • 如若程序调用exec函数,则将清除所有已安装的终止处理程序。
  • 注意:内核使程序执行的唯一方法是调用一个exec函数。进程自愿终止的唯一方法是显式或隐式(通过调用exit)地调用_exit或_Exit。进程也可非自愿地由一个信号使其终止。

  • 一个C程序的启动,以及它的各种终止方式

3. 命令行参数
  • 当执行一个程序时,调用exec的进程可将命令行参数传递给新程序。这是Unix shell的一部分常规操作。
4. 环境表
  • extern char **environ;称environ为环境指针;指针数组为环境表,其中各指针指向的字符串为环境字符串
5. C 程序的存储空间布局
  • 历史沿袭至今,C程序一直由以下几部分组成:

    1. 正文段。这是由CPU执行的机器指令部分。通常,正文段是可由多个应用程序共享的,在存储器中只需一个副本。正文段常常是只读的。
    2. 初始化数据段,也称为数据段。包含了程序中需明确地赋初值的变量。
    3. 未初始化数据段,也称为bss段(block started by symbol,由符号开始的块)。在程序开始执行之前,内核将此段中的数据初始化为0或空指针。
    4. 栈。自动变量以及每次函数调用时所需保存的信息都存放在此段中。
    5. 堆。通常在堆中进行动态存储分配。惯例,堆位于未初始化数据段和栈之间。
  • 一种典型安排方式,这是程序的逻辑布局

  • size命令报告正文段、数据段和bss段的长度(以字节为单位)

    $ size /usr/bin/cc /bin/shtext data bss dec hex filename346919 3576 6680 357175 57337 /usr/bin/cc102134 1776 11272 115182 1c1ee /bin/sh第4、5列分别是以十进制和十六进制表示的3段总长度

6. 共享库
  • 共享库使得可执行文件中不再需要包含公用的库函数,而只需要在所有进程都可以引用的存储区中保存这种库例程的一个副本。减少了每个可执行文件的长度,但增加了一些运行时间开销。这种时间开销发生在该程序第一次被执行时,或者每个共享函数库函数第一次被调用时。
  • 另一个优点是可以用库函数的新版本代替老版本而无需对使用该库的程序重新连接编译。
7. 存储空间分配
  • ISO C说明了3个用于存储空间动态分配的函数

    malloc,初始值不确定calloc,每一位都初始化为0realloc,新增区域内的初始值不确定

  1. #include <stdlib.h>
  2. void *malloc(size_t size);
  3. void *calloc(size_t nobj, size_t size);
  4. void *realloc(void *ptr, size_t newsize);
  5. All three return: non-null pointer if OK, NULL on error
  6. void free(void *ptr);
  • 这3个分配函数所返回的指针一定是适当对齐的,满足最苛刻的对齐要求,使其可用于任何数据对象。
  • 返回通用指针void *
  • 这些分配例程通常用sbrk系统调用实现。该系统调用扩充或缩小进程的堆。虽然sbrk可以扩充或缩小进程的存储空间,但是大多数malloc和free的实现都不减少进程的存储空间。释放的空间可供以后再分配,将它们保持在malloc池中而不返回给内核。
  • 大多数实现所分配的存储空间比所要求的要稍大一些,额外的空间用来记录管理信息——分配块的长度、指向下一个分配块的指针等。
  • 常见错误:1. 释放一个已经释放了的块;2. 调用free时所用的指针不是3个alloc函数的返回值;3. 忘记调用free造成内存泄露。
8. 替代的存储空间分配程序
  • libmalloc
  • vmalloc
  • quick-fit,历史上所使用的标准malloc算法是最佳适配或首次适配存储分配策略。quick-fit算法比上述两种快,但可能使用较多存储空间
  • jemalloc
  • TCMalloc,高性能、高扩展性和高存储效率。开源,是Google-perftools工具中的一个
  • 函数alloca,在当前函数的栈帧上分配存储空间,而不是在堆上。

    优点:当函数返回时,自动释放它所使用的栈帧,不再需要自己释放空间缺点:alloca函数增加了栈帧的长度,而某些系统在函数已经被调用后不能增加栈帧长度,于是也就不能支持alloca函数。本书4个平台均支持该函数。

9. 环境变量
  • Unix内核并不查看这些字符串,它们的解释完全取决于各个应用程序。
  • LANG、PATH、HOME、PWD、TERM ...
  1. #include <stdlib.h>
  2. char *getenv(const char *name);
  3. Returns: pointer to value associated with name, NULL if not found
  1. #include <stdlib.h>
  2. int putenv(char *str); // name已存在,替换
  3. Returns: 0 if OK, nonzero on error
  4. int setenv(const char *name, const char *value, int rewrite); // 当name已存在时,根据rewrite参数而覆盖或保持原值
  5. int unsetenv(const char *name); // 即使name不存在也不算出错
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表