目录
1.fork函数介绍
fork之前父进程独立执行,只有父进程没有子进程,fork之后,父子进程二个执行流分别执行,也就是说fork之后就有二个进程
进程具有独立性,代码和数据必须独立的,代码只能被读取,因为会产生写时拷贝
除了这些fork之后代码共享,那么fork之后,是不是只有fork之后的代码是被父子进程共享的,答案不是:fork之后,父子共享所有代码
但是子进程执行的后续代码 != 共享的所有代码,只不过子进程只能从这里开始执行
2.fork之后,操作系统做了什么?
首先我们知道
进程 = 内核的进程数据结构 + 进程的代码和数据
创建子进程的内核数据(struct task_struct + struct mm_struct + 页表 )+ 代码继承父进程,数据以写时拷贝的方式,来进行共享
这样子完成了独立性,因为这种办法,哪怕时子进程崩了,父进程也能单独运行
3.写时拷贝(深浅拷贝)
通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本
写时拷贝本身就有操作系统完成的
为什么要写时拷贝?
我们可以创建子进程的时候,就把数据分开,不行吗?
答案是可以但是
1.父进程的数据,子进程不一定全用,即使全用,也不一定全部写入,所以会有浪费空间的可能
2. 最理想的情况,只有会被父子修改的数据,进行分离拷贝,不需要修改的共享,但是从技术角度复杂
3. 如果fork的时候,就无脑拷贝数据给子进程,无疑会增加fork的成本(内存和时间)
4.fork失败的原因
1.系统有太多进程
2.实际用户的进程超过了限制
模拟进程创建失败代码
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
for(;;)
{
pid_t id = fork();
if(id < 0)
{
printf("创建子进程失败....\n");
break;
}
if(id == 0)
{
printf("I am a chile ... %d\n",getpid());
sleep(2);
exit(0);
}
}
return 0;
}
创建失败图片
5.进程终止
为什么return 0,其他可以吗?给谁return
常见进程退出
1.代码跑完,结果正确
2.代码跑完,结果不正确
3.代码没跑完,程序异常了
那么return0;表达的意思是代码跑完结果正确,returrn非零表示失败,这些return的值表示进程退出码
那么为什么用非零来表示失败,不能表示正确吗?
因为:非零表示不同的结果,失败有很多原因,但是成功的结果只有一个
而至于这个return的值是给父进程读取,让父进程知道这个进程运行成功或者失败
证明代码
int main()
{
return 123;
}
echo $? 这个命令,在bash中,最近一次执行完毕时,对应进程的退出码
那么再看一下下面结果
可以看到命令行退出码有特别的设定
那么我们1怎么知道命令退出码的意思
代码
int main()
{
int i = 0;
for(i=0;i<100;i++)
{
printf("%d; %s\n",i,strerror(i));
}
return 0;
}
结果
进程错误码显示不完全, 太多了
关于终止常见做法
1.在mian函数return
2.在自己的代码任意位置输入exit()
除了exit还有一个_exit,exit是终止进程并刷新缓冲区,_exit是终止进程,但不会刷新缓冲区
6.进程等待
1.为什么要进行进程等待
因为子进程退出,父进程如果不管不顾,就有可能造成僵尸进程的问题,进而造成内存泄漏
另外,进程一旦变成僵尸进程就无法被kill -9 杀死了
进程退出是有退出码的,而子进程return的进程码要被父进程给接受到,要不然父进程不知道子进程完成的怎么样
2.模拟进程等待
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t id = fork();
if(id == 0 )
{
while(1)
{
printf("我是子进程我正在运行...pid: %d\n",getpid());
sleep(1);
}
}
else
{
printf("我是父进程: pid: %d, 我准备等待子进程\n",getpid());
sleep(40);
pid_t ret = wait(NULL);
if(ret < 0)
{
printf("等待失败!\n");
}
else
{
printf("等待成功: result: %d\n",ret);
}
sleep(20);
}
return 0;
}
监测脚本
while :; do ps axj | head -1 && ps axj | grep myproc | grep -v grep; sleep 1; echo "####################"; done
上面代码的意思是
先打印父进程,然后到子进程运行,然后我们手动在40秒把子进程给杀了,那么子进程就是z僵尸状态,然后过了40秒后调用了wait函数把子进程给回收了
那么子进程就消失了
下面图片已经是杀掉了子进程,子进程从S状态变成了Z状态
父进程回收了子进程所以子进程消失了