代码改变世界

Linux进程环境

2012-01-04 17:58  马哈鱼  阅读(492)  评论(0编辑  收藏  举报

一 main函数

当内核使用一个exec函数执行C程序时,在调用main函数之前先调用一个特殊的启动例程,可执行程序将此例程指定为程序的起始地址。启动例程从内核获取命令行参数和环境变量,然后为调用main函数做好准备。

二 进程终止
进程终止的方式有8种,前5种为正常终止,后三种为异常终止:
1 从main函数返回;
2 调用exit函数;
3 调用_exit或_Exit;
4 最后一个线程从启动例程返回;
5 最后一个线程调用pthread_exit;
6 调用abort函数;
7 接到一个信号并终止;
8 最后一个线程对取消请求做出响应。
(1)exit函数
#include <stdlib.h>
void exit( int status );
void _Exit( int status );
#include <unistd.h>
void _exit( int status );
这三个函数用于正常终止一个程序,_exit和_Exit立即进入内核,而exit则要先做一些清理工作(调用执行各终止处理程序,关闭所有标准I/O流),再进入内核。三个函数所带的整型参数称为终止状态或退出状态,如果(a)调用这些函数不带参数,(b)main函数中的return语句无返回值,(c)main函数没有声明返回类型为整型,则进程的终止状态是未定义的。main函数返回一个整型值与用该值调用exit是等价的。
(2)atexit函数
#include <stdlib.h>
int atexit( void (*fun)( void ) );
一个进程可以登记32个函数,这些函数由exit自动调用,这些函数被称为终止处理函数,atexit函数可以登记这些函数。exit调用终止处理函数的顺序和atexit登记的顺序相反,如果一个函数被多次登记,也会被多次调用。

三 环境表
每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以NULL结尾的环境字符串,环境指针environ是一个全局变量,指向指针数组的地址。通常用getenv和putenv函数来访问特定的环境变量,而不是environ全局变量。如果要查看整个环境,则必须用environ全局变量。

四 C程序的存储空间布局
1 正文段
CUP执行的机器指令部分,是共享和只读的。
2 初始化数据段
又称作数据段,包含了程序中明确需要赋初值的变量。
3 非初始化数据段
在程序开始执行前,内核将此段中的数据初始化为0或空指针。
4 栈
自动变量以及每次函数调用时所需保存的数据都存放在此段中。
5 堆
用于动态存储分配。堆位于栈和非初始化数据段之间。

五 存储器分配
#include <stdlib.h>
void *malloc( size_t size );
void *calloc( size_t nobj, size_t size );
void *realloc( void *ptr, size_t newsize );
void free( void *ptr );
malloc函数分配指定字节数的存储区,该存储区中的初始值不确定;calloc函数为指定数量且指定长度的对象分配存储空间,该空间中的每一位都初始化为0;realloc函数更改存储区的长度(增加或减少),新增区域内的初始值不确定,如果ptr为空,realloc和malloc的功能相同。
以上函数的大多数实现所分配的存储空间都比所要求的大一些,额外的空间用来存储管理信息。如果在一个超过已分配区的尾端进行写操作,就会重写下一个分配区的管理记录;同样,在一个已分配区的起始位置之前写入,会重写本分配区的管理记录。这种错误是灾难性的,但因为不会很快暴露出来,所以很难发现。

六 环境变量
环境字符串的形式如:name=value,它们的解释完全取决于各个应用程序,而与内核无关。
#include <stdlib.h>
char *getenv( const char *name );
int putenv( char *str );
int setenv( const char *name, const char *value, int rewrite );
int unsetenv( const char *name );
getenv函数返回指向name=value中的value的指针;putenv函数把字符串name=value放入环境表中,如果name已经存在,则先删除原来的定义;setenv函数将name设置为value,如果name存在且rewrite非0,则删除其现有定义,若rewrite为0,则不删除其现有定义;unsetenv函数删除name的定义,即使不存在也不会出错。

七 setjmp和longjmp
#include <setjmp.h>
int setjmp( jmp_buf env );
void longjmp( jmp_buf env, int val );
setjmp和longjmp函数用于处理发生在深层次函数调用中的出错情况,longjmp函数可以在栈上跳过若干个调用帧,返回到当前函数调用路径上的某个函数中。在希望返回到的位置调用setjmp,数据类型jmp_buf是某种形式的数组,存放在调用longjmp时能用来恢复栈状态的所有信息。因为需要在另一函数中引用env变量,所以将env定义为全局变量。当检查到一个错误时,调用longjmp函数,第一个参数env就是在调用setjmp时所用的env,第二个参数val非0,它将成为从setjmp处返回的值。使用第二个参数的原因是一个setjmp可以对应多个longjmp,这样就可以根据返回值来判断造成返回的longjmp函数在那个函数中,从而确定出错的位置。

八 getrlimit和setrlimit函数
#include <sys/resource.h>
int getrlimit( int resource, struct rlimit *rlptr );
int setrlimit( int resource, const struct rlimit *rlptr );
getrlimit和setrlimit函数用于获取或设置进程的资源限制。资源限制通常是由进程0建立的,由每个后续进程继承。更改资源限制时,注意以下三条规则:
1 进程的软限制值只能小于或等于硬限制值;
2 任意进程都可以降低其硬限制值,但它必须大于或等于其软限制值,这种操作对普通用户是不可逆的;
3 只有超级用户进程可以提高硬限制值。
资源限制影响到调用进程并由其子进程继承,这意味着为了影响一个用户的所有进程,需要将资源限制构造在shell中。