fork的一些用法
(程序员难免会写出一写带有bug的程序使得进程在运行时难免会挂掉)
守护进程:父进程创建子进程,让子进程执行业务(进程程序替换),父进程负责守护子进程。
当子进程在执行业务的时候“意外挂掉”,父进程重新启动子进程。让子进程提供服务。
提高程序“高可用”的一种手段
进程终止场景
正常
代码运行完毕,完成了既定的功能
代码运行完毕,但是没有完成既定的功能
可以通过 echo $? 查看进程退出码
1.从main函数的return 返回
2.调用库函数 exit
3.系统调用函数 _exit
_exit函数
#include <unistd.h>
void _exit(int status);
参数:status 定义了进程的终止状态,父进程通过wait来获取该值
exit函数
exit最后也会调用 _exit, 但在调用 _exit之前,还做了其他工作:
1. 执行用户通过 atexit或on_exit定义的清理函数。
2. 关闭所有打开的流,所有的缓存数据均被写入
3. 调用_exit
回调函数:程序读到时先是注册
等到程序要终止时回去调用
atexit(函数名)
异常终止
1.解引用空指针,解引用野指针
2.double free (已经释放的内存空间再次进行了释放)
3.内存访问越界
刷新缓冲区
为什么不显示hello呢?
\n 具有刷新缓冲区的作用,它会把我们缓冲区的字符刷新到屏幕上
刷新缓冲区的操作?
1./n
2.从main函数返回
3.exit函数
4.fflush(stdout);
全缓冲:当缓冲区写满了的时候,才进行IO行缓冲
行缓冲:在这种情况下,当在输入和输出中遇到换行符时 标准I/0库执行I/0操作
不缓冲:不带缓冲。标准I/0库不对字符进行缓冲存储
进程等待
之前讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。
另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法
杀死一个已经死去的进程。
最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,
或者是否正常退出。
父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
wait 函数
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
成功返回被等待进程pid,失败返回-1。
参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
wait 是阻塞调用 (死等直到等到为止)
pstack [进程号]
查看进程的堆栈信息,也就是看一下这一瞬间你的进程在干啥
waitpid
函数原型:
pid t waitpid(pid t pid, int *status, int options);
pid :
Pid = -l,等待任一个子进程。与wait等效
Pid > 0.等待其进程ID与pid相等的子进程
status : 退出状态信息 ==》等同于wait函数的参数
options :
WNOHANG:设置waitpid为非阻塞状态:若pid指定的子进程没有结束,则waitpid0 函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
结论:非阻塞调用一定要搭配循环使用
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;如果设置了选项WNOHANG,
而调用中waitpid发现没有已退出的子进程可收集,则返回0
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
waitpid 阻塞调用(就是我们说的死等,直到等到进程结束)
//子进程进入僵尸状态
非阻塞加循环
为啥进行程序替换
4.进程程序替换
4.0 为啥需要进程程序替换
因为父进程创建出来的子进程和父进程拥有相同的代码段, 所以, 子进程看到的代码和父进程是一样的。当我们想要让子进程执行不同的程序时候,就需要让子进程调用进程程序替换的接口,从而让子进程执行不一样的代码
exec函数簇
4.2 exec函数簇(代码怎么做)
int execl(const char *path,const char *arg...)
path : 带路径的可执行程序(需要路径)- 替换成为这个程序
arg : 给程序传递命令行参数的
1.第一个命令行参数可以是程序本身也可以不是
2.最后一个参数以NULL结尾
返回值:
替换成功加载新的程序执行新的代码
替换失败返回-1
int execlp(const char *file, const char *arg,参数:
file:可执行程序,可以不用带有路径,也可以带有路径
arg:传递给可执行的程序的命令行参数,第一个参数,需要可执行程序本身,如果需要传递多个参数, 则用“,”进行间隔, 末尾以NULL结尾
返回值:
这些函数如果调用成功则加载新的程序从启动代码开始执行如果调用出错则返回-1
int execle(const char *path, const char *arg,..., char*const envp[])
char** environ
参数: path: 带路径的可执行程序(需要路径)
arg: 传递给可执行的程序的命令行参数,第一个参数,需要可执行程序本身如果需要传递多个参数, 则用“,”进行间隔,末尾以NULL结尾
envp : 程序猿传递环境变量, 换句话说, 程序员调用该函数的时候,需要自己组织环境变量传递给函数返回值:
这些函数如果调用成功则加载新的程序从启动代码开始执行.如果调用出错则返回-1
int execv(const char *path, char *const argv ]
参数: path:带路径的可执行程序(需要路径)传递给可执行的程序的命令行参数,以指针数组的方式进行传递
argv :第一个参数,需要可执行程序本身,
多个参数就都放到数组当中
末尾以NULL结尾
返回值:
这些函数如果调用成功则加载新的程序从启动代码开始执行如果调用出错则返回-1