`
M_ittrue
  • 浏览: 77012 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

fork函数

 
阅读更多

fork,分叉之意,这是因为其一次调用,两次返回所造成的.先看看一个小程序的执行结果.


 

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	pid_t pid;
	char *message;
	int n;
	pid = fork();//fork调用
	if (pid < 0) {//调用出错
		perror("fork failed");
		exit(1);
	}
	if (pid == 0) {//子进程返回
		message = "This is the child\n";
		n = 6;
	} else {//pid>0,此时pid是子进程的pid,你进程返回
		message = "This is the parent\n";
		n = 3;
	}
	for(; n > 0; n--) {
		printf(message);
		sleep(1);//休眠1秒,cpu调度切换
	}
	return 0;
}

$ ./a.out 
This is the child
This is the parent
This is the child
This is the parent
This is the child
This is the parent
This is the child
$ This is the child
This is the child

 说明:

由于fork调用,由用户态代换到核心态,父进程产生一个子进程,进程拷贝父进程的相关状态与资源,并生成一个独有的pid标志.

此时父子进程都在核心态等态cpu调度返回,由于不同的cpu调度算法,哪个进程先返回是不一定的,把于输出也是不一样的.

这里是子进程返回,并先输出.但此时sleep(1),1秒对于cpu来说是一个很长的周期,这个时候cpu进行调度,回到使核心态的父进程返回并打印,就是这样周期切换调度创成这样的输出结果.

当最后的父进程结束时,父进程的父进程(shell进程),便输出,但这个时候子进程还没结束,所以子进程直接打印在了shell后.

下面这个图可以看一下这个"分叉"的过程.

fork

 

  1. 父进程初始化。

  2. 父进程调用fork,这是一个系统调用,因此进入内核。

  3. 内核根据父进程复制出一个子进程,父进程和子进程的PCB信息相同,用户态代码和数据也相同。因此,子进程现在的状态看起来和父进程一样,做完了初始化,刚调用了fork进入内核,还没有从内核返回

  4. 现在有两个一模一样的进程看起来都调用了fork进入内核等待从内核返回(实际上fork只调用了一次),此外系统中还有很多别的进程也等待从内核返回。是父进程先返回还是子进程先返回,还是这两个进程都等待,先去调度执行别的进程,这都不一定,取决于内核的调度算法。

  5. 如果某个时刻父进程被调度执行了,从内核返回后就从fork函数返回,保存在变量pid中的返回值是子进程的id,是一个大于0的整数,因此执行下面的else分支,然后执行for循环,打印"This is the parent\n"三次之后终止。

  6. 如果某个时刻子进程被调度执行了,从内核返回后就从fork函数返回,保存在变量pid中的返回值是0,因此执行下面的if (pid == 0)分支,然后执行for循环,打印"This is the child\n"六次之后终止。fork调用把父进程的数据复制一份给子进程,但此后二者互不影响,在这个例子中,fork调用之后父进程和子进程的变量messagen被赋予不同的值,互不影响。

  7. 父进程每打印一条消息就睡眠1秒,这时内核调度别的进程执行,在1秒这么长的间隙里(对于计算机来说1秒很长了)子进程很有可能被调度到。同样地,子进程每打印一条消息就睡眠1秒,在这1秒期间父进程也很有可能被调度到。所以程序运行的结果基本上是父子进程交替打印,但这也不是一定的,取决于系统中其它进程的运行情况和内核的调度算法,如果系统中其它进程非常繁忙则有可能观察到不同的结果。另外,读者也可以把sleep(1);去掉看程序的运行结果如何。

  8. 这个程序是在Shell下运行的,因此Shell进程是父进程的父进程。父进程运行时Shell进程处于等待状态,当父进程终止时Shell进程认为命令执行结束了,于是打印Shell提示符,而事实上子进程这时还没结束,所以子进程的消息打印到了Shell提示符后面。最后光标停在This is the child的下一行,这时用户仍然可以敲命令,即使命令不是紧跟在提示符后面,Shell也能正确读取。

 

    fork函数的特点概括起来就是“调用一次,返回两次”,在父进程中调用一次,在父进程和子进程中各返回一次。从上图可以看出,一开始是一个控制流程,调用fork之后发生了分叉,变成两个控制流程,这也就是“fork”(分叉)这个名字的由来了。子进程中fork的返回值是0,而父进程中fork的返回值则是子进程的id(从根本上说fork是从内核返回的,内核自有办法让父进程和子进程返回不同的值),这样当fork函数返回后,程序员可以根据返回值的不同让父进程和子进程执行不同的代码。

    fork的返回值这样规定是有道理的。fork在子进程中返回0,子进程仍可以调用getpid函数得到自己的进程id,也可以调用getppid函数得到父进程的id。在父进程中用getpid可以得到自己的进程id,然而要想得到子进程的id,只有将fork的返回值记录下来,别无它法。

fork的另一个特性是所有由父进程打开的描述符都被复制到子进程中。父、子进程中相同编号的文件描述符在内核中指向同一个file结构体,也就是说,file结构体的引用计数要增加。

 

   对于父进程pid我们可以用getppid()取得,而自身进程可以用getpid()取得,但子进程只能由返回取得,别无它法.


 

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,int *argv[])
{
	pid_t pid;
	char *msg;
	
	int n=0;
	printf("shell pid is %d\n",getppid());
	pid = fork();
	if(pid<0)
	{
		perror("fork failed");
		exit(0);
	}
	if(pid == 0)
	{
		printf("parent pid is %d\n",getppid());
		printf("childhem pid is %d\n",getpid());
		msg = "This is the child\n";
		n = 2;
	}else
	{
		msg = "This is the parent\n";
		printf("shell pid is %d\n",getppid());
		printf("parent pid is %d\n",getpid());
		printf("child pid is %d\n",pid);
		n = 1;
	}
	for(;n>0;n--)
	{
	//	printf(msg);
		//sleep(1);
	}
	return 0;
}
输出;
shell pid is 6279
shell pid is 6279
parent pid is 8669
child pid is 8670
parent pid is 8669
childhem pid is 8670


 

分享到:
评论

相关推荐

    linux_fork函数

    ### Linux中的`fork()`函数详解 #### 一、`fork()`函数基础介绍 在Linux操作系统中,`fork()`函数是一个非常重要的系统调用,用于创建一个新的进程。这个新进程被称为子进程,而调用`fork()`函数的原进程则称为父...

    创建新进程:fork函数:fork函数干什么? fork函数与vfork函数的区别在哪里?为何在一个fork的子进程分支中使用_exit函数而不使用exit函数?

    创建新进程:fork 函数 fork 函数是 UNIX 统操作系统中用于创建新进程的系统调用。它创建了一个完全相同的子进程副本,并返回一个进程标识符(PID)。fork 函数的返回值在父进程和子进程中不同:在父进程中,返回子...

    fork 函数详解

    fork 函数详解 fork 函数是 Unix 系统中一个非常重要的系统调用,它允许一个进程创建一个新的进程,该新的进程是原来进程的副本。fork 函数的返回值在父进程和子进程中不同,在父进程中返回新创建的子进程的进程 ID...

    fork函数和子函数进程

    ### fork函数和子进程知识点详解 #### 一、引言 在计算机编程中,特别是针对多任务处理领域,进程管理是实现系统并发的关键技术之一。其中,`fork()`函数是Unix/Linux系统中创建新进程的核心手段。通过深入理解`...

    linux内核 do_fork 函数源代码浅析

    ### Linux 内核 do_fork 函数源代码浅析 #### 一、引言 在 Linux 内核中,进程的创建是一项基本且重要的功能。本文将深入剖析 do_fork 函数,它作为核心组件之一,在 Linux 内核中扮演着创建新进程的关键角色。do_...

    演示fork函数的效果

    在"演示fork函数的效果"这个主题中,我们将深入探讨`fork()`的工作原理以及如何使用它。 首先,`fork()`函数的调用会返回两次:一次在父进程中,一次在子进程中。在父进程中,`fork()`返回新创建子进程的进程ID...

    c语言的fork函数笔记

    ### c语言中的`fork()`函数详解 #### 一、引言 `fork()`函数作为操作系统提供的核心系统调用之一,在进程管理和并发编程中扮演着重要角色。本文将深入探讨`fork()`函数的基本概念、工作原理及其在C语言中的具体应用...

    fork函数[归类].pdf

    《深入理解fork函数:进程创建的艺术》 fork函数在软件开发中扮演着至关重要的角色,尤其是在操作系统编程和并发处理中。它是Unix/Linux系统中创建新进程的主要方式。当我们谈论fork时,我们实际上是在讨论如何复制...

    实验二fork函数kkkkkkkkkkkkk

    实验二fork函数kkkkkkkkkkkkk

    fork函数使用

    关于fork函数的介绍,和测试程序,原版fork linux平台

    C语言的fork函数在Linux中的进程操作及相关面试题讲解

    2.fork 函数被调用一次,但返回两次, 两次返回的唯一区别是子进程的返回值是 0, 而父进程的返回值是新子进程的 PID。 3.子进程和父进程继续执行 fork 调用之后的指令。 在上图的存储空间布局中,父子进程只共享正文...

    进程控制 fork函数的使用等

    wait函数允许父进程挂起,直到一个子进程终止或者接收到信号,然后恢复并返回子进程的退出状态。 在实验中,通过fork系统调用,我们可以创建一个新的进程。在创建过程中,父进程会复制自身的内存空间,包括代码、...

    fork函数例程执行过程分析及说明

    `fork()`函数是Linux操作系统中用于创建新进程的关键系统调用。在本文档中,我们将深入探讨`fork()`的工作原理,以及如何通过示例代码理解其执行过程。 首先,让我们来了解一下`fork()`函数的基本概念。`fork()`会...

    linux下fork函数实例

    用fork创建子进程; 回答no执行父进程,yes执行父进程并复制进程的操作;在父进程将str3,str4接至str0中,并输出str0 ;在子进程中,将str1,str2接至str0中,并输出str0

    结合 linux 0.11 源码分析为什么 fork 函数会返回两个值

    在Linux操作系统中,`fork()`函数是一个非常关键的系统调用,它被用来创建一个新的进程——也就是我们常说的子进程。这个过程被称为进程复制,因为子进程会继承父进程的大部分属性,包括打开的文件、内存空间、信号...

    在win系统下模拟linux中的fork()函数执行过程与基础通信过程

    在win系统下模拟linux中的fork()函数执行过程与基础通信过程 备注清晰。

    fork函数的写时拷贝

    fork函数的写时拷贝技术 fork函数是一种用于创建子进程的系统调用,它在创建子进程时,会将父进程的虚拟存储空间复制给子进程的虚拟存储空间。这种机制使得父子进程可以共享文件等资源,但是为了避免多个进程之间的...

    fork函数中的内存复制和共享

    原来刚刚开始做Linux下面的多进程编程的时候,对于下面这段代码感到很奇怪:  #include  #include  #include  #include  #include  #include  #define LEN 2  void err_exit(char*fmt,…);...

    fork()函数

    本文档以ppt形式编写,通过例子,学习fork()函数的应用及用法,适合初学者。

Global site tag (gtag.js) - Google Analytics