zoukankan      html  css  js  c++  java
  • [APUE]UNIX进程的环境(上)

    一、 前言

      本章将学习:当执行程序时,其main函数是如何被调用的,命令行参数是如何传送给执行程序的,典型的存储器布局是什么样式,如何分配另外的存储空间,进程如何使用环境变量,进程终止的不同方式等。另外还将说明longjmp和setjmp函数以及它们与栈的交互作用。

    二、 main函数

      C程序的入口是main函数,main函数的原型是:

    int main(int argc, char argv[]);

      当内核启动C程序时(使用一个exec函数),在调用main前先调用一个特殊的启动例程。可执行程序文件将此启动例程制定为程序的起始地址--这是由链接器设置的,而链接器程序则由C编译程序(通常是cc)调用。启动例程从内核取得命令行参数和环境变量值,然后为调用main函数作好安排。

    三、进程终止

      有五种方式使进程终止:

    • 正常终止: (a) 从main返回。
      (b) 调用exit。
      (c) 调用_exit。

    • 异常终止: (a) 调用abort。
      (b) 由一个信号终止。

      上节提到的启动例程是这样编写的,使得从main返回后立即调用exit函数,如果将启动历程以C代码形式表示(此例程通常用汇编编写),则它调用main函数的形式可能是: exit(main(argc, argv));

    1. exit和_exit函数

      exit和_exit函数用于正常终止一个程序:_exit立即进入内核,exit则先执行一些清除处理(包括调用执行各终止处理程序,关闭所有标准IO流等),然后进入内核。

    #include <stdlib.h>
    void exit(int status);
    
    #include <unistd.h>
    void _exit(int statu);

    使用不同头文件的原因是:exit是由ANSI C说明的,而_exit则是由POSIX.1说明的

      由于历史原因,exit函数总是执行一个标准IO库的清除关闭操作:对所有打开的流调用fclose函数,这会造成缓存中的所有数据都被刷新(写入到文件上)。

    2. atexit函数

      按照ANSI C的规定,一个进程可以登记多至32个的终止处理函数(exit handler),这些函数将由exit自动调用。可用atexit函数来注册这些函数。

    #include <stdlib.h>
    int atexit(void (*func)(void));
    返回值:成功为0,出错非0

      atexit的参数是一个无参数并且无返回的函数的指针。exit以注册这些函数的相反顺序调用它们。如果一个函数被注册多次那会被调用多次。   根据ANSI C和POSIX.1 exit首先调用各终止处理程序,然后按需多次调用fclose。下图显示了一个C程序是如何启动的,以及它终止的各种方式。

       

    内核使程序执行的唯一方式是调用一个exec函数。

    四、命令行参数

      当执行一个程序时,调用exec的进程可将命令行参数传给该程序。

    五、环境表

      每个程序都接收到一张环境表。与参数表一样,环境表也是一个字符指针数组,其中每个指针包含一个以null结束的字符串的地址。全局变量environ则包含了该指针数组的地址。 extern char **environ;   如果该环境包含五个字符串,那么它们看起来如下图:

     

    其中每个字符串的结束处都有一个null字符。我们称environ为环境指针。指针数组为环境表,其中各指针指向的字符串为环境字符串。通常使用getenv和putenv函数来存取特定的环境变量,而不是用environ变量。但是如果要查看整个环境则必须使用environ指针。

    六、C程序的存储空间布局

      C程序由以下几部分组成:

    • 正文段。这是由CPU执行的机器指令部分。通常正文段是共享的,所以即使是经常执行的程序(如文本编辑程序、C编译程序、shell等)在存储器中也只需有一个副本。另外正文段常常是只读的。
    • 初始化数据段。通常将此段称为数据段。其中包含了程序中需赋初值的变量。如C程序中任何函数之外的说明:
      int maxcount = 99;
      使此变量以初值存放在初始化数据段中。
    • 非初始化数据段。通常称此段为bss段(block started by symbol, 由符号开始的块),在程序开始执行之前,内核将此段初始化为0。函数外的说明: long sum[1000] 使此变量存放在非初始化数据段中。
    • 栈。自动变量以及每次函数调用所需保存的信息(返回地址和调用者的环境信息)都存放在此段中。
    • 堆。通常在堆中进行动态存储分配。堆位于非初始化数据段项和栈底之间。
      下图显示了这些段的典型安排方式。

      从图中可以看到未初始化数据段的内容并不存放在磁盘程序文件中。需要存放在磁盘程序文件中的段只有正文段和初始化数据段。size命令报告正文段、数据段、和bss段的长度:
    $ size /bin/cc /bin/sh
    text  data  bss dec    hex
    81920 16384 664 98968  18298 /bin/cc
    90112 16384 0   106496 1a000 /bin/sh

      第4列和第5列分别以十进制和十六进制表示的总长度。

  • 相关阅读:
    进程、线程和协程的区别(转)
    IO多路复用机制(转)
    防火墙及其功能(转)
    TCP连接的建立和终止。
    TCP和UDP细致刻画,区别。
    typename T和class T区别与联系
    TCP UDP的详解开始 ----UNIX网络编程
    关于UNIX网络编程的的OSI,1.7章的总结
    UNIX网络编程daytime服务端和客户端的实现过程
    linux shell脚本执行错误:bad substitution
  • 原文地址:https://www.cnblogs.com/orlion/p/6242758.html
Copyright © 2011-2022 走看看