栈帧:堆栈是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函数只有出错才会有返回值,而成功是没有返回值的。