《Unix高级环境编程》第七章 进程环境

时间:2022-10-21 22:16:32

Process Environment

7-1 Introduction

在第八章介绍进程控制原语(process control primitives)之前,我们需要先研究一下进程执行的环境。

本章我们会关注:
1. main函数是如何在程序执行时被调用的
2. 命令行参数是如何传递给新进程的
3. 内存布局什么样?
4. 如何分配额外的内存
5. 进程如何使用环境变量
6. 终止进程的几种方法
7. longjmpsetjmp
8. 进程的资源限制

对于本章节知识点进行了总结,链接如下:http://blog.csdn.net/feather_wch/article/details/50725828
可用于日后复习之用

7-2 main Function

C程序从main函数开始,其原型如下:

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

C程序是有kernel调用exec程序之一来调用的。在main函数之前存在着特殊的启动函数(start-up)。启动函数被设置为程序的开始地址。这是由link editor设置的。

启动程序的作用?

用于从kernel(内核)获得一些值,如获得了命令行参数(command-line argument)和环境(environment)

7-3 Process termination

进程终止的有几种方式:
一般的终止方式:
1. 从main return
2. 调用exit
3. 调用_Exit, _exit
4. 开始程序的最后一个线程 return
5. 在最后一个线程调用 pthread_exit

异常的终止方式:
6. 调用abort
7. 接收到信号
8. Response of the last thread to a cancellation request(11.5 and 12.7)

启动程序如果是用C编写的(大多是用汇编编写的),那么调用main的部分如下:

exit(main(argc, argv));

Exit Functions

通常终止进程的三个函数:_exit, _Exit和exit
原型如下:

#include <stdlib.h>

void exit(int status);
void _Exit(int status);

#include <unistd.h>

void _exit(int status);

exit()和_Exit()是由ISO C规定的。
_exit()是由POSIX.1 规定的

_exit, _Exit和exit的区别

exit会先进行清除工作,再返回到kernel(例如会调用fclose关系所有打开的流,所有缓冲的输出数据都会flush)
_exit, _Exit会立即返回到kernel

exit(0)return 0是一样的

如果我们并不指定main的返回值类型,如:

main()
{
printf("hello world\n");
}

在不同的系统上,main会返回给环境不同的值,我们可以进行如下测试:

$gcc hello.c
$./a.out
hello world
$echo $? '显示exit状态'
$0 '默认的c99标准,返回的值为0'

$gcc -std=c89 hello.c '采用c89标准来编译'
$./a.out
hello world
$echo $? '显示exit状态'
$12 '返回的值为随机值'

可知,明确指定main的返回值是很重要的,在有些系统中还有可能因此崩溃。

在一些编译器和lint(1) 程序(检查程序中潜在的错误)中,使用exit代替return,会出现警告。然而exit和return效果是一样的。因此可以忽视。

atexit Function

ISO C中,一个进程可以注册32个由exit自动调用的函数。这被称为exit handlers,可以通过调用atexit来注册。
详细讲解atexit链接:http://blog.csdn.net/feather_wch/article/details/50723255

7-4 Command-Line Argument

exec可以将命令行参数传递给新程序,这也是shell的一般操作

int main(int argc, char *argv[])
{
return 0;
}

7-5 Enviornment List

每个程序也传递环境列表。类似于command-line argumentenvironment list是字符指针的数组。该指针数组的地址保存在全局变量environ

 extern char ** environ;

我们称environ为环境指针
《Unix高级环境编程》第七章 进程环境
环境是由如下内容组成:
name = value

大多数系统给main提供了第三个参数,用于存放环境链表,原型如下:

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

第三个参数相对于全局变量并没有提供什么优势。因此POSIX.1 规定environ取代main的第三个参数。

getenv、putenv

访问特定的环境变量一般通过这两个函数

7-6Memory Layout of a C program

compoents 解释 具体说明
Text segment CPU执行的机器指令 文本段是共享的,only a single copy needs to be in memory for frequently executed programs, such as text editors, the C compiler, the shells, and so on.此外文本段是只读的
Initialized data segment 也被成为data segment 保存在程序初始化的变量, 如int maxcount = 99;
Uninitialized data segment 又称为“bss” segment 在程序开始执行后,由内核初始化0和NULL
Stack 保存自动变量 Each time a function is called, the address of where to return to and certain information about the caller’s environment, such as some of the machine registers, are saved on the stack.每次递归调用函数的时候,一个新的stack frame就会被使用
Heap 动态内存分配 处于为初始化数据段和stack之间

《Unix高级环境编程》第七章 进程环境

a.out文件中存在更多地段类型,如debugging information, linkage table for dynamic shared libraries等等,但是这些额外的段并不是进程执行程序的镜像的部分

从图7.6我们可以知道bss段是exec执行时自动初始化为0的,那么仅仅有text段initialized data segment是保存在程序文件中的

  • size(1) command可以查看各个段的大小
    如:size /bin/sh

7-7 Shared Libraries

共享库移除了可执行文件*同的库程序(library routines),在内存中存放着library routine的copy,任何进程都可以调用。

优点:1. 减少可执行文件的尺寸 2. 库程序可以直接用新版本替换,而不需要重新连接每个使用该库的程序(前提是:参数类型和数量不变)
缺点:程序第一次调用,或者该库程序第一次调用的时候,会有额外的时间消耗

$gcc hello.c    '使用共享库'
$size a.out
text data bss dec hex filename
1214 560 8 1782 6f6 a.out

$gcc -static hello.c '不使用共享库'
$size a.out
text data bss dec hex filename
790784 7572 9016 807372 c51cc a.out

如上面的测试可知,使用共享库和不使用共享库,可执行程序的大小相差很多。

7-8 Memory Allocation

malloc、calloc、realloc、alloca

链接:http://blog.csdn.net/feather_wch/article/details/50725778

7-9 Environment Variables(环境变量)

1.什么是环境变量?

环境字符串例如name=value的形式,例如环境变量HOME 和 USER

2.环境变量的作用

  1. HOME or USER 在登陆时自动被设置
  2. 可以在shell的启动文件里面设置环境变量来控制shell的操作
  3. 又如环境变量MAILPATH能告知shell哪里能找到mail

3.环境变量的操作函数(getenv/putenv/setenv/unsetenv)

链接7.9:http://blog.csdn.net/feather_wch/article/details/50740004

4.putenv和setenv的区别

见链接7.9

5.环境变量相关注意点

  • 我们应该总是使用getenv来获取环境的特殊值,而不是直接访问environ
  • 下章我们会知道我们可以仅仅影响当前进程和其子进程的环境
  • 我们不能影响父进程的环境,父进程经常是shell
  • 环境变量删除容易,增加难(栈上面存放环境变量的空间是无法扩充的

6.修改、增加环境变量的几种情况

见链接7.9

7-10 setjmp and longjmp

1.作用

C中我们不能goto一个其他函数离得label,我们必须使用setjmplongimp来完成这种类型的branching

链接7.10:http://blog.csdn.net/feather_wch/article/details/50740245

2-注意

如果你正在编写可移植性的代码,并且在其中使用了非局部的跳转,你必须使用volatile

在第十章讲到信号时,我们会讲解他们的信号版本:sigsetjmpsiglongjmp

3-使用自动变量的潜在问题

最基本原则:自动变量在声明它的函数返回后永远不能被引用。
例如:函数中定义的数组,函数结束时却将其数组地址返回。这些空间已经被释放了,但是其他地方还引用就会产生错误。最好的办法是用static, extern或者maalloc分配空间

7-11 getrlimit、setrlimit Functions

这有什么用?

每个进程都有一组资源的限制,通过getrlimit、setrlimit能查寻和改变资源限制。
链接:http://blog.csdn.net/feather_wch/article/details/50749105