APUE进程环境

main函数如何被调用
内核通过调用execve系统调用来调用加载器。
加载器在可执行目标文件中的段头表的指导下,把可执行目标文件的相关内容拷贝到代码和数据段。接着加载器跳转到程序的入口点,也就是符号_start的地址。
在_start符号的地址处,有一段启动代码叫做启动例程(这个在APUE中也有讲解),这段启动代码是在ctrl.o目标文件中定义的,对于所有的C语言程序都是一样的。
启动例程从内核取得命令行参数和环境变量值。
调用初始化代码后,启动例程调用atexit例程,这个例程用于统计注册的函数。(详见APUE的讲解)
让后调用应用程序的main程序,这就开始执行我们的C代码了。
在应用程序返回之后,启动代码调用_exit程序,他将控制返回给操作系统。
0x080480c0 <_start>:               /* entry point in .text */
     call __libc_init_first         /* startup code in .text */
     call _init                     /* startup code in .init */
     call atexit                    /* startup code in .text */
     call main                      /* application main routine */
     call _exit                     /* returns control to OS */
                                 /* control never reaches here */
详见http://learn.akae.cn/media/ch19s02.html   
我们说main函数是程序的入口点其实不准确,_start才是真正的入口点,而main函数是被_start调用的。
为什么每个C程序都需要一个叫main的函数?
因为C的启动例程对于每个C程序而言都是相同的,要跳转到一个叫main的函数。
为什么C的main函数可以通过调用exit或者执行一条return语句,或者两者都不做,而程序仍然能够正确终止吗?
如果main以return语句结束,控制传到启动例程,它调用_exit再将控制权返回给操作系统。
如果省略return仍然相同。如果调用exit终止,那么exit将最终通过调用_exit系统调用将控制权返回给操作系统。
所以无论如何控制权都会返回给操作系统。


命令行参数如何传递给执行程序
通过调用exec函数族的函数可以讲命令行参数和环境变量传递给新程序。详见APUE第八章

进程如何使用环境变量
环境表:ertern char **environ;
libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时要用extern声明。
环境变量是被继承的;每一个进程有一个不同的环境变量表拷贝结果是,你不能从一个其他进程改变当前进程的环境变量,比如shell进程。
可以通过getenv和putenv函数来访问特定的环境变量,而不是通过environ变量。但是,如果要查看整个环境变量,则必须使用environ指针。
环境表和环境字符串通常存储在进程存储空间的顶部,删除一个很简单,但插入时就要考虑空间的问题。

C程序的存储空间的布局
详见《链接的学习》

存储空间的分配
#include <stdlib.h>
void *malloc(size_t  size );
void *calloc(size_t  nobj , size_t  size );
void *realloc(void * ptr , size_t  newsize );
                                                                       All three return: non-null pointer if OK,  NULL  on error
void free(void * ptr );
这些分配例程都是sbrk系统调用的封装,该系统调用用于扩充或缩小进程的堆。
应该注意的是,大多数实现所分配的存储空间都比所要求的稍微大一些,额外的空间用来记录管理信息--分配块的大小,指向下一个分配块的指针等。
这就意味着如果超过了一个已经分配区的尾端进行写操作,则会重写后一个块的管理记录,一定要避免,否则是灾难性的。
还有一个问题,对调用malloc 函数后应该调用free函数,防止内存泄漏。
函数alloca,该函数是在当前函数的栈帧上分配空间,而不是堆中。
其优点是当函数返回时,自动释放分配的空间。缺点是alloca函数增加了栈的长度,而某些系统不允许函数被调用后增加栈的长度,这就影响了可移植性。

进程的退出 exit 函数
进程有 5 种正常终止的方式和 3 种异常终止的方式。但是不管进程如何终止都会执行内核中一段相同的代码。这段代码为相应的进程关闭所有打开描述符,释放它所使用的存储器等。
正常1:从main函数返回return。---------正常死亡
正常2:调用exit函数。-----------------自动死亡,留下遗嘱
正常3:调用_exit或_Exit函数。---------自动死亡,没有遗言
其目的是为进程提供一种无需运行终止处理程序或信号处理程序而终止的方法。
正常4:进程的最后一个线程在其启动例程中执行返回语句。但是该线程的返回值不会作为进程的返回值,进程以终止状态0返回;
正常5:进程的最后一个线程调用pthread_exit函数。此时,进程的终止状态总是为0,而与传送给pthread_exit函数的参数没有关系。
异常1:调用abort。----------自杀
产生SIGABRT信号;
异常2:进程收到某些信号。----被杀
此信号可以有自身、其他进程或者内核产生;
异常3:最后一个线程对“取消”请求做出响应。

内核使程序执行的唯一方法是调用一个exec函数。
进程自愿终止的唯一方式是显示或隐式的调用exit/_exit/_Exit。
进程也可以非自愿的有一个信号时期终止。
atexit函数登记终止处理程序exit handler
main函数的返回值http://blog.csdn.net/fangfei_119/archive/2008/04/07/2256656.aspx


setjmp和longjmp函数
这两个函数用于处理发生在深层嵌套函数调用中出错的情况。
#include<setjmp.h>
int setjmp(jum_buf env);              两个返回值,一个是函数本身执行的返回值,一个是longjmp的返回值
void longjmp(jmp_buf env, int val);   无返回值
因为需要在不同的函数中跳跃,故规范的处理方式是将env变量定义为全局变量。
longjmp设置第二个参数的原因是一个setjmp可以对应多个longjmp函数,故需要longjmp来指定返回值。
当调用longjmp返回时,变量的值有可能会回滚,这取决于两个方面:
1,当没有进行优化时,全局,静态,易失,自动,寄存器变量类型都不会回滚
2,当进行优化时,全局,静态,易失变量类型会回滚,而自动和寄存器类型不会回滚
主要原因是register和auto类型在优化后会放入寄存器中,而不是存放在内存中,longjmp调用时寄存器值入栈,longjmp返回时,值就回滚了。
题外话:简单介绍一下volatile关键字
volatile关键字用来声明那些可以被编译器未知的因素更改的变量类型,
用volatile声明的变量每次被访问时,执行部件都会从内存单元中去内容,
而没有声明为volatile的类型可能有于编译器优化的原因而从寄存器中取值,
为了编制一个使用非局部跳转的可移植程序,则必须使用volatile关键字。
http://blog.chinaunix.net/u/21277/showart_238959.html

进程资源
每个进程都有一组资源限制,其中一些可以使用getrlimit和setrlimit函数查询和更改。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值