简介:这节我们主要讲进程环境相关的内容。例如程序是如何开始执行的、进程一些环境如何获取、进程的内存布局是什么样的,进程各种不同的终止方式。
1:程序如何开始执行。
通常、我们都知道,我们写的c程序入口时main函数。main函数的两个参数分别是argc和argv,其中argc是参数的个数,argv是一个指针数组,存储的是具体的参数。
当我们开始执行程序的时候,先调用一个启动例程。启动例程从内核读取号命令行参数和环境变量,为main函数做好安排。然后启动main函数。注:这个过程我们简单了解即可。
2:进程终止的各种方式。
总共有8中进程终止的方式,这里逐一做介绍。
(1)从main函数返回。
(2)调用exit。
(3)调用_exit或_Exit。
(4)最后一个线程从启动例程返回。
(5)最后一个线程调用pthread_exit。
(6)调用abort。
(7)接收到一个信号异常终止。
(8)最后一个线程对取消请求做出相应。
首先我们这里给出一张图片,这张图片包括进程启动和退出的一些流程。注:我们只讨论针对用户部分,对于内核部分我们不做讨论。
从图中我们就可以看出,从main函数中返回是返回到启动例程中去了。之后我们再返回到内核中去。
(2)和(3)唯一的区别是(2)会做一些清除处理,然后返回到内核中去。
(6)abort:此函数将产生一个异常终止信号,并且该函数保证该信号不会被终止。
同时、从图中我们看到有终止程序,接下来我们将介绍两个有关终止处理的函数。
函数原型:
void exit(int status);
int atexit(void (*func)(void));
第一个函数我们已经了解了,其中的参数是返回父进程的状态。
第二个函数是注册终止处理的函数,它注册函数的顺序和我们再做终止处理调用终止处理函数的顺序正好相反。
下面我给出一个例子
#include
#include
#inculde
void exit1();
void exit2();
int main()
{
printf("test atexit");
atexit(exit1);
atexit(exit2);
exit(0);
}
void exit1()
{
printf("exit1\n");
}
void exit2()
{
printf("exit2\n");
}
结果如下图
3:前面我们提到启动例程从内核中继承环境表,此环境表给我们的程序做准备。
在这里,我们简单介绍一下环境表。存储环境表的是一个指针数组,并且内存中有一个环境指针指向环境表的内存地址。
环境表是由name=value组成,如下图所示。
我们通常使用purtenv和getenv函数来访问和设置环境表中的环境变量。
char* getenv(const char* name);//此函数搜索环境表中的name,并且返回环境表中对应value的指针。
int putenv(const char* name);//此函数参数部分是name=value形式的字符串,它遍历环境表,并且将环境表对应的值设置成我们的value。
4:接下来我们讲解C程序的存储空间布局。
c程序存储空间可以分为如下几个部分:
正文段:CPU执行机器指令的部分,代码存储的位置,CPU从中读取并执行相应的指令。
初始化数据段:此段内存表明了程序明确要赋值 的部分,例如全局变量int a = 10;
非初始化数据段:此段存储没有要明确初始化的内存部分。
栈:自动变量分配的区域。例如函数中定义的int a = 10;
堆:程序员手动申请和释放内存的区域。
下面给出一张图,这张图是关于内存分配的。
通常情况下,我们程序员手动分配函数会用到如下几个函数:
malloc、calloc、 realloc、free。关于这几个函数是怎么使用的,请参考man手册。
5:环境变量的格式也是name=value的格式,我们可以使用getenv和setenv函数获取和设置环境变量。
所谓的环境变量我的理解就是提高给程序一个可以使用的环境所定义的变量。
6:系统的各种限制。
我们计算机的资源是有限的,我们需要在者有限的资源中运行程序,这些限制都有一个最大值和一个默认值。最大值也就是我们硬件上的限制,例如你的计算机只有10m内存,那么你
很难去申请超过10m的内存。还有一个就是软件上的限制,这个限制我们可以更改。
eg:程序可以打开的文件描述符数目,通常的情况下是1024个文件描述符,但是我们可以更改到2048个(这需要看具体的计算机了)。
下面介绍两个函数关于这种限制的。
int getrlimit(int resource, struct rlimit *rptr);
int setrlimit(int resource, const struct* rlimit *rlptr);
其中struct rlimit结构如下。
struct rlimit
{
rlim_t rlim_cur;/*软件上的限制*/
rlim_t rlim_max;/*硬件上的限制*/
};
注:我们硬件显示只有在系统初始化时候0号进程才能更改。然后由各个进程继承。关于更改各种限制有如下几条规则。
(1)任何一个进程都可以更改软限制,但是必须小于硬限制值。
(2)任何一个进程都可以降低硬限制的值,但是不能向大更改。
(3)只有超级用户进程才能更改硬限制值。
关于计算机的各种限制如下表。