Linux中使用C语言的fork()函数创建子进程示例教程

 2024-03-01 03:03:16  阅读 0

1.fork简介

一个进程,包括分配给该进程的代码、数据和资源。 fork()函数通过系统调用创建一个与原进程几乎一模一样的进程,即两个进程可以做一模一样的事情,但如果初始参数或传入的变量不同,则两个进程还可以做不同的事情。 。

进程调用fork()函数后,系统首先为新进程分配资源,例如存储数据和代码的空间。 然后将原进程的所有值复制到新进程中,除了少数与原进程的值不同的值。 相当于克隆自己。

让我们看一个例子:

#include  
#include   
int main ()  
{  
  pid_t fpid; //fpid表示fork函数返回的值 
  int count=0; 
  fpid=fork();  
  if (fpid < 0)  
    printf("error in fork!");  
  else if (fpid == 0) { 
    printf("i am the child process, my process id is %d/n",getpid());  
    printf("我是爹的儿子/n");//对某些人来说中文看着更直白。 
    count++; 
  } 
  else { 
    printf("i am the parent process, my process id is %d/n",getpid());  
    printf("我是孩子他爹/n"); 
    count++; 
  } 
  printf("统计结果是: %d/n",count); 
  return 0; 
} 

运行结果为:

i am the child process, my process id is 5574
我是爹的儿子
统计结果是: 1
i am the parent process, my process id is 5573
我是孩子他爹
统计结果是: 1

在fpid=fork()语句之前,只有一个进程在执行这段代码,但是在这条语句之后,就变成了两个进程。 这两个过程几乎相同。 下一条要执行的语句都是if(fpid。为什么两个进程的fpid不一样呢?这和fork函数的特性有关。fork调用的一个奇妙之处就是它只被调用一次,但它可以返回两次。它可能有三个不同的返回值。:

1)在父进程中,fork返回新创建的子进程的进程ID;

2)在子进程中,fork返回0;

3)如果发生错误,fork返回负值;

fork函数执行后,如果新进程创建成功,会出现两个进程,一个是子进程,一个是父进程。 在子进程中,fork函数返回0。在父进程中,fork返回新创建的子进程的进程ID。 我们可以通过fork返回的值来判断当前进程是子进程还是父进程。

引用一位网友的解释,为什么父子进程中fpid的值不同。 》其实就相当于一个链表,进程之间形成一个链表,父进程的fpid(p表示点)指向子进程的进程ID,因为子进程没有子进程,所以它的fpid 为 0。

出现 Fork 错误的原因有两个:

1)当前进程数已达到系统规定的上限,errno的值被设置为。

2)系统内存不足,errno的值设置为。

新进程创建成功后,系统中会出现两个基本相同的进程。 这两个进程的执行顺序没有固定的顺序。 哪个进程先执行取决于系统的进程调度策略。

每个进程都有一个唯一的(不同的)进程标识符(ID),可以通过()函数获得,还有一个记录父进程pid的变量,并且可以通过()函数获得变量的值。

fork执行完成后,出现两个进程,

有人说,两个过程的内容一模一样,但打印的结果不同。 这是因为判断条件的原因。 上面列出的只是进程的代码和指令,以及变量。

执行fork后,进程1的变量为count=0,fpid! =0(父进程)。 进程2的变量是count=0和fpid=0(子进程)。 这两个进程的变量是独立的,存在于不同的地址。 它们不被共享。 这点应该注意。 可以说,我们使用fpid来识别和操作父子进程。

可能有人还会奇怪,为什么不从#位置复制代码呢。 这是因为 fork 复制了进程的当前情况。 执行fork时,进程已经执行完int count=0; fork 只复制下一个要执行的代码。 到一个新的过程。

2.分叉高级知识

我们先看一下代码:

#include  
#include  
int main(void) 
{ 
  int i=0; 
  printf("i son/pa ppid pid fpid/n"); 
  //ppid指当前进程的父进程pid 
  //pid指当前进程的pid, 
  //fpid指fork返回给当前进程的值 
  for(i=0;i<2;i++){ 
    pid_t fpid=fork(); 
    if(fpid==0) 
      printf("%d child %4d %4d %4d/n",i,getppid(),getpid(),fpid); 
    else 
      printf("%d parent %4d %4d %4d/n",i,getppid(),getpid(),fpid); 
  } 
  return 0; 
} 

运行结果为:

  i son/pa ppid pid fpid
  0 parent 2043 3224 3225
  0 child 3224 3225  0
  1 parent 2043 3224 3226
  1 parent 3224 3225 3227
  1 child   1 3227  0
  1 child   1 3226  0 

这段代码相当有趣,我们仔细分析一下:

步骤1:在父进程中,执行指令进入for循环,i=0,然后执行fork。 fork执行后,系统中出现了两个进程,分别是p3224和p3225(后面我会用pxxxx来表示进程ID,就是xxxx的进程)。 可以看到父进程p3224的父进程是p2043,而子进程p3225的父进程恰好是p3224。 我们用链表来表示这种关系:

p2043->p3224->p3225 

第一次fork后,p3224(父进程)的变量为i=0,fpid=3225(fork函数返回父进程中的子进程id),代码内容为:

for(i=0;i<2;i++){ 
  pid_t fpid=fork();//执行完毕,i=0,fpid=0 
  if(fpid==0) 
    printf("%d child %4d %4d %4d/n",i,getppid(),getpid(),fpid); 
  else 
    printf("%d parent %4d %4d %4d/n",i,getppid(),getpid(),fpid); 
} 
return 0; 

所以打印出结果:

  0 parent 2043 3224 3225
  0 child 3224 3225  0

步骤2:假设先执行父进程p3224。 进入下一个循环时,i=1,则执行fork,系统中添加一个新进程p3226。 对于此时的父进程来说,p2043->p3224(当前进程)->p3226(创建的子进程)。

对于子进程p3225,执行第一个循环后,i=1,则执行fork,系统中添加一个新进程p3227。 对于此进程,p3224->p3225(当前进程)->p3227(创建的子进程)。 从输出中我们可以看到p3225原本是p3224的子进程,现在变成了p3227的父进程。 父子是相对的,这一点大家应该都很容易理解。 只要当前进程执行fork,该进程就成为父进程并打印出来。

所以打印的结果是:

  1 parent 2043 3224 3226
  1 parent 3224 3225 3227 

进程调度算法c++代码_进程调度代码实现_c语言实现进程调度

步骤3:在第二步中,创建了两个进程p3226和p3227。 这两个进程执行完函数后就结束了,因为这两个进程无法进入第三次循环,无法fork。 是时候执行0了;,其他进程也是如此。

以下是p32​​26和p3227打印的结果:

  1 child   1 3227  0
  1 child   1 3226  0 

细心的读者可能会注意到,p3226和p3227的父进程不应该是p3224和p3225。 怎么可能是1呢? 这里就不得不说一下进程创建和死亡的过程。 p3224和p3225执行第二个循环后,main函数应该退出,也就是说,进程应该死亡,因为它已经完成了所有事情。 p3224和p3225死亡后,p3226和p3227将没有父进程。 这在操作系统中是不允许的,因此将p3226和p3227的父进程设置为p1。 P1永远不会死。 至于为什么,这里就不介绍了,留给“3. 叉高级知识”。

总结一下,该程序的执行流程如下:

该程序最终产生了 3 个子进程并执行了 () 函数 6 次。

我们看另一段代码:

#include  
#include  
int main(void) 
{ 
  int i=0; 
  for(i=0;i<3;i++){ 
    pid_t fpid=fork(); 
    if(fpid==0) 
      printf("son/n"); 
    else 
      printf("father/n"); 
  } 
  return 0; 
 
} 

其执行结果为:

  father
  son
  father
  father
  father
  father
  son
  son
  father
  son
  son
  son
  father
  son 

这里不做详细解释,只是粗略分析一下。

  for    i=0     1      2
       father   father   father
                    son
              son    father
                    son
        son    father   father
                    son
              son    father
                    son

每一行代表一个进程的运行打印结果。

总结一下规则,对于这种N次循环的情况,函数执行次数为2*(1+2+4+...+2N)次,创建的子进程数量为1+ 2+4+...+2N 个人。

网上有人说N个循环生成2*(1+2+4+...+2N)个进程。 这种说法是错误的。 希望大家关注。

同时,如果想测试一个程序中创建了多少个子进程,最好的办法是调用一个函数来打印进程的pid,即调用("%d/n",( )); 或通过(“+ /n”); 以确定已生成多少个进程。 有些人想统计调用("+");创建的进程数,这是不合适的。 我来分析一下具体原因。

老规矩,大家看一下下面的代码:
#include  
#include  
int main() { 
  pid_t fpid;//fpid表示fork函数返回的值 
  //printf("fork!"); 
  printf("fork!/n"); 
  fpid = fork(); 
  if (fpid < 0) 
    printf("error in fork!"); 
  else if (fpid == 0) 
    printf("I am the child process, my process id is %d/n", getpid()); 
  else 
    printf("I am the parent process, my process id is %d/n", getpid()); 
  return 0; 
} 

执行结果如下:

fork!
I am the parent process, my process id is 3361
I am the child process, my process id is 3362 

如果注释掉语句("fork!/n");,则执行("fork!");

新程序的执行结果为:

fork!I am the parent process, my process id is 3298
fork!I am the child process, my process id is 3299 

程序之间的唯一区别是 /n 回车符。 为什么结果如此不同?

这与缓冲机制有关。 当有某些内容时,操作系统只是将内容放入缓冲队列中,并没有真正将其写入屏幕。 不过,只要看到/n,就会立即刷新,所以可以立即打印。

运行后(“叉子!”),“叉子!” 仅放置在缓冲区中。 当程序运行到fork时,“fork!” 缓冲区中的内容由子进程复制。 所以,还有fork! 在子级缓冲区中。 所以,你最终看到的是 fork! 2次! ! ! !

运行后(“fork!/n”),“fork!” 立即打印到屏幕上,并且不会出现分叉! 稍后分叉的子进程中缓冲区中的内容。 因此,你看到的结果将会是fork! 被封一次! ! ! !

所以(“+”); 无法正确反映进程数。

看了这么多你可能有点累了,但是我还是要贴出最后一段代码来进一步分析fork函数。

#include  
#include  
int main(int argc, char* argv[]) 
{ 
  fork(); 
  fork() && fork() || fork(); 
  fork(); 
  return 0; 
} 

问题是,不计算主进程本身,程序创建了多少个进程。

为了回答这个问题,我们先做一些错误的事情,使用程序来验证到目前为止有多少个进程。

答案是一共有20个进程,除去主进程,还有19个进程。

我们仔细看看为什么还有19个进程。

第一个fork和最后一个fork肯定会被执行。

主要是中间的三个叉子,可以画个图来描述一下。

这里需要注意&&和|| 运营商。

A&&B,如果A=0,则不需要继续执行&&B; 如果A不为0,则需要继续执行&&B。

A||B,如果A不为0,则无需继续执行||B。 如果A=0,则需要继续执行||B。

fork()的返回值对于父进程和子进程是不同的。 按照上面的分支A&&B和A||B画图,可以得到5个分支。

标签: fork c语言 linux系统

如本站内容信息有侵犯到您的权益请联系我们删除,谢谢!!


Copyright © 2020 All Rights Reserved 京ICP5741267-1号 统计代码