Process creation <fork function> principle (implemented by code)

1. Before talking about the principles of process creation, waiting, termination, etc. and code implementation, you first need to understand several states of the process
1. Linux进程状态:R run 运行状态 0 (TASK_RUNNING),可执行状态&运行状态(在run_queue队列里的状态)

2. Linux进程状态:S 浅度睡眠 1 系统默认sleep (TASK_INTERRUPTIBLE),可中断的睡眠状态, 可处理signal

3. Linux进程状态:D 深度睡眠 2 硬件交互I/O (TASK_UNINTERRUPTIBLE),不可中断的睡眠状态, 可处理signal,有延迟

4. Linux进程状态:T stop 状态 4 (TASK_STOPPED or TASK_TRACED),暂停状态或跟踪状态, 不可处理signal, 因为根本没有时间片运行代码

5. Linux进程状态:Z 僵死状态 16(TASK_DEAD - EXIT_ZOMBIE),退出状态,进程成为僵尸进程。不可被kill, 即不响应任务信号, 无法用SIGKILL杀死

6.  linux进程状态 : X 死亡状态: 32
2. Other process knowledge
   1.一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。

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

process creation

  • fork basics

    The fork function is very important. It creates a new process from an existing process. The new process created is a child process, and the original process is called the parent process.

  • fork function return value

    The child process returns 0;
    the parent process returns the PID of the child process.

for example:
/* 
 *  fork_test.c 
 *  Created on: 2017*12*31
 */  
#include <unistd.h>  
#include <stdio.h>   
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("Child/n");   //对某些人来说中文看着更直白。  
        count++;  
    }  
    else {  
        printf("i am the parent process, my process id is %d/n",getpid());   
        printf("Father/n");  
        count++;  
    }  
    printf("统计结果是: %d/n",count);  
    return 0;  
}
Results of the:
i am the child process, my process id is 5574
Child
统计结果是: 1
i am the parent process, my process id is 5573
Father
统计结果是: 1
analyze:
   在语句fpid=fork()之前,只有一个进程在执行这段代码,但在这条语句之后,就变成两个进程在执行了,这两个进程的几乎完全相同,将要执行的下一条语句都是if(fpid<0)……
One of the wonderful things about the fork call is that it is only called once, but can return twice, and it can have three different return values:
   1)在父进程中,fork返回新创建子进程的进程ID;
   2)在子进程中,fork返回0;
   3)如果出现错误,fork返回一个负值;

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

   引用一位网友的话来解释fpid的值为什么在父子进程中不同。“其实就相当于链表,进程形成了链表,父进程的fpid(p 意味point)指向子进程的进程id, 因为子进程没有子进程,所以其fpid为0.
There may be two reasons for fork errors:
   1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。
   2)系统内存不足,这时errno的值被设置为ENOMEM。

   创建新进程成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。
   每个进程都有一个独特(互不相同)的进程标识符(process ID),可以通过getpid()函数获得,还有一个记录父进程pid的变量,可以通过getppid()函数获得变量的值。

After the fork is executed, two processes appear:

Create process description

    1、有人说两个进程的内容完全一样啊,怎么打印的结果不一样啊,那是因为判断条件的原因,上面列举的只是进程的代码和指令,还有变量啊。
    2、执行完fork后,进程1的变量为count=0,fpid!=0(父进程)。进程2的变量为count=0,fpid=0(子进程),这两个进程的变量都是独立的,存在不同的地址中,不是共用的,这点要注意。可以说,我们就是通过fpid来识别和操作父子进程的。
    3、还有人可能疑惑为什么不是从#include处开始复制代码的,这是因为fork是把进程当前的情况拷贝一份,执行fork时,进程已经执行完了int count=0;fork只拷贝下一个要执行的代码到新的进程。

  • fork advanced
for example:
/* 
 *  fork_test.c 
 *  version 2 
 *  Created on: 2017*12*31
 */  
#include <unistd.h>  
#include <stdio.h>  
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;  
} 
Results of the:
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 
This code is more interesting, let's analyze it carefully:
    第一步:在父进程中,指令执行到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=3225  
     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;  
}

The variable of p3225 (child process) is i=0, fpid=0 (the fork function returns 0 in the child process), and the code content is:

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

    第二步:假设父进程p3224先执行,当进入下一个循环时,i=1,接着执行fork,系统中又新增一个进程p3226,对于此时的父进程,p2043->p3224(当前进程)->p3226(被创建的子进程)。
    对于子进程p3225,执行完第一次循环后,i=1,接着执行fork,系统中新增一个进程p3227,对于此进程,p3224->p3225(当前进程)->p3227(被创建的子进程)。从输出可以看到p3225原来是p3224的子进程,现在变成p3227的父进程。父子是相对的,这个大家应该容易理解。只要当前进程执行了fork,该进程就变成了父进程了,就打印出了parent。
     所以打印出结果是:
            1 parent 2043 3224 3226
            1 parent 3224 3225 3227 
    第三步:第二步创建了两个进程p3226,p3227,这两个进程执行完printf函数后就结束了,因为这两个进程无法进入第三次循环,无法fork,该执行return 0;了,其他进程也是如此。
    以下是p3226,p3227打印出的结果:
       1 child     1 3227    0
       1 child     1 3226    0
    细心的读者可能注意到p3226,p3227的父进程难道不该是p3224和p3225吗,怎么会是1呢?这里得讲到进程的创建和死亡的过程。
To summarize, the flow of execution of this program is as follows:

write picture description here

 这个程序最终产生了3个子进程,执行过6次printf()函数。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325848827&siteId=291194637