APUE(7)---进程环境
一、main函数
C程序总是从main函数开始执行。main函数的原型是:
int main(int argv, char *argv[]);
当内核执行C程序时,在调用main前先调用一个特殊的启动例程。可执行程序文件将此启动例程指定为程序的起始地址---这是由连接编辑器设置的,而连接编辑器则由C编译器调用。
二、进程终止
一共有8种方式进程终止,其中五种为正常终止,他们是:1、从main返回;2、调用exit;3、调用_exit或_Exit;4、最后一个线程从其启动例程返回;5、从最后一个线程调用pthread_exit。异常终止有三种:1、调用abort;2、接到一个信号;3、最后一个线程对取消请求做出响应。
1、退出函数
3个函数用于正常终止一个程序,_exit和_Exit立即进入内核,exit则限制性一些清理处理,然后返回内核。
#include <stdlib.h>
void exit(int status);
void _Exit(int status);
#include <unistd.h>]
void _exit(int status);
由于历史原因,exit函数总是执行一个标准I/O库的清理关闭操作:对于所有打开流调用fclose函数,这造成输出缓冲中的所有数据都被冲洗。3个退出函数都带一个整型参数,称之为终止状态。如果main没有声明返回类型为整型,则该进程的终止状态是未定义的。但是若main的返回类型是整型,并且main执行到最后一条语句时返回,那么该进程的终止状态是0。
2.函数atexit
按照ISO C的规定,一个进程可以登记多至32个函数,这些函数将有exit自动调用。我们称这些函数为终止 处理程序(exit handler),并调用atexit函数来登记这些函数。
#include <stdlb.h>
int atexit(void (*func)(void));
atexit的参数是一个函数地址,当调用次函数时无需向他传递任何参数,也不期望它返回一个值。exit调用这些函数的顺序与他们登记时候的顺序相反。同一函数如登记多次,也会被调用多次。exit首先调用个终止处理程序,然会关闭所有打开流。注意,内核是程序执行的唯一方法是调用一个exec函数,进程自愿终止的唯一方法是显式或隐式调用_exit或_Exit。进程也可非自愿地由一个信号使其终止。
7-2 一个C程序如何启动和终止的
三、环境表
每个程序都接收到一张环境表。与参数表一样,环境表也是一个字符指针数组,其中每个指针包含一个以null结束的C字符串的地址。全局变量environ则包含了该指针数组的地址: extern char **environ。
四、C程序的存储空间布局
历史沿袭至今,C程序一直由下列及部分组成:
1、正文段:CPU执行的极其指令部分。通常正文段是可共享的,所以即使是频繁执行的程序在存储器中也只需有一个副本,另外正文段常常是只读的,以防止程序由于意外而修改其指令。
2、初始化数据段:程序中需明确地赋初值的变量,通常称之为数据段。
3、未初始化数据段:通常将此段称为bbs段(block started by symbol),在程序开始执行之前,内核将此段中的数据初始化为0或空指针。未初始化的全局变量就放在这个段。
4、栈:自动变以及每次函数调用时所需保存的信息都存放在此段中。每次函数调用时,其返回地址以及调用者的环境信息都存放在栈中。然后,最近被调用的函数在栈上为其自动和临时变量分配存储空间。通过以这种方式使用栈,C递归函数可以工作。递归函数每次调用时自身时,就用一个新的帧栈,因此一次函数嗲用实力中的变量集不会影响另一次函数调用实例中的变量。
5、堆、通常在堆中进行动态存储分配,由于历史原因,堆位于未初始化数据段和栈之间
7-6 典型的存储空间安排
未初始化数据段的内容并不存放在磁盘程序文件中。其原因是内核在程序开始运行前将他们都设置为0,需要放在磁盘程序文件中的段只有正文段和初始化数据段。
五、共享库
共享库是的可执行文件中不再需要包含公用的库函数,而只需要在所有进程都可引用的存储区中保存这种库例程的一个副本,程序第一次执行或第一次调用某个库函数时,用动态连接方法与共享库函数相连接,者减少了每个可执行文件的长度,但增加了一些运行时间。共享库还有一个优点,就是可以用新版本库函数替换老版本库函数,而无需对程序进行重新编译。
六、存储空间分配
ISOC说明了3个用于储存空间动态分配的函数:
#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 *tpr);
malloc、分配指定字节数的存储区。此存储区中的初始值不确定;calloc,为指定数量长度的对象分配存储空间。该空间中的每一位都初始化为0;realloc,增加或减少以前分配区的长度,当增加长度时,可能需将以前分配区的内容移到另一个足够大的区域,一边在尾端提供增加的存储去,而新增区域内的初始值不确定。这3个函数所返回的指针一定是适当对齐的,使其可用于任何数据对象。
这些分配例程通常用sbrk系统调用实现。该系统调用扩充进程的堆。大多数实现分配的存储空间比所要求的要稍大一些,额外的空间用来记录惯例信息----分配快的长度、指向下一个分配快的指针等。这就意味着,如果超过一个已分配的尾端或者在已分配区起起始位置之前进行写操作,则会该写另一块的管理信息。
七、环境变量
环境字符串的形式是: name=value,unix内核并不查看这些字符串,他们的解释完全取决于各个应用程序。ISO C定义了一个函数getenv,可以用其取环境变量值,但是该标准又称环境的内容是由实现定义的。ISO C没有定义任何环境变量
#include <stdlib.h>
char *getchar(const char *name);
putenv取形式为name=value的字符串,将其防盗环境表中,如果name已经存在,则先删除其原来的定义;setenv将name设置为value。如果name已经存在,如果rewrite非0,则首先删除其现有的定义,否则不删除其现有定义。unsetenv删除name的定义。即使不存在这种定义也不算出错。
#include <stdlib.h>
int putenv(char *str);
int setenv(const char *name, const char *value, int rewrite);
int unsetenve(const char *name);
环境表和环境字符串通常存放在进程存储空间的顶部(栈之上)。删除一个字符串很简单---只要先在环境表中找到该指针,然后将所有后续指针都向环境表顺不顺序移动一个位置就可以了。如果修改一个现有的name:如果新value的长度少于或等于现有value的长度,则只要将新字符串复制到元字符串所用的空间中即可;如果新value的长度大于元长度,则必须调用malloc为新字符串分配空间,然后将新字符串复制到空间中,接着使黄竞标中针对name的指针指向新分配区。
八、函数setjmp和longjmp
在C中,goto语句是不能跨越函数的,而执行这种类型跳转功能的函数是setjmp和longjmp。这两个函数对于处理发生很深层嵌套函数调用中的出错情况是非常有用的。
#include "apue.h"
#define TOK_ADD 5 void do_line(char *);
void cmd_add(void);
int get_token(void); int main(void)
{
char line[MAXLINE];
while(fgets(line, MAXLINE, stdin) != NULL)
{
do_line(line);
}
exit();
} char *tok_ptr; void do_line(char * ptr)
{
int cmd;
tok_ptr = ptr;
while((cmd = get_token()) > )
{
switch(cmd)
{
case TOK_ADD:
cmdd_add();
break;
}
}
} void cmd_add(void)
{
int token;
token = get_token();
} int get_token(void)
{ }
7-9 进行命令处理程序的典型骨架部分
自动变量的存储单元在每个函数的帧栈中。数组line在main的帧栈中,整型cmd在do_line的帧栈中,整型token在cmd_add的帧栈中。多层次嵌套调用返回错误可以使用非局部goto---setjmp和longjmp函数。非局部指的是,这不是由普通的C语言goto语句在一个函数内实施的跳转,二是在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中。
#include <setjmp.h>
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);
#include "apue.h"
#include <setjmp.h>
#define TOK_ADD 5 jmp_buf jmpbuffer; int main(void)
{
char line[MAXLINE]; if(setjmp(jmpbuffer) != )
{
printf("error");
} while(fgets(line, MAXLINE, stdin) != NULL)
{
do_line(line);
}
exit();
} ... void cmd_add(void)
{
int token;
token = get_token();
if(token < )
{
longjmp(jmpbuffer, );
}
}
7-11 setjmp和longjmp实例
#include "apue.h"
#incldue <setjmp.h> static void f1(int, int, int, int);
static void f2(void); static jmp_buf jmpbuffer;
static int globval; int main(void)
{
int autoval;
register int regival;
volatile int volaval;
static int statval; globval = ; autoval = ; regival = ; volaval = ; statval = ;
if(setjmp(jmpbuffer) != )
{
printf("after longjmp:\n");
printf("globval = %d, autoval = %d, regival = %d, volaval = %d, statval = %d\n",
globval, autoval, regival, volaval, statval);
exit();
} globval = ; autoval = ; regival = ; volaval = ; statval = ; f1(globval, autoval, regival, volaval, statval);
exit();
} static void f1(int i, int j, int k, int l)
{
printf("globval = %d, autoval = %d, regival = %d, volaval = %d, statval = %d\n",
globval, i, j, k, l); f2();
} static void f2(void)
{
longjmp(jmpbuffer, );
}
7-13 longjmp对各类变量的影响
全局变量、静态变量和易失变量不受优化的影响,在longjmp之后,他们的值是最近所呈现的值。通过这一实例我们可以理解到,如果要编写一个使用非局部跳转的可移植程序,则必须使用volatile属性。
九、函数getrlimit和setrlimit
每个进程都有一组资源限制,其中一些可以用getrlimit和setrlimit函数查询和更改。
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlptr);
int setrlimit(int resource, const struc rlimit *rlptr);
在更改资源限制时,遵循以下条件:1、任何一个进程可将一个软限制值更改为小于或等于其硬件限制值;2、任何一个进程都可降低其硬限制值,但必须大于或等于其软限制值;3、只有超级用户进程可以提高硬限制值;4、资源限制影响到调用进程并由子进程继承。
APUE(7)---进程环境的更多相关文章
- (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- [07]APUE:进程环境
[a] exit / _Exit / _exit #include <stdlib.h> void exit(int status) void _Exit(int status) #inc ...
- (六) 一起学 Unix 环境高级编程 (APUE) 之 进程控制
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (七) 一起学 Unix 环境高级编程(APUE) 之 进程关系 和 守护进程
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- 《UNIX环境高级编程》(APUE) 笔记第七章 - 进程环境
7 - 进程环境 Github 地址 1. main 函数 C 程序总是从 main 函数 开始执行: int main(int argc, char *argv[]); \(argc\) 为命令行参 ...
- 【转】apue《UNIX环境高级编程第三版》第一章答案详解
原文网址:http://blog.csdn.net/hubbybob1/article/details/40859835 大家好,从这周开始学习apue<UNIX环境高级编程第三版>,在此 ...
- Unix编程第7章 进程环境
准备雄心勃勃的看完APUE,但是总感觉看着看着就像进入一本字典,很多地方都是介绍函数的用法的,但是给出例子远不及函数介绍的多.而且这本书还是个大部头呢.第7章的讲的进程环境,进程是程序设计中一个比较重 ...
- Linux进程环境
Linux下C程序都是main开始的,main函数的原型是: int main(int argc, char **argv) 其中argc是命令行参数的数目,argc是指向参数的各个指针所构成的数组. ...
- Unix环境高级编程(五)进程环境
本章主要介绍了Unix进程环境,包含main函数是如何被调用的,命令行参数如何传递,存储方式布局,分配存储空间,环境变量,进程终止方法,全局跳转longjmp和setjmp函数及进程的资源限制. ma ...
- linux_api之进程环境
本篇索引: 1.引言 2.main函数 3.进程的终止方式 4.exit和_exit函数 5.atexit函数 7.环境表 8.C程序程序空间布局 9.存储空间的手动分配 10.库文件 1.引言 一个 ...
随机推荐
- 4_python之路之模拟工资管理系统
python之路之模拟工资管理系统 1.程序说明:Readme.txt 1.程序文件:salary_management.py info.txt 2.程序文件说明:salary_management. ...
- canvas绘制矩形
canvas绘制矩形 方法 fillRect(x, y, width, height) 画一个实心的矩形 clearRect(x, y, width, height) 清除一块儿矩形区域 stroke ...
- PEM文件和private.key文件生成IIS服务器所需的pfx文件(配置SSL用)
第一步:输入域名,点击"创建免费的SSL证书"按钮,如图 第二步:输入邮箱,点击创建,如图 创建成功后,需要进行dns验证,结果图如下: 第三步:在域名所在的云服务器上,添加域名解 ...
- C#如何消除绘制图形缩放时抖动,总结
一.手动双缓冲 首先定义一个BitmapBitmap backBuffer = new Bitmap(画布宽度, 画布高度);然后获取这个Bitmap的GraphicsGraphics graphic ...
- 使用AdBlock plus屏蔽广告
使用前 使用后 定制规则 使用前 添加规则 id=1的为广告
- 用Eclipse进行远程Debug代码
在新的公司,由于项目很大,在本机运行会很慢,所以都是在本地开发,在远程虚拟机上运行.这样就让我痛苦了,我怎么在本地Eclipse上进行debug调试呢,但是在公司前辈的指导下让我知道了本地Eclips ...
- Linux下的service命令和chkconfig命令的原理
CentOS下的service命令和chkconfig命令的原理 1.service命令的原理 service命令用来对服务进行启动和关闭,比如service mysqld start可以启动mysq ...
- MySQL篇之Navicat可视化工具
主要内容: Navicat工具的介绍和使用 1.介绍和下载安装 <1>介绍 Navicat是一款针对MySQL数据库开发的可视化管理工具,以图形界面的形式操作MySQL数据库. 但在生产环 ...
- java创建TXT文件并进行读、写、修改操作
import java.io.*; /** * * 功能描述:创建TXT文件并进行读.写.修改操作 * * @author <a href="mailto:zha ...
- java 蓝桥杯算法提高 _3K好数
nums[i][j] 存的是i位数的时候,首位数字是j的K好数的数目,i从1位开始的结果,去算2位时的结果,去算3位时的结果...最后得到l位的结果.K进制只是一个范围. import java.ut ...