进程环境 (第七章)
1、进程终止方式
正常终止:
- main函数返回
- exit()
- _exit() 或 _Exit()
- 最后一个线程返回
- 最后一个线程调用pthread_exit()
异常终止:
- abort()
- 接到一个信号
- 最后一个线程对取消请求做出响应
相关函数如下:
#include <stdlib.h>
void exit(int status);
void _Exit(int status);
#include <unistd.h>
void _exit(int status);
其中exit() (或调用return)会调用终止处理函数后再调用_exit()/_Exit(),终止处理函数常用于处理终止前的一些数据,如关闭打开的I/O等。status为终止状态,同main()函数返回值。如main()声明的返回类型不为int;或者调用的上述exit不带终止状态;或者main()执行未返回值的return,则终止状态未定义。如main声明为返回int,且main()执行到最后一条语句后隐藏返回,则终止状态为0。
添加终止处理函数方法如下:
#include <stdlib.h>
int atexit(void (*func)(void));
成功返回0,失败非0
atexit()注册函数的顺序与调用顺序相反,同个函数被注册多次将被调用多次。其中支持的函数个数,各系统不一,至少为32个。
2、C程序的存储空间
C程序的存储空间如下:
需要存放在磁盘文件中的只有正文段和初始化的数据。
- 正文段:常为可共享的只读空间;
- 初始化数据段:即数据段,存放初始化的数据,如初始化的全局变量;
- 非初始化数据段:即bss段,执行执行前会被自动初始化为0或NULL,如非初始化的全局变量;
- 栈:存放自动变量及每次函数调用时的信息,如函数调用时的返回地址、环境信息等(会自动释放);
- 堆:进行动态存储分配(要手动释放)。
查看各段的大小,如下:
3、存储空间分配
空间分配函数有3个,如下:
#include <stdlib.h>
void *malloc(size_t size);
分配空间未初始化
void *calloc(size_t nobj, size_t size);
分配空间初始化为0(分配size个单元,每个单元的大小为nobj)
void *realloc(void *ptr, size_t newsize);
增减分配区的长度(内容未初始化,其中newsize为新的总的长度,若ptr为NULL,则同malloc)
注:实现分配的长度会大小请求的长度(其中包括一些管理信息),所以在分配空间的前后写,会修改另一个数据单元的管理记录信息。对于空间的另一种常见错误是
重复释放一个块或者调用free()释放非上述3个函数分配的空间。
另有一个函数 alloca() ,其用法同malloc(),但其在栈上分配空间(不用手动释放,缺点是有的系统在函数被调用后,栈的空间不能增加了,便不支持该函数了)
4、环境变量
环境变量相关函数如下:
#include <stdlib.h>
char *getenv(const char *name);
返回NULL或name对应的value
int putenv(char *str);
成功返回0,失败非0
其中str形式为:"name=value",若name存在则先删除。
int setenv(const char *name,const char *value,int rewrite);
成功返回0,失败-1
若name存在,且rewrite非0,则先删除(修改),若rewrite为0,则不修改(但不出错)
int unsetenv(const char *name);
成功返回0,失败-1
删除指定name,若不存在也不算出错
其中系统中定义的name如下:
putenv和setenv的区别:setenv()中会重新分配空间来存放”name=value”;而putenv不会,所以putenv的参数不能在栈中,否则会出错。
环境表(name=value的指针数组)和环境字符串存放在进程空间的顶部(栈之上),所以该空间不能再增加。
5、实现跨函数的跳跃:setjmp()和longjmp()
相关函数定义如下:
#include <setjmp.h>
int setjmp(jmp_buf env);
直接调用返回0,从longjmp返回则为非0
指定要跳转到的点
void longjmp(jmp_buf env,int val);
跳转到指定的点
注:jmp_buf用来存放恢复栈的所有信息,通常为全局变量,两个函数应为同一个jmp_buf。val为setjmp的返回值(一个setjmp可能对应多个longjmp,所以用
val区分是从哪里返回的)
在跳转时,全局、静态变量的值不变,对于自动变量要想其不变,可指定 volatile 属性。如 volatile int x;
6、查询/更改进程的资源限制
进程的资源限制通常在系统初始化时由0进程建立,相关函数如下:
#include <sys/resource.h>
int getrlimit(int resource,struct rlimit *rlptr);
成功返回0,出错返回非0
int setrlimit(int resource,const struct rlimit *rlptr);
成功返回0,出错返回非0
struct rlimit{
rlim_t rlim_cur;
rlim_t rlim_max;
}
更改进程的资源限制时规则:
- 任一进程都可将其软限制值改为小于等于其硬限制值
- 任一进程都可降低其硬限制值,但必须大于等于其软限制值,同时对于普通用户该操作不可逆
- 只有root可提高硬限制值
其中,RLIM_INFINITY 指定一个无限量的限制,resource 的取值如下:
注:资源限制影响到调用进程并由子进程继承。shell就是这个原理。