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

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. 进程终止

进程终止

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,657评论 18 139
  • 一、温故而知新 1. 内存不够怎么办 内存简单分配策略的问题地址空间不隔离内存使用效率低程序运行的地址不确定 关于...
    SeanCST阅读 7,808评论 0 27
  • 1. 进程标识 1.1 进程ID 由于每个进程ID都是唯一的,Unix使用进程ID作为进程的标识。使用ps命令可以...
    伤口不该结疤阅读 1,838评论 0 51
  • 心:今天开始,我就要『简书』上说话了,好像还有些小小激动。万一,简友们都不喜欢,怎么办? 灵:不喜欢你就不说话了吗...
    静菥阅读 248评论 0 1
  • 12306大量数据泄露 来源 今早乌云漏洞平台突然爆出了一个骇人听闻的消息:12306用户资料疑似大量泄漏,甚至有...
    Vaayne阅读 882评论 1 3