关于Fork函数的那些事(深入理解ECF)

Fork函数:调用系统的fork()指令,即父进程调用fork()从而创建一个新的子进程
关于系统的fork()指令

一次调用,两次返回。
返回值为0,说明此时在子进程中;返回值大于0,说明在父进程中,返回值为子进程的进程号(PID)

接下来看看其他系统指令:
getpid():

返回当前进程的PID

getppid():

返回当前进程父进程的PID

*waitpid(pid_t pid,int statusp, int options):

主要是等待子进程终止或者停止,并返回要等待并要被系统回收的子进程的PID
@若pid>0,等待集合是一个单独的子进程,进程ID等于pid
@若pid=-1,等待集合是由父进程所有的子进程组成
@@ 当options=0时,waitpid挂起调用进程的执行,直到它的等待集合(wait set)中的一个子进程终止
@@@如果调用进程没有子进程,那么waitpid返回-1,并且设置errno为ECHILD。
@@@如果waitpid函数被一个信号中断,那么它返回-1,并设置errno为EINTR

wait(&status):

相当于调用waitpid(-1,&status,0)

WIFEXITED(status):

如果子进程通过调用exit或者一个返回(return)正常终止,就返回真

WEXITSTATUS(status):

当WIFEXITED( )为真时,才可使用,并返回一个正常终止的子进程的退出状态

接下来用ubantu运行几个程序来理解Fork函数

前期准备:

1.http://csapp.cs.cmu.edu/public/code.html 下载.tar文件解压,包含了CSAPP书上的所有代码
2.在终端中键入文件移动命令:sudo mv csapp.h /usr/include和 sudo mv csapp.c /usr/include 更多的命令 mv – help
3.在gcc的时候最后加上-lpthread就可以

由于csapp.h里没有#include <csapp.c> 导致编译失败,应在文件的末端#endif 前加上即可

现在运行以下代码:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h> 
#include <signal.h>
void fork1()
{
    int x = 1;
    pid_t pid = fork();

    if (pid == 0) {
	printf("Child has x = %d\n", ++x);
    } 
    else {
	printf("Parent has x = %d\n", --x);
    }
    printf("Bye from process %d with x = %d\n", getpid(), x);
}
int main(int argc, char *argv[])
{
	fork1();
	 return 0;
}

运行结果:
在这里插入图片描述
父进程的输出是:x = 0
子进程的输出是:x = 2
并且能够看到父进程和子进程的进程编号,一个是18354,一个是18355

接下来是另一段代码:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h> 
#include <signal.h>
void fork2()
{
    printf("L0\n");
    fork();
    printf("L1\n");    
    fork();
    printf("Bye\n");
}
int main(int argc, char *argv[])
{
	fork2();
	 return 0;
}

运行结果:
在这里插入图片描述
为什么会有这样的运行结果呢?
现在通过思维导图的方式进行演示:
在这里插入图片描述
由此可以看出fork之后产生新的进程,并且子进程和父进程同时进行下一步的操作。

再来看一段代码:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h> 
#include <signal.h>
void fork4()
{
    printf("L0\n");
    if (fork() != 0) {
	printf("L1\n");    
	if (fork() != 0) {
	    printf("L2\n");
	}
    }
    printf("Bye\n");
}
int main(int argc, char *argv[])
{
	fork4();
	 return 0;
}

运行结果为:
在这里插入图片描述
对于输出结果是否会产生好奇?
不急,咱们看看思维导图:
在这里插入图片描述
对于Fork()函数而言,产生的子进程的pid为0(由前文的fork1可知),而原父进程的pid则是大于0的,故结合前文的fork2可得到相应的思维导图供理解。

接下来是另一段比较有趣的代码:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h> 
#include <signal.h>
void fork7()
{
    if (fork() == 0) {
	/* Child */
	printf("Terminating Child, PID = %d\n", getpid());
	exit(0);
    } else {
	printf("Running Parent, PID = %d\n", getpid());
	while (1)
	    ; /* Infinite loop */
    }
}
int main(int argc, char *argv[])
{
	fork7();
	 return 0;
}

其运行结果如图:
在这里插入图片描述
实际上这个程序不会结束,会陷入一个死循环,那么在Linux中我还想进行其他操作怎么办呢?
这个时候使用ctrl+z对其进行挂起或者ctrl+c强行终止,即可进行其他操作,如图中所示,我对他进行挂起。

用ps命令即可查看进程的信息,如图:
在这里插入图片描述
可以看到进程任在进行,但是这个时候子进程已经死了,形成一个僵死进程,但父进程还在运行,此时子进程无法被收回,并且由于父进程一直在运行,导致shell命令行不能够出现,若此时不进行进程挂起或强制退出则无法进行其他操作。

接下来看一段好玩的程序:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void){
	int i;
	for(i=0;i<2;i++){

		fork();
        printf("*");
	}
	return 0;
}

运行结果为:
在这里插入图片描述
那么为什么会输出8个呢?
看看流程图:
在这里插入图片描述
其罪魁祸首就在于标准I/O并未在fork时就输出,而存在于缓冲区中。在fork时,子进程自动继承了父进程的缓冲区,导致比预期多输出两个

通过这几个小程序可以帮助我们理解fork函数的有关知识,掌握fork函数是掌握异常控制流必备的一步,鄙人知识浅薄,分享就到这啦~ 我们下次再见!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值