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

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

发信人:scircle(yuanyuan),信区:Security标题:unix环境高级编程--第7章UNIX进程的环境发信站:BBS水木清华站(MonMar2715:53:352000)第七章〓Unix进程的环境〓引言下一章将介绍进程控制原语,在此之前需先了解进程的环境。在本章中我们将说明:当执行一通程序时,其main函数是如何被调用的,命令行参数是如何传送给执行程序的;典型的存储器布局是什么样式;如何分配另外的存储空间;进程如何使用环境变量;进程终止的不同方式等。另外,我们也将说明longjmp和setjmp函数以及它们与栈的交互作用。在本章结束之前,我们将查看进程的资源限制。〓main函数一道C程序总是从main函数开始执行。main函数的原型是:intmain(intargc,char*argv[]);其中,argc是命令行参数数,argv是指向参数的各个指针所构成的数组。在节中我们将对命令行参数进行说明。在系统核起动一道C程序时(用一个exec函数,我们将在节中说明exec函数),在调用main前先调用一个特殊的起动例程。可执行程序文件将此起动例程指定为程序的起始地址〖CD2〗这是由连接编辑程序设置的,而连接编辑程序则由C编译程序(通常是cc)调用。起动例程从系统核取得命令行参数和环境变量值,然后为调用main函数作好安排。〓进程终止有五种方式使进程终止:正常终止(a)从main返回(b)调用(c)调用迹茫模*常病絜异常终止(a)调用abort(第十章(b)由一个信号终止(第十章上节提及的起动例程是这样编写的,使得从main返回后即调用exit函数。如果将起动例程以C代码工表示(实际上该例程常常用汇编语言编写),则它调用main函数的形式可能是:exit和迹茫模*常病絜xit函数exit和迹茫模*常病絜xit函数正常终止一个程序:迹茫模*常病絜xit立即进入系统核,exit则先执行一些清除处理(包括的用执行各终止处P2程序,关闭所有标准I/O流等#includevoidexit(int#includevoid迹茫模*常病絜xit(int我们将在节讨论这两个函数对其它进程,例如终止进程的父、子进程的影响。使用不同头文件的原因是:exit是由ANSIC说明的,而迹茫模*常病絜xit则是由说明的。由于历史原因,exit函数总是执行一个标准I/O库的清除关闭操作:对于所有打开流调用fclose函数。回忆节,这造成所有在缓存中的数据都被刷新(写到文件上)。exit和迹茫模*常病絜xit都带一个整型参数,我们称此为终止状态。大多数UnixShell都提供检查一个进程终止状态的方法。如果(a)若调用这些函数时不带终止状态,或(b)main执行了一个无返回值的return语句,或(c)main执行隐式返回,则该进程的终止状态是末定义的。这就意味着,下列经典性的C语言程序#indudemain〓〓printf(hello,world\是不完整的,因为main函数没有使用return语句返回(隐式返回),它在返回到C的起动例程时并没有返回一个值(终止状态)。另外,若使用或者exit(0);则向启动执行此程序的进程(常常是一个shell进程)返回终止状态0。另外,main函数的说明实际上应当是:int在下一章,我们针会了解到一个进程如何一道程序执行,如何等待执行该程序的进程完成,然后取得其终止状态。将main说明为返回一个整型以及用exit代替return,对某些C编译程序和UnixLint(1)程序而言会产生不必要的警告信息。问题是这些编译程序并不了解在main中的exit与return语句的作用相同。警告信息可能是controlreachesendofnonvoidfunction。(控制到达非void函数的结束处),避开这种警告信息的一种方法是:在main中使用return语句而不是exit。但是这样做的结果是使我们不能用Unix。grep公用程序来找出一道程序中的所有exit调用。另外一个解决方法是将main说明为返回void而不是int,然后的旧调用exit。这也避开了编译程序的警告,但从程序设计角度看却并不正确。在本章中,我们将main表示为返回一个整型,因为这是ANSIC和所定义的。我们将不理会编译程序不必要的警告。atexit函数按照ANSIC的规定,一个进程可以登记多至32个函数,这些函数将由exit自动调用。我们称这些函数为终止处理程序,并用atexit函数来登记这些函数。#includeintatexit(voidReturns:0ifOK,nonzeroonerror返回:若成功为0,出错为非其中,参数(*func)(void)是一个函数地址,当调用此函数时无需向它传送任何参数,也不期望它返回一个值。exit以登记这些函数的相反顺序调用它们。同一函数如若登记多次,则也被调用多次。终止处理程序这一机制是由ANSIC新引进的。SVR4和都提供这种机制。系统V的早期版本和则都不提供此机制。按照ANSIC和,exit首先调用各终止处理程序,然后按需多次调用fclose,关闭所有打开流。图摘要显示了一个C程序是如何起动的,以及它终止的各种方式。图〓一个C程序是如何起动和终止的注意,系统核使一道程序执行的唯一方法是调用一种exec函数。一个进程自愿终止的唯一方法是显示或隐式地(调用exit)调用迹茫模*常病絜xit。一个进程也可非自愿地由一个信号使其终止(在图中没有显示)。实例程序说明了如何使用atexit函数。执行程序产生:$mainisfirstexitfirstexitsecondexit注意,在main中我们没有调用exit,而是用了return语句。程序〓终止处理程序的实例〓命令行参数当执行一个程序时,调用exec的进程可将命令行参数传递给该新程序。这是Unixshell的一部分常规操作。在前几章的很多实例中,我们已经看到了这一点。实例程序将其所有命令行参数都回送到标准输出上(Unixecho(1)程序不回送第0个参数。)编译此程序,并将其可执行代码文件定名为echoarg,则$arg1TESTargv[0]:argv[1]:argv[2]:argv[3]:程序〓将所有命令行参数回送到标准输出ANSIC和都要求argv[argc]是一个空指针。这就使我们可以将参数处理循环改写为:for(i=0;arg[i]〓环境表每个程序都接收到一张环境表。与参数表一样,环境表也是一个字符指针数组,其中每个指针包含一个以null符结束的字符串的地址。全局变量environ则包含了该指针数组的地址。externchar例如:如果该环境包含了五个字符串,那么它看起来可能如图中所示。图〓由五个字符串组成的环境其中,在每个字符串的结束处都有一个null字符。我们称environ为环境指针,指针数组为环境表,其中各指针指向的字符串为环境字符串。按照惯例,环境由这样的字符串组成,这与图中所示相同。大多数予定义名是完全由大写字母组成的,但这只是一个惯例。在历史上,大多数Unix系统对main函数提供了第三个参数,它就是环境表地址:intmain(intargc,char*argv[],char*envp[]因为ANSIC规定main函数只有两个参数,而且第三个参数与全局变量environ相比也没有带来更多益处,所以也规定应使用environ而不使用第三个参数。通常用getenv和putenv函数(在节中说明)来存取特定的环境变量,而不是用environ变量。但是,如果要查看整个环境,则必须使用environ指针。〓C程序的存储空间季知?nbsp;由于历史原因,C程序一直由下列几部分组成:·正文段。这是由CPU执行的机器指令部分。通常,正文段是可共享的,所以即使是经常执行的程序(文本编辑程序、编译程序、shell等)在存储器中也只需有一个副本。另外,正文段常常是只读的,以防止一道程序由于忌外子故而修改其自身的指令。·初始化数据段。通常将此段就称为数据段,它包含了程序中需赋初值的变量。例如:C程序中在任何函数之外的说明:int使此变量以初值存放在初始化数据段中。·非初始化数据段。通常将此段称为bss段,这一名称来源于早期汇编程序的一个操作符,意思是blockstartedbysymbol(由符号开始的块),在程序开始执行之前,系统核将此段初始化为0。在函数外的说明longsum[1000]使此变量存放在非初始化数据段中。·栈。自动变量以及每次函数调用时所需保存的信息都存放在此段中。每次函数调用时,其返回地址、以及调用者的环境信息(例如某些机器寄存器)都存放在栈中。然后,新被调用的函数在栈上为其自动和临时变量分配存储空间。以这种方式使用栈,C函数可以递归调用。·堆。通常在堆中进行动态存储分配。由于历史上形成的惯例,堆位于非初始化数段顶和栈底之间。图显示了这些段的一种典型安排方式。这是一个程序的逻辑布局一虽然并不要求一个具体实现一定以这种方式安排其存储空间。尽管如此,这给出了一个我们便于作有关说明的一种典型安排。图〓典型的存储器安排对于在VAX上的,正文段在0号单位开始,栈顶则在之下开始。在VAX机器上,在堆顶和栈底之间未用的虚地址空间是很大的。从图还可注意到末初始化数据段的内容并不存放在磁盘程序文件中。需要存放在磁盘程序文件中的段只有正文段和初始化数据段。size(1)命令报告正文段、数据段和bass段的长度(单位:字节)。例如:$size/bin/cctext〓data〓bss〓dec〓81920〓16384〓664〓98968〓18298〓90112〓16384〓0〓106496〓1a000〓第4和第5列是分别以十进制和十六进制表示的总长度。〓共享库现在,很多Unix系统支持共享库。Arnold[1986]说明了一个在系统V上共享库的一个早期实现,Gingell等[1987]则说明了一个在SunOs上的另一个实现。共享库使得可执行文件中不再需要包含常用的库函数,而只需在所有进程都可存访的存储区中保存这种库例程的一个副本。在程序第一次执行或者第一次调用每个库函数时,用动起连接方法将程序与共享库函数相连接。共享库的另一个优点是可以用库函数的新版本代替老版本而无需对使用该库的程序重新连接编辑。(假定参数数和类型都没有发生改变。不同的系统使用了不同的方法使程序可以说明是否要使用共享库。CC(1)和ld(1)命令的选择项是典型的。作为长度方面发生变化的例子,下列可执行文件(典型的程序)先用无共享库方式创建:$ls-1-rwxrwxr-x〓1〓stevens〓〓104859Aug〓214:25$sizetext〓data〓bass〓dec〓49152〓49152〓0〓98304〓如果我们再编译此程序使其使用共享库,则可执行文件的正文和数据段的长度都显著减少;$ls-1-rwxrwxr-x〓1stevens〓〓24576Aug〓214:26$sizetext〓data〓bass〓dec〓8192〓8192〓0〓16384〓〓存储器分配ANSIC说明了三个用于存储空间动态分配的函数malloc。分配指定字节数的存储区。在此存储区中的初始值是不确定的。calloc。为指定长度的对象,分配能容纳其指定个数的存储空间。该空间中的每一位(bit)都初始化为0。realloc。更改以前分配区的长度(增加或减少)。当增加长度时,可能需将以前分配区的内容移到另一个足够的大区域,而新增区域内的初始值则不确定。#includevoid*malloc(size迹茫模*常病健t—玸void*calloc(size迹茫模*常病健t—玭迹茫模*常病絫void*realloc(void迹茫模*常病絫Allthreereturn:nonnullpointerifOK,NULLon三个函数返回:若成功为非空指针,出错为voidfree(void这三个分配函数所返回的指针一定是适当对齐的,使其可用于任何数据对象。例如,在一个特定的系统上,如果最苛刻的对齐要求是double,则对齐

1 / 17
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功