美文网首页
从零开始UNIX环境高级编程(7):进程环境

从零开始UNIX环境高级编程(7):进程环境

作者: 伤口不该结疤 | 来源:发表于2017-04-24 20:56 被阅读198次

    0. 引言

    在学习进程控制前,先了解进程运行的环境,如:main函数是如何被调用的;命令行参数是如何传递给新程序的;程序在内存空间中是什么样的结构;进程的终止方式等。

    进程环境

    1. main函数

    1.1 程序执行的入口函数

    虽然C程序总是从main函数开始执行,但是内核使用exec函数执行C程序时,在调用main前会先调用一个启动例程,这个启动例程就是_start。以下面这段代码为例,通过gcc test.c -o test编译生成可执行文件test

    /*test.c*/
    int main()
    {
        return 0;
    }
    

    使用objdump对test进行分析,得到反汇编结果如下,可以看出main函数地址为0x4004b4,在_start有去调用到mov $0x4004b4,%rdi。这个示例只是为了证明main函数是由启动例程_start调用的,此处不过多的去探究反汇编得到的这些符号的含义,有兴趣的同学可以参考 程序员的自我修养:链接、装载与库第11章的第1节 入口函数和程序初始化

    ckt@ubuntu:~/work/unix/code/chapter7$ objdump -S -d signal.o
    ... ...
    
    00000000004003d0 <_start>:
      4003d0:   31 ed                   xor    %ebp,%ebp
      4003d2:   49 89 d1                mov    %rdx,%r9
      4003d5:   5e                      pop    %rsi
      4003d6:   48 89 e2                mov    %rsp,%rdx
      4003d9:   48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
      4003dd:   50                      push   %rax
      4003de:   54                      push   %rsp
      4003df:   49 c7 c0 50 05 40 00    mov    $0x400550,%r8
      4003e6:   48 c7 c1 c0 04 40 00    mov    $0x4004c0,%rcx
      4003ed:   48 c7 c7 b4 04 40 00    mov    $0x4004b4,%rdi
      4003f4:   e8 c7 ff ff ff          callq  4003c0 <__libc_start_main@plt>
      4003f9:   f4                      hlt    
      4003fa:   90                      nop
      4003fb:   90                      nop
     
      ... ...
    
      00000000004004b4 <main>:
      4004b4:   55                      push   %rbp
      4004b5:   48 89 e5                mov    %rsp,%rbp
      4004b8:   b8 00 00 00 00          mov    $0x0,%eax
      4004bd:   5d                      pop    %rbp
      4004be:   c3                      retq   
      4004bf:   90                      nop
    ... ...
    

    1.2 命令行参数

    接着来看main函数的参数,main函数原型如下

    int main(int argc, char const *argv[])
    

    通过命令行运行一个C程序时,我们可以通过命令行传入参数。main函数中的参数argc表示参数个数,argv中存放的是参数值。

    • 示例
      使用如下代码打印出从命令行传入的参数
    #include "apue.h"
    
    int main(int argc, char const *argv[])
    {
        int i = 0;
        for (i = 0; i < argc; i++)
            printf("%s\n", argv[i]);
        return 0;
    }
    
    • 运行结果
    ckt@ubuntu:~/work/unix/code/chapter7$ cc get_arg.c -o get_arg
    ckt@ubuntu:~/work/unix/code/chapter7$ ./get_arg
    ./get_arg
    ckt@ubuntu:~/work/unix/code/chapter7$ ./get_arg test1 test2 test3 
    ./get_arg
    test1
    test2
    test3
    

    1.3 环境表

    大多数UNIX系统支持main函数带3个参数,其中第3个参数envp就是环境变量表的地址,和argv一样,evnp也是一个数组

    int main(int argc, char const *argv[], char *envp[])
    
    envp示意图
    • 示例
      打印出当前程序的环境变量表
    #include "apue.h"
    
    void printinfo(char * info)
    {
        int i = 0;
        while (info[i] != '\0')
        {
            printf("%c", info[i]);
            i++;
        }
        printf("  ");
    }
    
    int main(int argc, char const *argv[], char *envp[])
    {
        while (*envp != NULL)
        {
            printinfo(*envp);
            envp++;
        }
    
        printf("\n");
    
        return 0;
    }
    
    • 运行结果
      由于打印的环境变量值太多,省略部分打印结果。
    ckt@ubuntu:~/work/unix/code/chapter7$ cc get_environ.c -o get_environ
    ckt@ubuntu:~/work/unix/code/chapter7$ ./get_environ
    SSH_AGENT_PID=2030  SHELL=/bin/bash  OLDPWD=/home/ckt/work/unix/code/chapter10  
    LANG=en_US.UTF-8 HOME=/home/ckt _=./get_environ  
    

    2. C程序的存储空间布局

    C程序通常由5个部分组成:

    • 正文段(.text):存放CPU的机器指令
    • 初始化数据段(.data):存放全局静态变量和局部静态变量
    • 未初始化数据段(.bss):存未初始化的全局变量和局部静态变量
    • 栈(stack):存放局部变量和函数调用时所需保存的信息
    • 堆(heap):动态分配的变量
    典型的存储空间安排

    我们同样可以通过objdump去分析各个段,如果想要了解更多细节,请参考使用readelf和objdump解析目标文件

    程序与目标文件对应关系

    3. 共享库

    共享库使得可执行文件中不再需要包含公用的库函数,只需要在进程可引用的储存区保存这个库例程的一个副本。共享库有以下两个优点:
    1. 减少了可执行文件的长度
    2. 可以用库函数的新版本代替老版本,而无需对使用该库的程序重新连接编辑

    • 示例

    先用无共享方式创建可执行文件a.out,并使用size命令查看正文段和数据段的长度

    ckt@ubuntu:~/work/unix/code/chapter7$ gcc -static test.c 
    ckt@ubuntu:~/work/unix/code/chapter7$ ls -l a.out 
    -rwxrwxr-x 1 ckt ckt 883375 Apr 24 04:30 a.out
    ckt@ubuntu:~/work/unix/code/chapter7$ size a.out 
       text    data     bss     dec     hex filename
     790568    6128   11272  807968   c5420 a.out
    

    再使用共享库编译此程序,得到的可执行文件的正文和数据段长度明显减小

    ckt@ubuntu:~/work/unix/code/chapter7$ gcc test.c
    ckt@ubuntu:~/work/unix/code/chapter7$ ls -l a.out 
    -rwxrwxr-x 1 ckt ckt 8326 Apr 24 04:30 a.out
    ckt@ubuntu:~/work/unix/code/chapter7$ size a.out 
       text    data     bss     dec     hex filename
       1076     496      16    1588     634 a.out
    

    4. 动态存储空间分配

    存储空间分配

    5. 环境变量

    当我们登录Linux之后,使用shell来跟系统进行通信,在进入shell之前,系统需要一些变量来提供它的数据访问(如是否显示彩色、当前路径和主文件夹的路径等),这些变量就是环境变量。为了区别于自定义变量,环境变量通常用大写字符表示。

    5.1 终端中获取和设置环境变量

    使用env命令可以列出当前shell环境下的所有环境变量

    ckt@ubuntu:~/work/unix/code/chapter7$ env
    SSH_AGENT_PID=2030
    GPG_AGENT_INFO=/tmp/keyring-NWSXuo/gpg:0:1
    TERM=xterm
    SHELL=/bin/bash
    XDG_SESSION_COOKIE=b2968a33ddb5564574638ff00000000f-1492995827.229855-1165390831
    WINDOWID=71303173
    OLDPWD=/home/ckt/work/unix/code/chapter10
    GNOME_KEYRING_CONTROL=/tmp/keyring-NWSXuo
    USER=ckt
    ... ...
    

    使用echo $环境变量名命令可以查看单个环境变量,例如,当前路径保存在环境变量PWD中

    ckt@ubuntu:~/work/unix/code/chapter7$ echo $PWD
    /home/ckt/work/unix/code/chapter7
    

    使用PATH=$PATH:/home/ckt/work/unix/给PTAH变量追加路径/home/ckt/work/unix/

    ckt@ubuntu:~/work/unix/code/chapter7$ echo $PATH
    /home/ckt/work/msm8939-la-2-1/LINUX/android/out/host/linux-x86/bin:/home/ckt/work/adt-bundle-linux-x86_64-20140702/sdk/platform-tools:/home/ckt/bin:/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
    ckt@ubuntu:~/work/unix/code/chapter7$ PATH=$PATH:/home/ckt/work/unix/
    ckt@ubuntu:~/work/unix/code/chapter7$ echo $PATH
    /home/ckt/work/msm8939-la-2-1/LINUX/android/out/host/linux-x86/bin:/home/ckt/work/adt-bundle-linux-x86_64-20140702/sdk/platform-tools:/home/ckt/bin:/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/ckt/work/unix/
    ckt@ubuntu:~/work/unix/code/chapter7$ 
    

    5.2 使用getenv获得环境变量

    • 示例
    #include "apue.h"
    
    int main(int argc, char const *argv[])
    {
        char * home = NULL;
        char * shell = NULL;
        char * path = NULL;
    
        if ((home = getenv("HOME")) != NULL)
            printf("%s\n", home);
    
        if ((shell = getenv("SHELL")) != NULL)
            printf("%s\n", shell);
    
        if ((path = getenv("PATH")) != NULL)
            printf("%s\n", path);
    
        return 0;
    }
    
    • 运行结果
    ckt@ubuntu:~/work/unix/code/chapter7$ cc getenv_test.c -o getenv_test
    ckt@ubuntu:~/work/unix/code/chapter7$ ./getenv_test
    /home/ckt
    /bin/bash
    /home/ckt/work/msm8939-la-2-1/LINUX/android/out/host/linux-x86/bin:/home/ckt/work/adt-bundle-linux-x86_64-20140702/sdk/platform-tools:/home/ckt/bin:/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
    

    5.3 通过函数设置环境变量

    • 示例
    #include "apue.h"
    
    void print_pwd(char * info)
    {
        char * pwd = NULL;
    
        printf("%s : ", info);
    
        if ((pwd = getenv("PWD")) != NULL)
            printf("%s\n", pwd);
        else
            printf("pwd is NULL \n");
    
    }
    
    int main(int argc, char const *argv[])
    {
        print_pwd("before setenv");
    
        if (setenv("PWD", "test", 1) < 0)
            err_sys("setenv error");
    
        print_pwd("after setenv");
    
        if (unsetenv("PWD") < 0)
            err_sys("unsetenv error");
    
        print_pwd("after unsetenv");
    
        return 0;
    }
    
    • 运行结果
    ckt@ubuntu:~/work/unix/code/chapter7$ cc setenv_test.c -o setenv_test
    ckt@ubuntu:~/work/unix/code/chapter7$ ./setenv_test
    before setenv : /home/ckt/work/unix/code/chapter7
    after setenv : test
    after unsetenv : pwd is NULL 
    

    6. 进程终止

    进程终止

    参考

    相关文章

      网友评论

          本文标题:从零开始UNIX环境高级编程(7):进程环境

          本文链接:https://www.haomeiwen.com/subject/vubcnttx.html