APUE第七章 进程环境

引言

  • 当程序执行时,其main函数是如何被调用的–内核调用启动例程执行exec函数再执行main函数内容。
  • 命令行参数是如何传递给新程序的–调用exec的进程可将命令行参数传递给该新程序
  • 典型的存储空间布局是什么样式–从低到高:1.正文段:CPU执行的机器指令部分;2.初始化数据段:程序中明确赋初值的变量;3.未初始化数据段:变量已定义但并未赋初值;4.栈;5:堆;6:命令行参数和环境变量
  • 如何分配另外的存储空间–alloc,malloc,realloc
  • 进程如何使用环境变量
  • 进程的各种不同终止方式
  • longjmp和setjmp以及它们与栈的交互作用
  • 查看进程的资源限制

main函数

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

C程序总是从main函数开始执行,argc是命令行参数的数目,argv是指向参数的各个指针所构成的数组,也就是各个参数的地址。
当内核执行C程序时,在调用main函数之前先调用一个特殊的启动例程。启动例程从内核取得命令行参数和环境变量值。

进程终止

正常终止

  • 从main返回
  • 调用exit
  • 调用_exit或_Exit
  • 最后一个线程从其启动例程返回
  • 从最后一个线程调用pthread_exit

异常终止

  • 调用abort
  • 接到一个信号
  • 最后一个线程对取消请求做出相应

退出函数

#include <stdlib.h>
void exit(int status);
void _Exit(int status);

#include <unistd.h>
void _exit(int status);

参数status称为终止状态,如果main函数中没有声明返回类型为整型,那么该进程的终止状态是未定义的。例如

main(){
    printf("kk");
}

终止状态是未定义的。而又如

int main(){
    printf("kk");
    return 0;
}

则该进程的终止状态为0。

exit函数总是执行一个标准I/O库的清理关闭操作:对于所有的打开流调用fclose函数–》这意味着exit将冲洗缓冲的数据,使得内核中数据悉数写入磁盘文件中。其实在main函数exit(0)和return(0)是等价的

函数atexit

一个进程可以登记多至32个函数,这些函数将由exit自动调用。我们将这些函数称为终止处理程序,并调用atexit来登记这些函数。我们可以将希望程序退出时要做的工作登记在atexit中,并在执行exit函数时调用这些终止处理程序。

#include <stdlib.h>
int atexit(void (*func)(void));
//参数为一个函数地址

atexit函数调用被登记的函数时,既不需要给被调用的函数传递一个参数,也不期望被调用的函数返回一个值。注意:atexit登记终止处理函数的顺序与它调用终止处理函数的顺序相反。
atexit函数的实例如下:

#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"); //登记终止处理程序my_exit2

    if(atexit(my_exit1) != 0) 
        err_sys("can't register my_exit1");
    if(atexit(my_exit1) != 0)
        err_sys("can't register my_exit1");

    printf("main is done\n");
    return 0;

}

static void my_exit1(void){
    printf("first exit handler\n");
}
static void my_exit2(void){
    printf("second exit handler\n");
}

结果:
main is done
first exit handler
first exit handler
second exit handle

命令行参数

当执行一个程序时,调用exec的进程可将命令行参数传递给该新程序。

环境表

每个程序都接收到一张环境表,与参数表一样,环境表也是一个字符指针数组,其中每个指针包含一个以NULL结尾的C字符串的地址。
环境指针–环境表–表项为环境字符串(环境变量)

int main(int argc, char **argv, char **envp);
//envp就是环境表地址

C程序的存储空间布局

  1. 正文段:由CPU执行的机器指令部分。通常,正文段是共享的,而且常常是只读的,防止程序由于意外而修改其指令。
  2. 初始化数据段:包含程序中需明确地赋初值的变量。例如int maxcount = 9,便使此变量以其初值存储在初始化数据段中。
  3. 未初始化数据段:在程序开始执行之前,内核将此段中的内容初始化为0或空指针:long sum[100],使次变量存储在未初始化数据段中。
  4. 栈:存放自动变量以及每次函数调用时所需保存的信息。每次函数调用时,其返回地址以及调用者的环境信息都存放在栈中。
  5. 堆:通常在堆中进行动态存储分配。堆位于未初始化数据段与栈之间。

共享库

1.共享库使得可执行文件中不再需要包含公用的库函数,而只需在所有进程都可引用的存储区中保存这种库例程的一个副本。程序第一次执行或者第一次调用某个库函数时,用动态链接方法将程序与共享库函数相链接–>减少了可执行文件的长度。
2.库函数进行更新之后,无需对使用该库的程序重新连接编辑。

存储空间分配

存储空间动态分配的函数

#include <stdlib.h>

void *malloc(ssize_t size); //分配指定字节数的存储区,此存储区中初值不确定
void *calloc(ssize_t nobj, size_t size);//为指定数量指定长度的对象分配存储空间,该空间的每一位(bit)都初始化为0 
void *realloc(void *ptr, size_t newsize);//增加或减少以前分配区的长度

void free(void *ptr);

这些分配内存的例程通常用sbrk(2)实现,该系统调用扩充(或缩小)进程的堆。
在动态分配的缓冲区前或后进行写操作,破坏的可能不仅仅是该区的管理记录信息,因为在动态分配的缓冲区前或后可能用于其他动态分配的对象。
例:

#include <stdlib.h>
int main(){
    char *p = malloc(5); //为p分配5字节的内存空间
    p = "123"; //使p指向字符串123的首地址
    free(p); //这里free会报错
//在malloc(5)执行后的确为p分配了一个为5的内存空间,记此时p的地址为a,但是p="123"之后p指向了"123"的首地址,记这个地址为b。要知道a是不等于b的,而且b不是动态分配的内存空间,因此无法通过free释放掉。
//关键在于p="123",并没有在分配给p的5个字节内存空间上赋值
//对比
int main(){
     char *p = malloc(2);
     p[0] = 'a';
     p[1] = 'b';

}

环境变量

shell使用大量的环境变量来控制shell的动作,例如环境变量MAILPATH规定了shell程序到哪里去查看邮件。
环境字符串的形式为
name = value

#include <stdlib.h>
char *getenv(const char *name);

getenv可以取环境变量值,返回的指针指向name=value中的value,也就是环境变量值的地址。

#include <stdlib.h>
int putenv(char *str);
int setenv(const char *name, const char *value, int rewrite);
int unsetenv(const char *name);
  • putenv将name=value字符串放到环境表中,若重复直接覆盖;
  • setenv将name=value字符串放到环境表中,若重复且rewrite非0,则覆盖;若重复但rewrite为0,则不覆盖,而且不设置name=value,也不出错。

函数setjmp和longjmp

#include <setjmp.h>
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);

在程序中希望返回的位置调用setjmp,再调用longjmp使得程序返回setjmp的位置。

注意:若自动变量(例如 int a;a=5;)在setjmp之前赋初值,此时跳转执行另一个函数对自动变量进行修改(a=3;),然后调用longjmp返回setjmp的位置。这种情况下自动变量会保持a=3的值(gcc -O带优化编译的条件下),若不带优化,跳转回去之后自动变量会回滚至a=5。
如果有一个自动变量,且跳转之后不希望其值回滚,那么可定义为具有volatile属性,即volatile int a = 5;声明为全局变量或静态变量之后执行longjmp值不会改变。

函数getrlimit和setrlimit

#include <sys/resource.h>

int getrlimit(int resource, struct rlimit *rlptr);
int setrlimit(int resource, const struct rlimit *rlptr);

每个进程都有一组资源限制,可以用getrlimit查询,用setrlimit修改。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值