当前位置: 首页 > news >正文

《UNIX高级环境编程》 第七章 进程环境 读书笔记

一、main函数

C程序总是从main函数开始执行,main函数的原型是:

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

正如前面提到的,argc是命令行参数的数目,argv是指向个参数的指针组成的数组。

当内核执行C程序时,在调用main前先调用一个特殊的启动例程。可执行文件将此例程指定为程序的起始地址--这是由编译器设置的,而连接编译器则由c编译器调用(通常是cc)。启动例程从内核取得命令行参数和环境变量值,然后按上述方式为调用main做好安排

二、进程终止

八种使进程终止的方式:

(1)从main返回(正常终止)

(2)调用exit(正常终止)

(3)调用_exit或_Exit(正常终止)

(4)最后一个线程从启动例程返回(正常终止)

(5)最后一个线程调用pthread_exit(正常终止)

(6)调用abort(异常终止)

(7)接收到信号并终止(异常终止)

(8)最后一个线程取消对请求做出的响应(异常终止)

1.exit函数

区别:_exit和_Exit函数立即进入内核,而exit则先执行一些清理处理(调用执行终止处理程序,关闭所有标准IO流等等),然后进入内核:

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

exit函数总是会调用fclose函数关闭已经打开的标准IO流。

status参数称为终止状态,如果调用时未指定终止状态,或mian执行了一个无返回值的return,或main没有声明返回的类型为整形,则该进程的终止状态使未定义的。可以使用命令:echo $?来打印命令的退出状态码

return 0;等价于exit(0);

2.atexit函数

按照ISO C规定,一个进程可以登记多达32个终止处理函数,调用atexit函数来登记终止处理函数。

#include <stdlib.h> int atexit(void (*func)(void));

- atexit函数的参数是一个函数地址,调用时不需要传递任何参数,不期待返回任何值。

- exit调用这些终止处理函数的顺序与登记顺序相反,重复登记一个函数会导致重复调用。

根据ISO C标准,exit首先调用各终止处理函数,然后按需调用fclose。

POSIX.1扩展了ISO C标准,若程序调用了exec家族的一个函数(其作用是在当前程序在exec调用处启动一个新的程序,旧的进程立即终止不再运行),则清除旧程序的所有终止处理函数,然后执行新程序

内核使程序执行的唯一方法是调用一个exec函数。进程资源终止的唯一方法是显式或隐式的调用_exit或_Exit。进程也可以非结缘的由一个信号使其终止。

例:

#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"); 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"); } static void my_exit2(void){ printf("Second exit handler"); }

最终打印:

main is done

first exit handler

first exit handler

second exit handleigemei

三、环境表

每个程序都有一个环境表,是一个字符指针数组,其中每个指针包含一个以null结束的c字符串的地址。全局变量environ包含了该指针数组的地址:

extern char **environ;

其中每个字符串结尾都显式的有一个null字符。我们称environ为环境指针,指针数组为环境表。

通常使用getenv和putenv函数来访问特定的环境变量(后面会讲到)

四、C程序的存储空间布局

1.代码段(正文段):储存程序指令,通常正文段是可共享的,所以即使多次调用的程序也只需要保存一个副本即可。正文段只读,防止程序意外修改自身指令。

2.初始化数据段(只读数据段+已初始化的数据段):只读数据段中存储字符串常量、const变量等,已初始化的数据段初始值来自可执行文件(例如c程序中出现在任何函数之外并且已赋初始值的声明)。

3.非初始化数据段(BSS段):在程序开始之前将此段内的数据初始化为0或空指针(例如c程序中出现在任何函数之外并且未赋初始值的声明)。

4.:自动变量(局部变量)以及每次函数调用时所需保存的信息(返回地址即函数结束后返回到哪、调用者环境即保存的寄存器值、传递给函数的参数、局部变量)都存放在此段中。每次调用函数时,返回的地址以及调用者的环境信息都存放在栈中。通过这种方式调用栈,可以递归调用c函数,递归函数每次调用自身时就使用一个新的栈帧,因此一个函数调用实例中的变量及不会影响另一个函数调用实例中的变量(一个栈帧对应一次调用,每个栈帧相互独立)。栈是编译器自动管理的。地址由高向低增长。

5.:进行动态内存分配,由程序员手动管理,地址由低向高增长,容易出现内存碎片、内存泄漏等问题。

典型的地址空间:

- 栈底和堆顶之间有很大的虚地址空间

- 有图中注意到,未初始化的数据段内容并不存放在磁盘上的程序文件中,因为内核在程序开始运行之前将它们都设置为0.需要存放在程序文件中的段只要正文段和初始化数据段。

- size命令查看程序的空间占用情况

五、存储器分配

ISO C说明了三个用于存储空间动态分配的函数。

(1)malloc分配指定字节数的存储区,此存储区中的初始值不确定。

(2)calloc为指定数量具有指定长度的对象分配存储空间,该空间中每一位都初始化为0

(3)realloc更改以前分配的长度(可增加或减少)。增加长度时可能会将目标移动到另一个足够大的区域,新增区的初始值不确定。

#include <stdlib.h> void *malloc(size_t size); void *calloc(size_t nobj,size_t size); void *realloc(void *ptr,size_t newsize);//newsize是指新的存储区的长度 //成功则返回非空指针,失败则返回NULL void free(void *ptr);

因为这三个函数都返回通用指针void*,所以如果在程序中包括了stdlib.h库(获得函数原型),则我们将返回指针赋予一个不同类型的指针时,就不需要显式的执行类型强制转换。

什么是内存对齐?

内存对齐是指数据在内存中的起始地址必须是某个值的整数倍,这个值由数据类型决定,这是硬件的要求,不是语言的规定。例如:

char c; // 1字节对齐(任何地址都可以) short s; // 2字节对齐(地址必须是2的倍数) int i; // 4字节对齐(地址必须是4的倍数) double d; // 8字节对齐(地址必须是8的倍数) long long l;// 8字节对齐

malloc返回的指针一定是适当对齐的,可以用于任何数据对象。这句话是什么意思?假设本系统最严格的要求是dobule类型(参考上述例子),为8字节对齐,那么这三个函数返回的指针就会自动对齐到8字节,这样对于char的1字节、short的2字节、int的4字节都可以对齐了,所以我们可以将返回指针赋予任何一个类型的指针。

free释放ptr指向的存储空间。被释放的空间通常被送入可用存储区池,以后可以再次进行动态分配。

realloc可以增加或减少原分配区的长度,例如,如果为一个数组分配存储空间(512),然后再运行过程中填充他,但一段时间后发现原先的长度不够,就可以调用realloc扩充相应空间。需要注意的是,不能使用realloc延长一个静态的数组(我们常用的int arr[10]={0}就是静态分配)

大多数实现所分配的空间比所要求的大一点,这是因为要有额外的空间来记录管理信息(分配块的长度、指向下一个分配块的指针等等)。这会导致一个严重的问题,假如有一个分配块已经写满,而程序继续向其中写入数据,那么数据就会写入下一个分配块的管理信息。

其他致命错误:释放一个已经释放了的块;调用free时所用的指针不是三个alloc函数的返回值等等。开辟的空间忘记释放,这种情况被称为泄露。

alloca函数:它的调用序列与malloc相同,但是它再当前函数的栈帧上分配空间。优点是:当函数返回时,自动释放它所使用的栈帧,不用手动释放。缺点是:alloca函数增加了栈帧的长度,而某些系统在函数已经被调用后就不能再增加栈帧,所以这些系统并不支持alloca函数。

http://www.jsqmd.com/news/79001/

相关文章:

  • VSCode 插件中集成大模型开发指南:AI 赋能编程体验 - 实践
  • [JSK]动态数列II
  • Markdown写作常用组件 - Invinc
  • 2025妈妈杯大材料竞赛A题mathorcup大素材:集装箱智能破损检测问题手把手思路代码文章教学大学生数学建模
  • 功耗网路签核工具大盘点
  • Krita架构解密:开源绘画软件如何实现商业级性能?
  • 19.redis之缓存击穿
  • 2025.12.12
  • 极市平台 | NeurlPS‘25开源 | 中科院新作AutoSeg3D:在线分割一切3D物体,超越ESAM!
  • APC001F
  • #题解#洛谷P1120 小木棍#搜索#剪枝
  • 云服务器的核心优势
  • 2025安全婴儿面霜测评:华西珐玛领衔,敏宝护理指南 - 资讯焦点
  • PyCausalSim:基于模拟的因果发现的Python框架
  • 爬youtube视频笔记
  • 使用vscode运行python,解释器为anaconda的虚拟环境,使用pip命令安装库失败解决方案
  • 某游戏大厂的常用面试问题解析:Netty 与 NIO - 指南
  • 软件工程学习日志2025.12.12
  • 云端算力:数字时代的核心引擎与创新基石
  • 家乐事净水器加盟费多少?0加盟费+装修补贴+区域保护,全程扶持解读 - 资讯焦点
  • 病毒学研究的关键工具:重组病毒蛋白的技术解析与应用实践
  • zz深入了解LlamaIndex实现Agent代码和原理
  • 搜维尔科技:Xsens独立项目-面向独立工作室的高端动作捕捉
  • 2025年度活动板房厂家TOP5实力评测:资质口碑场景适配全维度选型指南 - 资讯焦点
  • 解码智能指针
  • 毕业设计实战:基于SSM+MySQL的药店管理系统设计与实现,从需求到测试轻松通关!
  • linux: gdb调试器
  • 6 个最佳开源 AI 仪表盘工具
  • 红队日记 --- W1R3S
  • 深夜炸场!GPT-5.2发布;Meta被曝用阿里千问优化新模型;马斯克点赞腾讯游戏业务:他们的品味非常好 | 极客头条