“小忙”解释一下,三天没更博 是因为有事情在忙,据Keep APP不完整计程 + 不完整计时,三天骑行一百多公里,第一次觉得骑车真的太累了……
不过我又回来啦 今日份继续更博咯,开启正文叭!
往期回顾:
Part0. 实验环境
Part1-1.熟悉UKylin环境
Part1-2.熟悉UKylin环境
#include
#include
int main()
{
int pid;
pid = fork();
if(pid < 0)
printf("返回值为:pid = %d. Failed to create process. \n",pid);
else if(pid == 0)
printf("返回值为:pid = %d. I'm the child process. My Process ID is:%d. \n", pid, getpid());
else
{
wait(0);
printf("返回值为:pid = %d. I'm the parent process. My Process ID is:%d. \n", pid, getpid());
}
return 0;
}
运行结果:
分析:fork()函数创建一个子进程,该子进程复制父进程的PCB。当返回值为0时代表子进程,当返回值是大于0是代表父进程,返回值为负数时表示创建进程失败。上述代码创建进程的父进程的进程号为5252,子进程的进程号为5253。
fork( )
功能:创建一个新的子进程。其子进程会复制父进程的数据与堆栈空间,并继承父进程的用户代码、组代码、环境变量、已打开的文件代码、工作目录和资源限制。
系统调用格式:
int fork()
如果fork调用成功则在父进程会返回新建立的子进程标识符(PID),而在新建立的子进程中则返回0。如果fork失败则直接返回-1。
修改后代码:
#include
#include //fork() getpid()
#include //exit()
#include //wait()
int main()
{
int pid;
pid = fork();
if(pid < 0)
printf("返回值为:pid = %d. Failed to create process. \n",pid);
else if(pid == 0)
{
printf("返回值为:pid = %d. I'm the child process. My Process ID is:%d. \n", pid, getpid());
exit(0);
}
else
{
wait(0);
printf("返回值为:pid = %d. I'm the parent process. My Process ID is:%d. \n", pid, getpid());
}
return 0;
}
分析:调用了wait函数和exit函数,父进程等待子进程执行完再执行。
1、wait()
等待子进程运行结束。如果子进程没有完成,父进程一直等待。wait( )将调用进程挂起,直至其子进程因暂停或终止而发来软中断信号为止。如果在wait( )前已有子进程暂停或终止,则调用进程做适当处理后便返回。
2、exit()
终止进程的执行。通常父进程在创建子进程时,应在进程的末尾安排一条exit( )语句,使子进程自我终止。exit(0)表示进程正常终止,exit(1)表示进程运行有错,异常终止。
如果调用进程在执行exit( )时,其父进程正在等待它的终止,则父进程可立即得到其返回的整数。操作系统须为exit( )完成以下操作:
(1)关闭软中断
(2)回收资源
(3)写记帐信息
(4)置进程为“僵死状态”
1.进程的创建
编写一段程序,使用系统调用fork( )创建两个子进程,在系统中有一个父进程和两个子进程活动。让每个进程在屏幕上显示一个字符;父进程显示字符“f”,两个子进程分别显示字符“s” 和“d”。多次运行可执行程序,观察记录屏幕上的显示结果,并分析原因。画出进程树的结构图。
#include
#include
#include
#include
int main()
{
int p1,p2;
while((p1 = fork()) == -1); // 创建成功为止
if(p1 == 0) //如果是子进程
{
putchar('s');putchar('\n');
exit(0);
}
else // p1 > 0 父进程
{
while((p2 = fork()) == -1); // 创建新的进程-成功为止
if(p2 == 0) //p1 > 0 && p2 = 0
{
putchar('d');putchar('\n');
exit(0);
}
else //p1 > 0 && p2 >0
{
wait(0);
sleep(0);
putchar('f');putchar('\n');
}
}
return 0;
}
#include
#include
#include
#include
int main()
{
int p1,p2;
while((p1=fork()) == -1);
if(p1 == 0)
while(1) printf("--");
else
{
while((p2=fork()) == -1);
if(p2 == 0)
while(1) printf("l");
else
while(1) printf("f");
}
exit(0);
}
运行结果分析:
代码中创建了两个子进程,三个进程(父子进程)并发执行,由于调度顺序的不同,导致输出的结果是随机的。并发执行涉及到三个进程抢占处理机问题,只有占有CPU资源才能执行相应输出。
3.修改下面程序,将每个进程的输出由单个字符改为一句话,再观察程序执行时屏幕上出现的现象,并分析其原因。多次执行程序,观察结果是否一致;将循环次数增加到1000、1500,观察结果是否一致,并分析原因。画出进程树的结构图。
#include
#include
#include
#include
int main()
{
int p1,p2,i;
while((p1=fork()) == -1);
if(p1 == 0)
for(i = 0; i < 500; i++)
printf("Daughter.");
//printf("Daughter %d.\n",i);
else
{
while((p2=fork()) == -1);
if(p2 == 0)
for(i = 0; i < 500; i++)
printf("Son.");
//printf("Son %d.\n",i);
else
for(i = 0; i < 500; i++)
printf("Parent.");
//printf("Parent %d.\n",i);
}
return 0;
}
注:请自行修改参数为1000、1500,并观察执行结果。
运行结果分析:
由于输出行数很多,终端一页无法完全看到所有输出。在多次运行后,可以观察到,两个子进程(输出分别为“Daughter”和“Son”)的输出是随机的,在并发执行中,对进程的调度不确定。多次执行,结果不一定一致。
进程树:
4.编写程序创建进程树如图1和图2所示,在每个进程中显示当前进程标识符PID号和父进程标识符。
Note:程序设计思路和方法:
图1和图2 是两个结构不同的进程树,在进行代码编写时,需要依次进行以下几步:
①首先确定需要创建几个子进程,可以根据节点数,也可以根据边数(我们用的后者设计的),以图2为例,有5条边,需要创建5个子进程pi(i=1…5);
②进行逻辑划分:准确确定左右分支上的进程,左孩子划分在上一个进程调用fork函数返回值为0的分支中进行创建,右孩子划分在上一个进程调用fork函数返回值为大于0的分支中进行创建。
③以图2的a、b、d节点进程为例,调用fork()函数(p1 = fork()),如果返回值为0(p1=0),代表子进程b节点;否则(p1>0),调用fork()函数创建一个新的进程(p2 = fork()),如果返回值为0(p1>0&&p2 = 0),代表进程d节点,否则(p1 > 0 && p2 > 0),代表父进程a节点。
④每个节点代表的进程调用getpid()函数,显示当前进程标识符PID;
⑤每个节点的父进程标识符处理:在调试代码的过程中终端结果可能会出现多个父进程PID =1的情况,而不是预期的父进程PID,这是因为在代码执行过程中,提前终止了父进程,导致子进程变为“僵尸程序”或“孤儿程序”,由操作系统接管。
⑥输出正确的父进程的PID,通过调用getppid()函数实现,可以通过调用wait()函数或sleep()函数控制父子进程的先后输出顺序。
Point1: PID=1的情况:父进程先退出了,造成子进程被init(ID=1)接管,所以用getppid出来的是1.
最后在执行父进程的时候加了sleep就能保证父进程后退出。
附:https://blog.csdn.net/happyguys12345/article/details/52550505/
Point2: 一般进程、孤儿进程和僵尸进程 1)一般进程
正常情况下:子进程由父进程创建,子进程再创建新的进程。父子进程是一个异步过程,父进程永远无法预测子进程的结束,所以,当子进程结束后,它的父进程会调用wait()或waitpid()取得子进程的终止状态,回收掉子进程的资源。
2)孤儿进程 孤儿进程:父进程结束了,而它的一个或多个子进程还在运行,那么这些子进程就成为孤儿进程(father
died)。子进程的资源由init进程(进程号PID = 1)回收。 3)僵尸进程
僵尸进程:子进程退出了,但是父进程没有用wait或waitpid去获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中,这种进程称为僵死进程。附:https://blog.csdn.net/a13568hki/article/details/103851388
(1)图1代码+进程树
//代码一:利用wait()函数,等待子进程先执行,最后输出父进程,依次显示当前进程的PID和父进程的PID。
#include
#include
#include
#include
int main()
{
int p1,p2,p3;
printf("My PID is %d.\n",getpid());
while((p1=fork()) == -1);
if(p1 == 0)
{
while((p2=fork()) == -1);
if(p2 == 0)
{
while((p3=fork()) == -1);
if(p3 == 0)
{
printf("PID = %d. I'm the third child. My parent's PID is %d.\n",getpid(),getppid());
exit(0);
}
else //p2 == 0 && p3 > 0
{
wait(0);
printf("PID = %d. I'm the second child. My parent's PID is %d.\n",getpid(),getppid());
exit(0);
}
}
else //p1 == 0 && p2 >0
{
wait(0);
printf("PID = %d. I'm the first child. My parent's PID is %d.\n",getpid(),getppid());
exit(0);
}
}
else
{
wait(0);
printf("PID = %d. I'm parent.\n",getpid());
}
return 0;
}
//代码二:利用sleep函数,设置不同参数,使得依次输出父子进程,并显示当前进程PID和父进程的PID。
#include
#include
#include
#include
int main()
{
int p1,p2,p3;
printf("My PID is %d.\n",getpid());
while((p1=fork()) == -1);
if(p1 == 0)
{
while((p2=fork()) == -1);
if(p2 == 0)
{
while((p3=fork()) == -1);
if(p3 == 0)
{
sleep(0);
printf("PID = %d. I'm the third child. My parent's PID is %d.\n",getpid(),getppid());
exit(0);
}
else //p2 == 0 && p3 > 0
{
sleep(1);
printf("PID = %d. I'm the second child. My parent's PID is %d.\n",getpid(),getppid());
exit(0);
}
}
else //p1 == 0 && p2 >0
{
sleep(2);
printf("PID = %d. I'm the first child. My parent's PID is %d.\n",getpid(),getppid());
exit(0);
}
}
else
{
sleep(3);
printf("PID = %d. I'm parent.\n",getpid());
}
return 0;
}
#include
#include
#include
#include
int main()
{
int p1,p2,p3,p4,p5;
printf("My PID is %d.\n",getpid());
while((p1=fork()) == -1);
if(p1 == 0)
{
while((p3=fork()) == -1);
if(p3 == 0)
{
printf("PID = %d. C stands for me.My parent's PID is %d.\n",getpid(),getppid());
}
else
{
wait(0);
printf("PID = %d. B stands for me.My parent's PID is %d.\n",getpid(),getppid());
}
}
else
{
while((p2=fork()) == -1);
if(p2 == 0)
{
while((p4=fork()) == -1);
if(p4 == 0)
{
printf("PID = %d. E stands for me.My parent's PID is %d.\n",getpid(),getppid());
}
else
{
while((p5=fork()) == -1);
if(p5 == 0)
{
printf("PID = %d. F stands for me.My parent's PID is %d.\n",getpid(),getppid());
}
else
{
wait(0);
sleep(1);
printf("PID = %d. D stands for me.My parent's PID is %d.\n",getpid(),getppid());
}
}
}
else
{
wait(0);
sleep(2);
printf("PID = %d. I'm parent. A stands for me.\n",getpid());
}
}
return 0;
}
//注:也可以用sleep设置不同延时 进行输出
进程树:
5.在Linux系统中运行下面的程序,最多可产生多少个进程,试画出进程家族树。
(1)main(){ fork();fork(); }
详细过程的理解,附:https://www.jianshu.com/p/1327c51a4a99
(2)main(){ fork();fork();fork() }
当每个进程都创建子进程时,满足如下规律:
——————
The End!
那写看似毫无波澜的日复一日,会在某一天 让你突然发现努力的意义。 “小忙”加油!
无悔昨天 & 感谢今天 & 喜欢明天~