您的位置:首页 > 编程语言

UNIX环境高级编程-第7章- 进程环境

2014-12-20 16:05 393 查看

7.2 main 函数

         C程序总是从 main 函数开始执行。main 函数的原型是

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

其中,argc 是命令行参数的数目,argv 是指向参数的各个指针所构成的数组。

当内核执行C程序时(使用一个exec函数执行),在调用mian函数前先调用一个特殊的启动例程。可执行程序文件将此启动例程指定为程序的起始地址——这是由连接编辑器设置的,而连接编辑器则由C编译器调用。下面是C程序启动和终止的流程,由图中可以知道,进程的终止根据不同的函数调用是不一样的,其中_exit和_Exit函数是终止进程直接进入内核,exit函数则先执行一些清理,然后再进入内核。内核是程序执行的方法只有调用exec函数。虚线框里面表示用户进程。



7.3 进程终止

       unix中有8种进程终止方式,其中5种是正常终止,3种是异常终止。如下所示:

正常终止:

(1)从main函数返回。

(2)调用exit函数。

(3)调用_exit或_Exit函数。

(4)最后一个线程从其启动例程返回。

(5)最后一个线程调用pthread_exit。

异常终止:

(6)调用abort函数。

(7)接到一个信号并终止。

(8)最后一个线程对取消请求做出响应。

1. exit函数

下面三个函数都是属于正常终止进程,_exit和_Exit直接进入内核,exit函数需要执行一些清理操作(例如终止处理程序、关闭标准I/O流等操作),然后才进入内核。

/* 正常终止程序函数 */
#include <stdlib.h> //ISOC说明的

void exit(int status);//进入内核之前执行一些清理操作
void _Exit(int status);//直接进入内核

#include <unistd.h> //POSIX.1说明的
void _exit(int status);//直接进入内核
//参数status为终止状态 (退出状态)。

2. atexit函数

        一个进程可以登记多达32个函数,这些函数由exit自动调用。称这些函数为终止处理程序。并由atexit函数进行登记,exit根据atexit登记的顺序,按照相反顺序调用终止处理程序函数,同一个函数若登记多次,则exit函数就多次调用该函数。atexit函数的参数是一个函数地址(函数指针)。

#include <stdlib.h>
int atexit(void (*func)(void));
//返回值:若成功则返回0,若出错则返回非0值

测试exit的执行过程:
#include "apue.h"

static void my_exit1(void);
static void my_exit2(void);
int main(void)
{
if (atexit(my_exit2) != 0)
err_sys("can't register my_exit2.\n");
if (atexit(my_exit1) != 0)
err_sys("can't register my_exit1.\n");
if (atexit(my_exit1) != 0)
err_sys("can't register my_exit1.\n");
printf("main is done.\n");
return 0;
}

static void my_exit1(void)
{
printf("first exit register.\n");
}
static void my_exit2(void)
{
printf("second exit register.\n");
}
执行该程序:

[root@www chapter_7]# ./a.out
main is done
first exit handler
first exit handler
second exit handler

7.5 环境表

         每个程序都会对应一张环境表,环境表是一个字符指针数组,其中每个指针包含一个以null结束的C字符串地址。全局变量environ包含了 该指针数组的地址:

extern char **environ; 

下图是环境表,包含3个C字符串,每个字符串结尾都显式的有一个null字符。



7.6 C 程序的存储空间布局

         C程序一直由下面几个部分组成:

(1)      正文字段。这是由CPU执行的机器指令部分。

(2)      初始化数据段。通常称此段为数据段,它包含了程序中需明确地赋初值的变量。例如,C程序中出现在任何函数之外的声明:int maxcount = 99;使此变量带有初值存放在初始化数据段中。(已初始化的全局变量)

(3)      非初始化数据段。通常称此段为bss段。在程序开始执行之前,内核将此段中的数据初始化为0或空指针。出现在任何函数外的C声明:long sum[1000];使此变量存放在非初始化数据段中。(未初始化的全局变量)

(4)      栈。自动变量(局部变量)以及每次函数调用时所需保持的信息都存放在此段中。

(5)      堆。通常在堆中动态存储分配。位于bss 段和栈之间。



7.8 存储器分配





7.9 环境变量

 环境字符串一般表示如下:

name = value;//环境变量名,环境变量值  

 

        以下根据对环境变量不同的操作得到不同的结果,包括获取环境变量当前值,添加环境变量,修改环境变量和删除环境变量;相对应的函数分别为getenv、putenv、setenv和unsetenv函数,具体实现功能和返回值如下:

/*
* 函数功能:获取环境变量值;
* 返回值:指向与name关联的value的指针,若未找到则返回NULL;
* 函数原型:
*/
#include <stdlib.h>
char *getenv(const char *name);
/*

* 函数功能:增加环境变量;
* 返回值:若成功则返回0,若出错则返回非0;
* 函数原型:
*/
#include <stdlib.h>
int putenv(char *str);
/* 说明:
* 把name = value的字符串存放在环境表中,如果name已存在,则先删除其原来的定义;
*/

/*
* 函数功能:修改环境变量;
* 返回值:若成功则返回0,若出错则返回-1;
* 函数原型:
*/
#include <stdlib.h>
int setenv(const char *name, const char *value, int rewrite);
int unsetenv(const char *name);
/* 说明:
* setenv将name设置为value值,若name已经存在,当rewrite为非0时,先删除其现有定义;
* 若rewrite为0,则不删除其现有定义,即不修改name的字符变量;
*
* unsetenv删除name的定义;
*
*/

测试程序:
#include "apue.h"

int main(void)
{
char *ptr;
int temp;

if((ptr = getenv("USER")) == NULL)
err_sys("get environment error.\n");
printf("getnev: USER = %s.\n",ptr);

temp = putenv("linux=linux3.8");
if(0 == temp)
printf("After putenv: linux= %s.\n",getenv("linux"));

temp = setenv("linux","hellow linux",0);
if(0 == temp)
printf("After setenv: rewrite= 0 and linux= %s.\n",getenv("linux"));
temp = setenv("linux","goodbye linux",1);
if(0 == temp)
printf("After setenv: rewrite != 0 and linux= %s.\n",getenv("linux"));
temp = unsetenv("linux");
if(0 == temp)
printf("After unsetenv: linux= %s.\n",getenv("linux"));
exit(0);
}

程序输出:
getnev: USER = root.  

After putenv: linux= linux3.8.  

After setenv: rewrite= 0 and linux= linux3.8.  

After setenv: rewrite != 0 and linux= goodbye linux.  

After unsetenv: linux= (null).  

7.10 setjmp 和 longjmp 函数

在C语言中,当嵌套函数调用出错时,可以采用非局部跳转函数setjmp和longjmp,利用这两个函数可以把程序控制流进行有效转移,这两个函数相结合可以在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中。

/* setjmp 和 longjmp 函数 */

/*
* 函数功能:当某个嵌套函数出错时,可实现程序控制流跳转;
* 函数原型:
* #include <setjmp.h>
* int setjmp(jmp_buf env);//若直接调用则返回0,若从longjmp调用则返回非0值;
*
* void longjmp(jmp_buf env, int val);
* 说明:
* 参数env是某种形式的数组,存放在调用longjmp时能恢复栈状态的所有信息,一般定义为全局变量;
* val设置setjmp的返回值;
*/
测试程序
#include <setjmp.h>
#include "apue.h"

static jmp_buf env;
static int globval;//全局变量

static void fun1(int,int,int,int);
static void fun2(void);
int main(void)
{
int autoval;//自动变量
register int registerval;//寄存器变量
volatile int volatileval;//易失变量
static int staticval;//静态变量
globval = 1; autoval = 2;
registerval = 3; volatileval = 4;
staticval = 5;
if(setjmp(env) != 0)
{
printf("after longjmp:\n");
printf("globval= %d, autoval= %d, registerval= %d,"
"volatileval= %d, staticval= %d\n",
globval,autoval,registerval,volatileval,staticval);
printf("longjmp error\n");
exit(0);
}
globval = 10; autoval = 11;
registerval = 12; volatileval = 13;
staticval = 14;
fun1(autoval,registerval,volatileval,staticval);
exit(0);
}
static
void fun1(int i, int j, int k, int l)
{
printf("in funi():\n");
printf("globval= %d, autoval= %d, registerval= %d,"
"volatileval= %d, staticval= %d\n",
globval,i,j,k,l);
fun2();

}
static void fun2(void)
{
longjmp(env,1);
}

输出结果:
没有优化编译的结果:

[root@www chapter_7]# gcc 7-5.c

[root@www chapter_7]# ./a.out

in funi():

globval= 10, autoval= 11, registerval=12,volatileval= 13, staticval= 14

after longjmp:

globval= 10, autoval= 11, registerval=12,volatileval= 13, staticval= 14

longjmp error

优化编译的结果:

[root@www chapter_7]# gcc -O 7-5.c

[root@www chapter_7]# ./a.out

in funi():

globval= 10, autoval= 11, registerval=12,volatileval= 13, staticval= 14

after longjmp:

globval= 10, autoval= 2, registerval=3,volatileval= 13, staticval= 14

longjmp error测序讲解:

1、在main函数中,第一次直接调用setjmp时返回值是0,所以不执行main函数的打印部分,直接调用fun1函数;

2、在fun1函数中,首先打印两条语句,然后调用fun2函数。

printf("in funi():\n");  

    printf("globval= %d, autoval= %d, registerval= %d,"  

                "volatileval= %d, staticval= %d\n",  

                i,j,k,l);  

3、在fun2函数中,调用longjmp函数,相当于在这里程序出错,返回到main函数的setjmp处,这次是main函数中的setjmp返回值是val=1,则执行以下的打印信息,然后exit退出main函数:

printf("after longjmp:\n");  

        printf("globval= %d, autoval= %d, registerval= %d,"  

                "volatileval= %d, staticval= %d\n",  

                globval,autoval,registerval,volatileval,staticval);  

        printf("longjmp error\n");  

        关于全局变量、自动变量、寄存器变量、易失变量、静态变量在setjmp和longjmp函数中的变化是有区别的,存放在存储器的变量将和longjmp时的值一样,存放在CPU和浮点寄存器的变量则会恢复为调用setjmp时的值。自动变量、寄存器变量存放在寄存器中,所以其变量值恢复到调用setjmp时的值,全局变量、易失变量、静态变量存放在存储器中,所以变量将和longjmp时的值一样。

7.11 getrlimit 和 setrlimit 函数

每个进程都有一组资源限制,其中一些可以使用getrlimit和setrlimit函数进行查询和修改。进程的资源限制是在系统初始化时由进程0建立的,然后由每个后续进程继承,每种实现都可以用自己的方法对各种限制做出调整。

/* 进程资源限制 */
/* 查询和修改进程资源 */
/*
* 函数功能:查询或修改进程资源;
* 返回值:若成功则返回0,若出错则返回非0值;
* 函数原型:
*/
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlptr);//查询进程资源
int setrlimit(int resource, const struct rlimit *rlptr);//修改进程资源
对这两个函数的每一次调用都会制定一个资源以及一个指向下列结构的指针。
/*
* rlimit 结构信息如下:
*/
struct rlimit
{
rlimit_t rlimit_cur; /* soft limit: current limit */
rlimit_t rlimit_max; /* hard limit: maximum value for rlimit_cur */
};

在更改资源限制时,必须遵循下列三条规则:
(1)任何进程都可将一个软限制值更改为小于或者等于其硬限制值。

(2)任何一个进程都可以降低其硬限制值,但它必须大于等于其软限制值。这种降低对普通用户来说是不可逆的。

(3)只有超级用户进程可以提高硬限制值。

这两个函数的resource参数是下列参数值之一:

RLIMIT_AS:     进程可用存储区的最大总长度(字节)。  

RLIMIT_CORE:   core文件的最大字节数,若其值为0则阻止创建core文件。  

RLIMIT_CPU:    CPU时间的最大值(秒),当超过此软限制时,向该进程发送SIGXCPU信号。  

RLIMIT_DATA:   数据段的最大字节长度。  

RLIMIT_FSIZE:  可以创建的文件的最大字节长度。当超过此软限制时,则向该进程发送SIGXFSZ信号。  

RLIMIT_LOCKS:  一个进程可持有的文件锁的最大数。  

RLIMIT_MEMLOCK:一个进程使用mlock能够锁定在存储器中的最大字节长度。  

RLIMIT_NOFILE: 每个进程能打开最大文件数。更改此限制将影响到sysconf函数在参数_SC_OPEN_MAX返回的值。  

RLIMIT_NPROC:  每个实际用户ID可拥有的最大子进程数。更改此限制将影响到sysconf函数在参数_SC_CHILD_MAX返回的值。  

RLIMIT_RSS:    最大驻内存集的字节长度。(resident set size in bytes,RSS)  

RLIMIT_SBSIZE: 用户在任一给定时刻可以占用的套接字缓冲区的最大长度。  

RLIMIT_STACK:  栈的最大字节长度。  

RLIMIT_VMEM:   这是RLIMT_AS的同义词。  

测试测序:

#include "apue.h"
#include <sys/resource.h>
static void pr_limits(char *,int);

int main(void)
{
pr_limits("RLIMIT_CORE",RLIMIT_CORE);
pr_limits("RLIMIT_CPU",RLIMIT_CPU);
pr_limits("RLIMIT_AS",RLIMIT_AS);
pr_limits("RLIMIT_DATA",RLIMIT_DATA);
pr_limits("RLIMIT_FSIZE",RLIMIT_FSIZE);
pr_limits("RLIMIT_MEMLOCK",RLIMIT_MEMLOCK);
pr_limits("RLIMIT_NOFILE",RLIMIT_NOFILE);
pr_limits("RLIMIT_LOCKS",RLIMIT_LOCKS);
pr_limits("RLIMIT_RSS",RLIMIT_RSS);
pr_limits("RLIMIT_NPROC",RLIMIT_NPROC);
pr_limits("RLIMIT_STACK",RLIMIT_STACK);
exit(0);
}
static void pr_limits(char* name, int resource)
{
struct rlimit limit;
if(getrlimit(resource, &limit) <0)
err_sys("getrlimit error for %s",name);

printf("%-14s ",name);
if(limit.rlim_cur == RLIM_INFINITY)
printf("(infinite) ");
else
printf("%-14ld",limit.rlim_cur);
if(limit.rlim_max == RLIM_INFINITY)
printf("(infinite) ");
else
printf("%-14ld",limit.rlim_max);
printf("\n");
}

输出结果:
RLIMIT_CORE    0             (infinite)    

RLIMIT_CPU     (infinite)  (infinite)    

RLIMIT_AS      (infinite)  (infinite)    

RLIMIT_DATA    (infinite)  (infinite)    

RLIMIT_FSIZE   (infinite)  (infinite)    

RLIMIT_MEMLOCK 65536         65536           

RLIMIT_NOFILE  1024          4096            

RLIMIT_LOCKS   (infinite)  (infinite)    

RLIMIT_RSS     (infinite)  (infinite)    

RLIMIT_NPROC   30742         30742           

RLIMIT_STACK   8388608       (infinite)   

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: