- 在终端1中多次运行
./pro
,发现当前进程PID一直在变,而父进程的PID没变过 - 父进程的PID为32452,在终端2中输入,
ps ajx | head -1 && ps ajx |grep 32452
指令
[yzq@VM-8-8-centos lesson]$ ps ajx | head -1 && ps ajx |grep 32452 PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND 907 3167 3166 907 pts/3 3166 R+ 1002 0:00 grep --color=auto 32452 32451 32452 32452 32452 pts/2 32452 Ss+ 1002 0:00 -bash
- 说明父进程PID 为 -bash
- bash为命令行解释器,本质上也是一个进程
命令行启动的所有程序,最终都会变成进程,而该进程对应的父进程都是bash
4. 为什么都是bash?
bash怕你写的代码有问题,所以使用bash创建的子进程完成任务,这样就算是挂了,bash也没事
4.指定进程暂停
- 在终端1中运行./pro,在终端2中输入
kill - 9+自己进程的PID
[yzq@VM-8-8-centos lesson]$ ./pro 我已经是一个进程了,PID为:29031,我的父进程PID为:28428 我已经是一个进程了,PID为:29031,我的父进程PID为:28428 我已经是一个进程了,PID为:29031,我的父进程PID为:28428 我已经是一个进程了,PID为:29031,我的父进程PID为:28428 我已经是一个进程了,PID为:29031,我的父进程PID为:28428 我已经是一个进程了,PID为:29031,我的父进程PID为:28428 我已经是一个进程了,PID为:29031,我的父进程PID为:28428 我已经是一个进程了,PID为:29031,我的父进程PID为:28428 我已经是一个进程了,PID为:29031,我的父进程PID为:28428 我已经是一个进程了,PID为:29031,我的父进程PID为:28428 我已经是一个进程了,PID为:29031,我的父进程PID为:28428 我已经是一个进程了,PID为:29031,我的父进程PID为:28428 我已经是一个进程了,PID为:29031,我的父进程PID为:28428 我已经是一个进程了,PID为:29031,我的父进程PID为:28428 Killed
- 在终端2中输入
kill - 9 29031
,即可在终端1中显示killed
,表示结束
5.如何创建子进程
创建子进程—— fork
,头文件为<unistd.h> ,返回值是 pid_t类型
#include<stdio.h> 2 #include<sys/types.h> 3 #include<unistd.h> 4 int main() 5 { 6 printf("AAAA\n"); 7 fork(); 8 printf("BBBB\n"); 9 sleep(1); 10 return 0; 11 }
- 继续在终端1中修改pro.c文件中的内容如上
[yzq@VM-8-8-centos lesson]$ ./pro AAAA BBBB BBBB
- 运行pro可执行程序,发现竟然执行两次BBBB
这是为什么呢?我们继续往下看
#include<stdio.h> 2 #include<sys/types.h> 3 #include<unistd.h> 4 int main() 5 { 6 printf("AAAA\n"); 7 fork(); 8 printf("BBBB:pid:%d,ppid:%d\n",getpid(),getppid()); 9 sleep(1); 10 return 0; 11 }
- 修改por.c文件的内容,加上自己和父进程的PID值
[yzq@VM-8-8-centos lesson]$ ./pro AAAA BBBB:pid:4285,ppid:31919 BBBB:pid:4286,ppid:4285
- 终端1中
./pro
运行可执行程序,两个执行B的printf语句打印自己进程的PID值不同,说明是两个进程 - 而下面BBBB的父进程PID与上面BBBB的子进程PID相同,说明创建了子进程
1. fork返回值
- 父进程返回子进程的PID值,子进程返回0,失败返回-1
1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<unistd.h> 4 int main() 5 { 6 printf("AAAA\n"); 7 pid_t ret= fork(); 8 printf("BBBB:pid:%d,ppid:%d,%d,%p\n",getpid(),getppid(),ret,&ret); 9 sleep(1); 10 return 0; 11 }
- 修改pro.c文件内容,加上ret的值和地址
[yzq@VM-8-8-centos lesson]$ ./pro AAAA BBBB:pid:7799,ppid:31919,7800,0x7ffefc72c02c BBBB:pid:7800,ppid:7799,0,0x7ffefc72c02c
在终端1中运行./pro
,上面的BBBB,ret值返回是下面BBBB的PID值 ,说明是父进程
而下面的BBBB,ret值为0,说明是子进程
2.使父子进程执行不同的任务
#include<stdio.h> 2 #include<sys/types.h> 3 #include<unistd.h> 4 int main() 5 { 6 pid_t ret= fork(); 7 if(ret==0) 8 { 9 //子进程 10 while(1) 11 { 12 printf("我是子进程,我的pid是:%d,我的父进程是:%d\n",getpid(),getppid()); 13 sleep(1); 14 } 15 16 } 17 else if(ret>0) 18 { 19 //父进程 20 while(1) 21 { 22 printf("我是父进程,我的pid是:%d,我的父进程是:%d\n",getpid(),getppid()); 23 sleep(1); 24 } 25 } 26 else 27 { //报错 29 } 30 return 0; }
- 修改pro.c文件的内容,设置if else语句实现
[yzq@VM-8-8-centos lesson]$ ./pro 我是父进程,我的pid是:13505,我的父进程是:31919 我是子进程,我的pid是:13506,我的父进程是:13505 我是子进程,我的pid是:13506,我的父进程是:13505 我是父进程,我的pid是:13505,我的父进程是:31919 我是子进程,我的pid是:13506,我的父进程是:13505 我是父进程,我的pid是:13505,我的父进程是:31919 我是父进程,我的pid是:13505,我的父进程是:31919 我是子进程,我的pid是:13506,我的父进程是:13505
父进程和子进程是同时运行的
说明在多执行流的环境下 if和else if可以同时成立
3. 结论
- fork之后,执行流会变成2个
- fork之后,谁先运行由调度器决定
- fork之后,fork之后的代码共享,通常通过if和else if来进行执行流分流
6. fork 原理
1.fork做了什么
子进程pcb的大部分属性会以父进程pcb为模板,把父进程大部分里面的数据拷给子进程
小部分属于子进程私有的,例如PID、PPID值
因为进程等于数据结构+代码和数据,所以父进程指向自己的代码和数据,子进程也会指向同样的代码和数据
创建子进程:创建独立的pcb结构,父子进程看到的是同一份代码和数据
2.fork 如何看待代码和数据
当我们把画图关闭后,并不会影响有道云笔记的使用,说明他们都是独立存在的
进程在运行的时候,是具有独立性的
当我们在执行代码同时运行父子进程时,若使用 kill- 9 干掉父进程后,子进程仍能运行
父子进程在运行时,也是具有独立性的
父子进程指向同一块代码和数据,独立性如何保证?
代码:
代码在内存区域是只读的(从来不会自己发生变化,不会有人修改)
父子进程两者都读,不会互相影响
数据:
1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<unistd.h> 4 int main() 5 { 6 int x=100; 7 pid_t ret= fork(); 8 if(ret==0) 9 { 10 //子进程 11 while(1) 12 { 13 printf("我是子进程,我的pid是:%d,我的父进程是:%d,%d\n",getpid(),getppid(),x); 14 sleep(1); 15 } 16 17 } 18 else if(ret>0) 19 { 20 //父进程 21 while(1) 22 { 23 printf("我是父进程,我的pid是:%d,我的父进程是:%d,%d\n",getpid(),getppid(),x); 24 x=50; 25 sleep(1); 26 } 27 } 28 return 0; 29 }
- 在终端1中修改pro.c文件的内容
[yzq@VM-8-8-centos lesson]$ ./pro 我是父进程,我的pid是:26332,我的父进程是:21231,100 我是子进程,我的pid是:26333,我的父进程是:26332,100 我是父进程,我的pid是:26332,我的父进程是:21231,50 我是子进程,我的pid是:26333,我的父进程是:26332,100 我是父进程,我的pid是:26332,我的父进程是:21231,50 我是子进程,我的pid是:26333,我的父进程是:26332,100 我是父进程,我的pid是:26332,我的父进程是:21231,50 我是子进程,我的pid是:26333,我的父进程是:26332,100
使用./pro
执行可执行程序,修改父进程中的x值后,只有父进程的x值被修改,子进程x值不变
说明如果有一个进程把数据改了,并不会影响另一个进程
当有一个执行流尝试修改数据的时候,操作系统自动给当前进程触发:写时拷贝4
3.fork如何理解两个返回值问题
- 当我们函数内部准备执行return的时候,我们的主体功能已经完成
- fork本质上是操作系统提供的一个创建子进程的函数
- 所以当到return时,说明创建子进程已经完成了,return语句,父进程会执行一次,子进程执行一次,共执行两次