进程环境

来源:http://www.aka.org.cn/Lectures/002/Lecture-2.1.6/Lecture-2.1.6/proc.html

6.1  进程及进程环境

  • 基本概念

  • main 函数

  • 命令行参数及 popt 库

  • 环境变量

  • C 程序的内存布局

  • 共享库

  • 内存分配

  • 程序的长跳转

  • 进程的资源限制

6.1.1  基本概念

  • 程序是保存在磁盘上的可执行代码和数据的映像.
  • 进程是正在运行的程序. 一个程序可以同时有多个进程实例, 它们互相没有关系.

6.1.2  main 函数

  • main 函数 C/C++ 程序的入口函数, 但并不是进程真正开始执行的地方.
  • main 函数的参数包括命令行参数个数以及参数数组.
    ===============================================================================
            int main (int argc, char *argv[]);
    ===============================================================================
    
  • 进程的终止有两种类型, 一种是正常终止, 一种是非正常终止.
  • 正常终止包括: 从 main 函数返回, 调用 exit 或 _exit.
    ===============================================================================
           #include <stdlib.h>
    
           void exit (int status);
    
           #include <unistd.h>
    
           void _exit (int status);
    ===============================================================================
    
  • 非正常终止包括: 调用 abort, 由信号终止.
  • exit 和 _exit 的区别在于, exit 要完成一些清除工作, 而 _exit 立即返回内核.
  • exit 要完成的清除工作包括标准 I/O 库的缓冲区刷新, 调用已注册的 atexit 函数等.
  • 通过 atexit 最多可注册 32 个由 exit 函数调用的进程终止清除函数.
    ===============================================================================
           #include <stdlib.h>
    
           int atexit (void (*function) (void));
    ===============================================================================
    

6.1.3  命令行参数及 popt 库

  • main 函数的 argc 是命令行参数个数, 其中包括程序名称.
  • argv [argc] 包含 NULL 指针.
  • popt 库是一个用来分析 GNU 风格命令行参数的函数库.
  • GNU 风格的命令行, 典型的有 rpm 命令的命令行.
    ===============================================================================
    GNU 风格的命令行参数特点:
    
        * 长参数名, 比如, --binary, 但对应有短参数, 比如, -b
    ===============================================================================
    
    ===============================================================================
           #include <popt.h>
    
           poptContext poptGetContext (char * name, int argc, char ** argv,
                                      struct poptOption * options, int flags);
           void poptFreeContext (poptContext con);
           void poptResetContext (poptContext con);
           int poptGetNextOpt (poptContext con);
           char * poptGetOptArg (poptContext con);
           char * poptGetArg (poptContext con);
    
           ......
    -------------------------------------------------------------------------------
    EXAMPLE:
            /* This example comes from popt(3) man page. */
            #include <popt.h>
            #include <stdio.h>
    
            void usage (poptContext optCon, int exitcode, char *error, char *addl)
            {
               poptPrintUsage (optCon, stderr, 0);
               
               if (error) fprintf (stderr, "%s: %s", error, addl);
               
               exit (exitcode);
            }
    
            int main (int argc, char *argv[]) {
              char    c;            /* used for argument parsing */
              int     i = 0;        /* used for tracking options */
              char    *portname;
              int     speed = 0;    /* used in argument parsing to set speed */
              int     raw = 0;      /* raw mode? */
              int     j;
              char    buf[BUFSIZ+1];
              poptContext optCon;   /* context for parsing command-line options */
    
              struct poptOption optionsTable[] = {
                              { "bps", 'b', POPT_ARG_INT, &speed, 0,
                                "signaling rate in bits-per-second", "BPS" },
                              { "crnl", 'c', 0, 0, 'c',
                                "expand cr characters to cr/lf sequences" },
                              { "hwflow", 'h', 0, 0, 'h',
                                "use hardware (RTS/CTS) flow control" },
                              { "noflow", 'n', 0, 0, 'n',
                                "use no flow control" },
                              { "raw", 'r', 0, &raw, 0,
                                "don't perform any character conversions" },
                              { "swflow", 's', 0, 0, 's',
                                "use software (XON/XOF) flow control" } ,
                              POPT_AUTOHELP
                              { NULL, 0, 0, NULL, 0 }
              };
    
              optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
              poptSetOtherOptionHelp(optCon, "[OPTIONS]* <port>");
    
              if (argc < 2) {
                    poptPrintUsage(optCon, stderr, 0);
                    exit(1);
              }
    
              /* Now do options processing, get portname */
              while ((c = poptGetNextOpt(optCon)) >= 0) {
                 switch (c) {
                    case 'c':
                       buf[i++] = 'c';
                       break;
                    case 'h':
                       buf[i++] = 'h';
                       break;
                    case 's':
                       buf[i++] = 's';
                       break;
                    case 'n':
                       buf[i++] = 'n';
                       break;
                 }
              }
              portname = poptGetArg (optCon);
              if((portname == NULL) || !(poptPeekArg (optCon) == NULL))
                 usage (optCon, 1, "Specify a single port", ".e.g., /dev/cua0");
    
              if (c < -1) {
                 /* an error occurred during option processing */
                 fprintf(stderr, "%s: %s\n",
                         poptBadOption (optCon, POPT_BADOPTION_NOALIAS),
                         poptStrerror (c));
                 return 1;
              }
    
              /* Print out options, portname chosen */
              printf ("Options  chosen: ");
              for (j = 0; j < i ; j++)
                 printf ("-%c ", buf[j]);
              if (raw) printf("-r ");
              if (speed) printf("-b %d ", speed);
              printf ("\nPortname chosen: %s\n", portname);
    
              poptFreeContext (optCon);
              exit (0);
            }
    ---------------------------------------
    $ ./popt --usage
    Usage: popt [-chnrs?] [-b BPS] [--usage] [OPTIONS]* <port>
    
    $ ./popt -c -b 9600 /dev/cua0
    Options  chosen: -c -b 9600
    Portname chosen: /dev/cua0
    ===============================================================================
    

6.1.4  环境变量

  • 环境变量以字符串数组的形式给出.
  • 该字符串数组的头指针定义为: extern char **environ.
  • getenv/setenv, setenv/unsetenv
    ===============================================================================
           #include <stdlib.h>
    
           char *getenv (const char *name);
           int putenv (const char *string);
    
           int setenv (const char *name, const char *value, int overwrite);
           void unsetenv (const char *name);
    ===============================================================================
    

6.1.5  C 程序的内存布局

  • 内存布局见图 6.1.
    ===============================================================================
    
                  -----------------
      高地址      |               | ---> 命令行参数和环境变量 (只读)
                  -----------------
                  |      栈       |
                  |- - - - - - - -|
                  |      | |      |
                  |      \ /      |
                  |               |
                  |               |
                  |      / \      |
                  |      | |      |
                  |- - - - - - - -|
                  |      堆       |
                  |---------------|
                  | 未初始化数据  |
                  |     (bss)     | ---> 由 exec 初始化为零
                  |---------------|
                  | 初始化后数据  |  \
                  |---------------|   |
                  |     text      |   | 由 exec 从程序中读取
      低地址      |               |  /
                  |---------------|
    
    ===============================================================================
    
        
    • 如何修改环境变量?

6.1.6  共享库

1. ldd 工具

ldd 用来显示执行文件需要哪些共享库, 共享库装载管理器在哪里找到了需要的共享库.

2. soname

共享库的一个非常重要的,也是非常难的概念是 soname——简写共享目标名(short for shared object name)。这是一个为共享库(.so)文件而内嵌在控制数据中的名字。如前面提到的,每一个程序都有一个需要使用的库的清单。这个清单的内容是一系列库的 soname,如同 ldd 显示的那样,共享库装载器必须找到这个清单。

soname 的关键功能是它提供了兼容性的标准。当要升级系统中的一个库时,并且新库的 soname 和老的库的 soname 一样,用旧库连接生成的程序,使用新的库依然能正常运行。这个特性使得在 Linux 下,升级使用共享库的程序和定位错误变得十分容易。

在 Linux 中,应用程序通过使用 soname,来指定所希望库的版本。库作者也可以通过保留或者改变 soname 来声明,哪些版本是相互兼容的,这使得程序员摆脱了共享库版本冲突问题的困扰。

查看/usr/local/lib 目录,分析 MiniGUI 的共享库文件之间的关系

3. 共享库装载器

当程序被调用的时候,Linux 共享库装载器(也被称为动态连接器)也自动被调用。它的作用是保证程序所需要的所有适当版本的库都被调入内存。共享库装载器名字是 ld.so 或者是 ld-linux.so,这取决于 Linux libc 的版本,它必须使用一点外部交互,才能完成自己的工作。然而它接受在环境变量和配置文件中的配置信息。

文件 /etc/ld.so.conf 定义了标准系统库的路径。共享库装载器把它作为搜索路径。为了改变这个设置,必须以 root 身份运行 ldconfig 工具。这将更新 /etc/ls.so.cache 文件,这个文件其实是装载器内部使用的文件之一。

可以使用许多环境变量控制共享库装载器的操作(表1-4+)。

                        表 1-4+ 共享库装载器环境变量
变量                       含义
LD_AOUT_LIBRARY_PATH       除了不使用 a.out 二进制格式外,与 LD_LIBRARY_PATH 相同。
LD_AOUT_PRELOAD            除了不使用 a.out 二进制格式外,与 LD_PRELOAD 相同。
LD_KEEPDIR                 只适用于 a.out 库;忽略由它们指定的目录。
LD_LIBRARY_PATH            将其他目录加入库搜索路径。它的内容应该是由冒号
                           分隔的目录列表,与可执行文件的 PATH 变量具有相同的格式。
                           如果调用设置用户 ID 或者进程 ID 的程序,该变量被忽略。
LD_NOWARN                  只适用于 a.out 库;当改变版本号是,发出警告信息。
LD_PRELOAD                 首先装入用户定义的库,使得它们有机会覆盖或者重新定义标准库。
                           使用空格分开多个入口。对于设置用户 ID 或者进程 ID 的程序,
                           只有被标记过的库才被首先装入。在 /etc/ld.so.perload 中指定
                           了全局版本号,该文件不遵守这个限制。
4. 使用 dlopen

另外一个强大的库函数是 dlopen()。该函数将打开一个新库,并把它装入内存。该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。比如 Apache Web 服务器利用这个函数在运行过程中加载模块,这为它提供了额外的能力。一个配置文件控制了加载模块的过程。这种机制使得在系统中添加或者删除一个模块时,都不需要重新编译了。

可以在自己的程序中使用 dlopen()。dlopen() 在 dlfcn.h 中定义,并在 dl 库中实现。它需要两个参数:一个文件名和一个标志。文件名可以是我们学习过的库中的 soname。标志指明是否立刻计算库的依赖性。如果设置为 RTLD_NOW 的话,则立刻计算;如果设置的是 RTLD_LAZY,则在需要的时候才计算。另外,可以指定 RTLD_GLOBAL,它使得那些在以后才加载的库可以获得其中的符号。

当库被装入后,可以把 dlopen() 返回的句柄作为给 dlsym() 的第一个参数,以获得符号在库中的地址。使用这个地址,就可以获得库中特定函数的指针,并且调用装载库中的相应函数。

6.1.7  内存分配

  • malloc/calloc/realloc/free
    ===============================================================================
           #include <stdlib.h>
    
           void *calloc (size_t nmemb, size_t size);
           void *malloc (size_t size);
           void free (void *ptr);
           void *realloc (void *ptr, size_t size);
    -------------------------------------------------------------------------------
        * calloc 分配后的数据被设置为 0.
        * malloc 分配的数据不清除.
        * free (NULL) 是合法的.
        * realloc 新分配的区域不经过初始化.
    ===============================================================================
    
  • alloca: 在栈中分配
    ===============================================================================
           #include <stdlib.h>
    
           void *alloca( size_t size);
    -------------------------------------------------------------------------------
        * alloca 在栈中分配, 不需要释放, 函数返回时自动释放.
        * 在某些系统的线程库实现中, 要注意每个线程的栈大小有限制.
    ===============================================================================
    

6.1.8  程序的长跳转

  • setjmp/longjmp: 在函数间跳转
    ===============================================================================
           #include <setjmp.h>
    
           int setjmp (jmp_buf env);
           void longjmp (jmp_buf env, int val);
    -------------------------------------------------------------------------------
    EXAMPLE:
    
          /* C 程序的长跳转 */
            #include <stjmp.h>
    
            jmp_buf   jmpbuffer;
    
            int main (void)
            {
    
                char line [MAXLINE];
    
                switch (setjmp (jmpbuffer)) {
                    case 0:
                        break;
                    case 1:
                        break;
                    default:
                        break;
                }
    
                return 0;
            }
    
            void some_func (void)
            {
                if (...)
                    longjmp (jmpbuffer, 0);
                else if (...)
                    longjmp (jmpbuffer, 1);
                else
                    longjmp (jmpbuffer, 2);
            }
    -------------------------------------------------------------------------------
       * jmpbuffer 必须是全局变量.
       * 注意在长跳转返回之后, 对不同类型的 (自动, 寄存器, 非易失) 变量处理不同.
         非易失变量不会发生改变, 但自动变量和寄存器变量可能要恢复为调用 setjmp 之前
         的值.
    ===============================================================================
    

6.1.9  进程的资源限制

  • getrlimit/setrlimit, getusage
    ===============================================================================
           #include <sys/time.h>
           #include <sys/resource.h>
           #include <unistd.h>
    
           int getrlimit (int resource, struct rlimit *rlim);
           int getrusage (int who, struct rusage *usage);
           int setrlimit (int resource, const struct rlimit *rlim);
    -------------------------------------------------------------------------------
                struct rlimit
                {
                     int  rlim_cur;
                     int  rlim_max;
                };
    
                struct rusage
                {
                     struct timeval ru_utime; /* user time used */
                     struct timeval ru_stime; /* system time used */
                     long ru_maxrss;          /* maximum resident set size */
                     long ru_ixrss;      /* integral shared memory size */
                     long ru_idrss;      /* integral unshared data size */
                     long ru_isrss;      /* integral unshared stack size */
                     long ru_minflt;          /* page reclaims */
                     long ru_majflt;          /* page faults */
                     long ru_nswap;      /* swaps */
                     long ru_inblock;         /* block input operations */
                     long ru_oublock;         /* block output operations */
                     long ru_msgsnd;          /* messages sent */
                     long ru_msgrcv;          /* messages received */
                     long ru_nsignals;        /* signals received */
                     long ru_nvcsw;      /* voluntary context switches */
                     long ru_nivcsw;          /* involuntary context switches */
                };
    ===============================================================================
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值