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

 更新时间:2020年4月25日 17:35  点击:2052

fork的意义

下图为,C 程序的存储空间布局(典型)

201668151756571.png (398×368)

1.一个现有进程可以调用 fork 函数创建一个新进程。
2.fork 函数被调用一次,但返回两次, 两次返回的唯一区别是子进程的返回值是 0, 而父进程的返回值是新子进程的 PID。
3.子进程和父进程继续执行 fork 调用之后的指令。
在上图的存储空间布局中,父子进程只共享正文段,其余的都各自有独立的副本 (通常使用 copy-on-write 的策略,速度比较快)。

fork 的两种用法
1.父子进程同时执行不同的代码段
典型应用:Web 服务器。
以下代码是简单的 fork 父子进程分别执行不同的代码:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#define err_sys(x) do { perror(x); exit(1); } while (0)

void invoke_child(char ch)
{
  printf("%c\n", ch);
}

int main(int argc, char *argv[])
{
  pid_t  pid;

  int   cnt = 3;
  char  arg[] = "abc";
  while(cnt--) {
    if ((pid = fork()) < 0) {
      err_sys("Fork error");
    } else if (pid == 0) {
      invoke_child(arg[cnt]);
      exit(0);
    }
  }

  return 0;
}

2.一个进程要执行一个不同的程序
典型应用:Shell。
这里就不举例子了~

关于fork的一道面试题
题目:请问下面的程序一共输出多少个“-”?

#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;
}

如果你对fork()的机制比较熟悉的话,这个题并不难,输出应该是6个“-”,但是,实际上这个程序会很tricky地输出8个“-”。

要讲清这个题,我们首先需要知道fork()系统调用的特性,

1.fork()系统调用是Unix下以自身进程创建子进程的系统调用,一次调用,两次返回,如果返回是0,则是子进程,如果返回值>0,则是父进程(返回值是子进程的pid),这是众为周知的。

2.还有一个很重要的东西是,在fork()的调用处,整个父进程空间会原模原样地复制到子进程中,包括指令,变量值,程序调用栈,环境变量,缓冲区,等等。

所以,上面的那个程序为什么会输入8个“-”,这是因为printf(“-”);语句有buffer,所以,对于上述程序,printf(“-”);把“-”放到了缓存中,并没有真正的输出(参看《C语言的迷题》中的第一题),在fork的时候,缓存被复制到了子进程空间,所以,就多了两个,就成了8个,而不是6个。

另外,多说一下,我们知道,Unix下的设备有“块设备”和“字符设备”的概念,所谓块设备,就是以一块一块的数据存取的设备,字符设备是一次存取一个字符的设备。磁盘、内存都是块设备,字符设备如键盘和串口。块设备一般都有缓存,而字符设备一般都没有缓存。

对于上面的问题,我们如果修改一下上面的printf的那条语句为:

printf("-n");

或是

printf("-");
fflush(stdout);

就没有问题了(就是6个“-”了),因为程序遇到“n”,或是EOF,或是缓中区满,或是文件描述符关闭,或是主动flush,或是程序退出,就会把数据刷出缓冲区。需要注意的是,标准输出是行缓冲,所以遇到“n”的时候会刷出缓冲区,但对于磁盘这个块设备来说,“n”并不会引起缓冲区刷出的动作,那是全缓冲,你可以使用setvbuf来设置缓冲区大小,或是用fflush刷缓存。

我估计有些朋友可能对于fork()还不是很了解,那么我们把上面的程序改成下面这样:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
  int i;
  for(i=0; i<2; i++){
   fork();
   //注意:下面的printf有“n”
   printf("ppid=%d, pid=%d, i=%d n", getppid(), getpid(), i);
  }
  sleep(10); //让进程停留十秒,这样我们可以用pstree查看一下进程树
  return 0;
}

于是,上面这段程序会输出下面的结果,(注:编译出的可执行的程序名为fork)

ppid=8858, pid=8518, i=0
ppid=8858, pid=8518, i=1
ppid=8518, pid=8519, i=0
ppid=8518, pid=8519, i=1
ppid=8518, pid=8520, i=1
ppid=8519, pid=8521, i=1

$ pstree -p | grep fork

|-bash(8858)-+-fork(8518)-+-fork(8519)---fork(8521)

面对这样的图你可能还是看不懂,没事,我好事做到底,画个图给你看看:

201668151954231.jpg (620×407)

注意:上图中的我用了几个色彩,相同颜色的是同一个进程。于是,我们的pstree的图示就可以成为下面这个样子:(下图中的颜色与上图对应)

201668152014108.jpg (437×97)

这样,对于printf(“-”);这个语句,我们就可以很清楚的知道,哪个子进程复制了父进程标准输出缓中区里的的内容,而导致了多次输出了。(如下图所示,就是我阴影并双边框了那两个子进程)

201668152030730.jpg (626×415)

[!--infotagslink--]

相关文章

  • C#启动进程的几种常用方法

    这篇文章主要介绍了C#启动进程的几种常用方法,实例分析了C#对系统进行的相关操作技巧,需要的朋友可以参考下...2020-06-25
  • C#获取所有进程的方法

    在本篇文章里小编给大家分享了关于C#获取所有进程的方法和步骤,有需要的朋友们跟着学习参考下。...2020-06-25
  • C#使用SendMessage实现进程间通信的方法

    这篇文章主要介绍了C#使用SendMessage实现进程间通信的方法,涉及C#中SendMessage方法的使用技巧,非常具有实用价值,需要的朋友可以参考下...2020-06-25
  • C#获取进程的主窗口句柄的实现方法

    C#获取进程的主窗口句柄的实现方法,需要的朋友可以参考一下...2020-06-25
  • C#中进程的挂起与恢复

    这篇文章主要介绍了C#中进程的挂起与恢复操作方法,非常不错,具有参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • 简单掌握Windows中C#启动外部程序进程的方法

    这篇文章主要介绍了Windows中C#启动外部程序进程的方法,例子中同时包括了进程关闭的方法,需要的朋友可以参考下...2020-06-25
  • 详解C语言进程同步机制

    这篇文章主要介绍了详解C语言进程同步机制的的相关资料,文中代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-06-18
  • 深入浅析WinForm 进程、线程及区别介绍

    进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。这篇文章主要介绍了WinForm 进程、线程的相关资料,需要的朋友可以参考下...2021-09-22
  • php安装pcntl扩展实现多进程

    pcntl中的php必须要安装pcntl才可以实现多线程了,在网上找到许多的关于pcntl安装教程,下面整理了一篇比较完整的关于php pcntl安装与使用方法。 pcntl中php实现多进...2016-11-25
  • php定时计划任务与fsockopen持续进程实例

    Web服务器执行一个PHP脚本,有时耗时很长才能返回执行结果,后面的脚本需要等待很长一段时间才能继续执行。如果想实现只简单触发耗时脚本的执行而不等待执行结果就直接执行下一步操作,可以通过fscokopen函数来实现。PHP支...2014-05-31
  • 判断指定的进程或程序是否存在方法小结(vc等)

    VC判断进程是否存在?比如我想知道记事本是否运行,要用到哪些函数等实例,需要的朋友可以参考下...2020-04-25
  • Asp.net core利用MediatR进程内发布/订阅详解

    这篇文章主要给大家介绍了关于Asp.net core利用MediatR进程内发布/订阅的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Asp.net core具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2021-09-22
  • C#获取进程和对进程的操作

    下面是一个例子:获取进程列表、创建“违禁”进程名单、查找并杀死进程。注意先要在项目里添加System.Management的引用。...2020-06-25
  • PHP框架Laravel中实现supervisor执行异步进程的方法

    这篇文章主要给大家介绍了PHP框架Laravel中实现supervisor执行异步进程的方法,文中介绍的非常详细,相信对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧。...2017-06-11
  • 用c语言实现HUP信号重启进程的方法

    本篇文章是对使用c语言实现HUP信号重启进程的方法进行了详细的分析介绍,需要的朋友参考下...2020-04-25
  • Redis fork进程分配不到内存解决方案

    这篇文章主要介绍了Redis fork进程分配不到内存解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2021-01-15
  • C#简单读取主机上所有进程的方法

    这篇文章主要介绍了C#简单读取主机上所有进程的方法,涉及C#进程的遍历读取操作相关实现技巧,需要的朋友可以参考下...2020-06-25
  • C#实现关闭其他程序窗口或进程代码分享

    这篇文章主要介绍了C#实现关闭其他程序窗口或进程代码分享,本文给出了两种方法,并分别给出示例代码,需要的朋友可以参考下...2020-06-25
  • C#网络编程基础之进程和线程详解

    这篇文章主要介绍了C#网络编程基础之进程和线程详解,本文对进程、线程、线程池知识做了浅显易懂的讲解,并配有代码实例,需要的朋友可以参考下...2020-06-25
  • [PHP]进程篇

    最近写了一个 监控在线进程的程序. 目的是为了 能实时监控执行程序是否断线,并及时重启。 用PHP写的,不算难,还成,但对于自己来讲挺有成就感的。 程序部分略省。 #将perl...2016-11-25