栈帧:堆栈是C语言程序运行时必须的一个记录调用路径和参数的空间

函数调用框架、传递参数、保存返回地址、提供局部变量空间。

堆和栈的关系:

堆栈一般指的是栈;实际上堆和栈是不同的。

堆需要用户在程序中显性申请,空间由用户维护,释放空间不当会导致内存泄漏;但是栈不用,由系统自动完成。

堆的空间比较大,栈比较小。

栈的生命周期短,随着函数退出和返回消亡;堆的生命周期可以是整个程序执行完成退出,但是一般用户都会在使用时申请空间,不用时释放空间,防止空间消耗完毕。

在fork之后处理的文件描述符有两种常见的情况:

1、父进程等待子进程完成,在这种情况下,父进程无需对其描述符做任何处理。当子进程终止后,子进程对文件偏移量的修改以执行的更新。

2、父子进程各自执行不同的程序段。在这种情况下,父子进程各自关闭他们不需要使用的文件描述符,这样就不会干扰到对方使用文件描述符。(这种方法在网络服务进程经常使用)

父子进程之间的区别:

fork的返回值不同(子进程返回0,失败小于0,父进程大于0);进程ID不同(子进程是0,父进程未知);具有不同的父进程ID;子进程的tms_time、tms_stime、tms_cutime和tms_ustime均被置0;

父进程设置的文件锁不会被子进程继承;子进程的未处理闹钟被清除;子进程的未处理信号集被设置为空集。

fork的两个用法:

1、一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待用户端请求,生成子进程来处理请求。

2、一个进程要执行一个不同的程序。例如,子进程从fork返回后,调用exec函数。

wait和waitpid函数

一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致进程终止的信号是那个。这个进程的父进程就可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。

调用wait或waitpid可能会发生什么情况:

1、如果其所有子进程都还在运行,则阻塞。

2、如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回。

3、如果它没有任何子进程,则立即出错返回。

进程程序替换:

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新进程替换,从新进程的启动例程开始执行。调用exec并不创建新进程。所以调用exec前后该进程的id没有改变。

exec函数:

#include<unistd.h>

int execl(const char* path,const char* arg,...)

int execlp(const char* file,const char* arg, ...)

int execle(const char* path,const *arg,...,char*const envp[])

int execv(const char* path,char*const argv[])

int execvp(const char* file,char*const argv[])

int execve(const char* path,char*const argv[],char*const envp[])

这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回,如果调用出错则返回-1,所以exec函数只有出错才会有返回值,而成功是没有返回值的。