unix/linux多进程编程学习日志1-进程环境

主要内容


①:unix/linux中main函数如何被调用

②:命令行参数如何传递给执行程序

③:典型存储器布局的样式

④:如何分配另外的存储空间

⑤:进程如何使用环境变量

⑥:各种不同的进程终止方式

⑦:longjmp和setjmp函数以及它们与栈的交互作用

⑧:进程的资源限制

1:main()函数


C程序总是从main()函数启动的,main()函数的原型是int main(int argc ,char *argv[]);

内核执行C程序时(使用一个exec),在调用main函数前先调用一个特殊的启动例程。可执行程序文件将启动例程指定为程序的起始地址。启动例程从内核获取命令行参数和环境变量值,为然后为按上述方式调用main函数做好准备.

2:进程的终止


 

UNIX/LINUX中有8种终止进程的方式,5种正常终止方式,3种异常终止方式,分别为:

正常方式:

①main()函数返回

②调用exit

③调用_exit或者_Exit

④最后一个线程从其启动例程返回

⑤最后一个线程调用pthread_exit

异常方式

⑥调用abort

⑦接到一个信号并终止

⑧最后一个线程对取消请求做出响应

上面提到了一个特殊的启动例程,如果用C语言的形式来表示(通常用汇编来写),它这么编写,exit(main(argc,argv));

 

3个正常终止程序函数的区别

①_exit()使用POSIX1.0说明的,头文件是unistd.h,exit()和_Exit()是由ISO C说明的头文件是stdlib.h

②_exit()和_Exit()立即进入内核,exit()会做清理工作(调用执行终止处理工作,关闭IO流)

atexit()函数

根据ISO规定,进程最多可以登记32个函数,这些函数由atexit()函数登记,由exit()调用,称为exit hander 终止处理程序.atexit原型如下

#include<stdlib.h>

int atexit(void (*func) (void));

参数是一个函数地址,这个地址的函数无传任何参数,也没有任何返回值.exit()调用它们的顺序,与登记的顺序相反.登记成功返回0,反之登记失败

 

 

3:环境表

每个程序都会有接收到一张环境表,类似于参数表,环境也是一个字符串指针数组,每个指针都是一个以NULL结尾的字符串地址.全局变量environ包含了环境指针的地址.

注意:通常通过getenv()和putenv来操作特定的环境变量,如果访问整个环境变量才使用environ指针.


4:C程序的存储空间布局


 

C程序的存储空间由以下及部分组成

①:正文段(Text segment),CPU执行的指令部分,这部分是可共享的,比如文本编辑器,shell,不过你打开多少个,正文段在存储器当中只需要一个副本即可.另外正文段常常是不可读,防止程序意外修改自身指令.

②:初始化数据段(Initialized data segment),通常也称之为数据段,包含程序当作需明确的赋初值的变量.例如C程序中,出现在任何函数之外的声明:

int maxcount = 100;

③:未初始化数据段(Uninitialized data segment),通常称为BSS段,在程序开始执行之前,由内核将此数据段中的数据初始化为0或者NULL.例如出现在任何函数之外的int arr[100];

④:栈(stack).自动变量以及每次函数调用时所需保留的信息都放在此段中.每次调用函数时,返回地址,调用者的环境信息(例如某些机器的寄存器值),新调用的函数在栈中申请一块空间,用来保存它的自动变量和临时变量。这也是为什C语言当中递归函数能够工作的原因,因为递归函数中每次它调用自身都会申请一个新的栈帧。因此同一个函数的两个或者多个调用实例不会互相影响。

⑤:堆(heap).用来动态分配内存.

一个典型的安排方式



unix/linux当中可以使用使用size命令查看正文段,数据段,以及bss段的大小

如下图所示


从上图中还可以看出,bss段的内容并不存放在磁盘文件当中.原因是内核再程序开始运行前会将它们都设置为0,存在磁盘的程序文件当中的只有正文段和初始化数据段.

text:正文段

data:初始化数据段

bss:未初始化数据段

dec和hex分别为三个数据段大小之和的十进制和十六进制表示.

5:共享库


 

又称为动态库,与之相反的是静态库。这种方法使得进程不需要包含公用的库例程,只在所有进程都可引用的存储位置维护这个库例程的一个副本即可。程序运行时再动态连接。

优点:

减少了可执行文件长度,库函数变更,不需要重新编译连接使用该库的程序。

缺点:

增加时间开销,这种开销发生在程序执行第一次调用共享库函数时。

example:

不使用共享库

使用共享库
 

可以看出不使用共享库的时候程序占用的空间变得很大

6:存储器分配


ISO三个用户存储器分配的函数

①void *malloc(size_t size);

分配一块指定字节数的存储区,初始值不固定

②void *calloc(size_t nobj , size_t size);

分配一块指定字节数的存储区,初始值为0

③void *realloc(void *ptr , size_t newsize);

更改以前分配的存储区的长度,如果长度增加,那么可能发生整个存储区域的移动,之前分配的内存里面值不变,而新增加的存储区值则不确定.另外由于可能发生存取区移动,所以对于任何以后可能需要用到realloc更改大小的存储区域,不应该有任何额外的指针指向其存储区中。newsize是指更改之后存储区的大小,而非前后之差。返回更改后的存储区指针。

注意:

a:三个函数返回的指针一定是适当对齐的,使其可用于任何数据对象。

b:大多数实现所分配的存储空间都比指定的大,额外空间用来记录管理信息。所以越界操作是件非常危险而且难以分析事情,尾端写入会重写后一个块的管理信息,起始位置之前写入,则会重写本块的管理记录。

其他替代的函数

libmalloc

vmalloc

快速适配(quit-fit)

alloca

这个函数在当前函数的栈帧上分配空间,函数返回时释放.

7:环境表



7.1:内核并不查看环境表,各个环境变量如何解释完全取决与应用程序

7.2父子进程环境表之间的关系

7.3:操作环境表的几个函数

char *getenv(const char *name); //获取某个环境变量的值,不存在则返回空

int putenv(char *str); //将取形式为name=value的字符串放到环境表中,如果环境表中以及存在name则将其替换

int setenv(const char *name,const char *value,const int rewrite) //将环境表中的name设置为value,如果name存在,则看rewrite,不为0替换,为0则

//不删除也不报错。

int unsetenv(const char *name); //删除name的定义,不存在也不报错

注意:setenv和putenv在实现时差别很大,putenv不会分配存储区,linux中将传递给它的字符串地址作为参数放入了环境表中,这种情况下如果将一个在栈中的字符串作为参数传递给了putenv就会发送错误,因为函数结束时,栈帧可能被重用。而setenv则必须分配存储区。

8:setjmp 和 longjmp




C语言当中go to并不能够跨越函数,但setjmp/longjmp能做到.这种方式用于深层次嵌套函数当中的出错处理非常有用。

常用方式

深层次嵌套函数调用当中的出错处理

jmp_buf jmpbuff //设置一个全局变量

setjmp(jmpbuff); //设置跳转位置

longjmp(jmpbuff,val) //执行跳转,val可以用来标记调用位置

9:getrlimit和setrlimit




9.1:每个进程均有一组资源限制,其中一些可由getrlimit和setrlimit查询或者更改

9.2:更改时遵循以下三条规则

①任何进程均可将其软限制更改为小于或者等于其硬限制

②任何进程均可降低其硬限制,但必须大于或者等于其软限制,普通用户而言这种修改是不可逆的

③只有超级用户可以提高其硬限制

9.3软硬限制

硬限制,软限制的上限,只能有超级用户提高

软限制,内核实际执行的限制,任何进程都可将其设为小于其硬限制的一个值

9.4 linux2.4.22支持的资源限制有如下几种

①RLIMIT_AS 进程可由存储区的最大长度,显然这会影响sbrk 和nmap函数

②RLIMIT_CORE core文件的最大字节数,为0则阻止创建core文件

③RLIMIT_CPU CPU时间的最大量值,超过此软限制时,向该进程发送SIGXCPU信号

④RLIMIT_DATA 数据段的最大字节长度.Initialized data segment,bss,heap的总和

⑤RLIMIT_FSIZE 创建文件的最大长度

⑥RLIMIT_LOCKS ...

P166


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值